此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.2.0spring-doc.cadn.net.cn

JDBC 批量作

如果对同一 JDBC 驱动程序进行批处理多个调用,则大多数 JDBC 驱动程序都会提供改进的性能 prepared 语句。通过将更新分组为批次,您可以限制往返次数 添加到数据库。spring-doc.cadn.net.cn

基本批量作JdbcTemplate

您完成JdbcTemplate通过实现特殊接口的两种方法进行批处理,BatchPreparedStatementSetter,并将该实现作为第二个参数传入 在batchUpdate方法调用。您可以使用getBatchSize方法提供 当前批次。您可以使用setValues方法设置 准备好的语句。此方法调用您在getBatchSize叫。以下示例更新t_actor基于列表中的条目的表, ,整个列表用作批处理:spring-doc.cadn.net.cn

public class JdbcActorDao implements ActorDao {

	private JdbcTemplate jdbcTemplate;

	public void setDataSource(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	}

	public int[] batchUpdate(final List<Actor> actors) {
		return this.jdbcTemplate.batchUpdate(
				"update t_actor set first_name = ?, last_name = ? where id = ?",
				new BatchPreparedStatementSetter() {
					public void setValues(PreparedStatement ps, int i) throws SQLException {
						Actor actor = actors.get(i);
						ps.setString(1, actor.getFirstName());
						ps.setString(2, actor.getLastName());
						ps.setLong(3, actor.getId().longValue());
					}
					public int getBatchSize() {
						return actors.size();
					}
				});
	}

	// ... additional methods
}
class JdbcActorDao(dataSource: DataSource) : ActorDao {

	private val jdbcTemplate = JdbcTemplate(dataSource)

	fun batchUpdate(actors: List<Actor>): IntArray {
		return jdbcTemplate.batchUpdate(
				"update t_actor set first_name = ?, last_name = ? where id = ?",
				object: BatchPreparedStatementSetter {
					override fun setValues(ps: PreparedStatement, i: Int) {
						ps.setString(1, actors[i].firstName)
						ps.setString(2, actors[i].lastName)
						ps.setLong(3, actors[i].id)
					}

					override fun getBatchSize() = actors.size
				})
	}

	// ... additional methods
}

如果您处理更新流或从文件中读取数据,则可能会有一个 首选批处理大小,但最后一个批处理可能没有该数量的条目。在这个 case 中,您可以使用InterruptibleBatchPreparedStatementSetter接口,它允许 一旦 input 源耗尽,您就会中断一个批处理。这isBatchExhausted方法 用于向批处理结束发出信号。spring-doc.cadn.net.cn

使用对象列表进行批量作

JdbcTemplateNamedParameterJdbcTemplate提供了另一种方式 提供批处理更新。您无需实现特殊的批处理接口,而是 以列表形式提供调用中的所有参数值。框架在这些 值并使用内部准备好的语句 setter 进行设置。API 因 是否使用命名参数。对于命名参数,您需要提供一个SqlParameterSource,则批处理的每个成员都有一个条目。您可以使用SqlParameterSourceUtils.createBatch创建此数组的便捷方法,将 在 Bean 样式对象数组中(其 getter 方法对应于参数),String-键 控Map实例(包含相应的参数作为值)或两者的混合。spring-doc.cadn.net.cn

以下示例显示了使用命名参数的批量更新:spring-doc.cadn.net.cn

public class JdbcActorDao implements ActorDao {

	private NamedParameterTemplate namedParameterJdbcTemplate;

	public void setDataSource(DataSource dataSource) {
		this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
	}

	public int[] batchUpdate(List<Actor> actors) {
		return this.namedParameterJdbcTemplate.batchUpdate(
				"update t_actor set first_name = :firstName, last_name = :lastName where id = :id",
				SqlParameterSourceUtils.createBatch(actors));
	}

	// ... additional methods
}
class JdbcActorDao(dataSource: DataSource) : ActorDao {

	private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)

	fun batchUpdate(actors: List<Actor>): IntArray {
		return this.namedParameterJdbcTemplate.batchUpdate(
				"update t_actor set first_name = :firstName, last_name = :lastName where id = :id",
				SqlParameterSourceUtils.createBatch(actors));
	}

		// ... additional methods
}

对于使用经典?placeholders 中,您可以传入一个列表 包含具有 Update 值的对象数组。此对象数组必须有一个条目 ,并且它们的顺序必须与它们的顺序相同 在 SQL 语句中定义。spring-doc.cadn.net.cn

以下示例与前面的示例相同,只是它使用了 classic JDBC?placeholders 的 Headers 中:spring-doc.cadn.net.cn

public class JdbcActorDao implements ActorDao {

	private JdbcTemplate jdbcTemplate;

	public void setDataSource(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	}

	public int[] batchUpdate(final List<Actor> actors) {
		List<Object[]> batch = new ArrayList<>();
		for (Actor actor : actors) {
			Object[] values = new Object[] {
					actor.getFirstName(), actor.getLastName(), actor.getId()};
			batch.add(values);
		}
		return this.jdbcTemplate.batchUpdate(
				"update t_actor set first_name = ?, last_name = ? where id = ?",
				batch);
	}

	// ... additional methods
}
class JdbcActorDao(dataSource: DataSource) : ActorDao {

	private val jdbcTemplate = JdbcTemplate(dataSource)

	fun batchUpdate(actors: List<Actor>): IntArray {
		val batch = mutableListOf<Array<Any>>()
		for (actor in actors) {
			batch.add(arrayOf(actor.firstName, actor.lastName, actor.id))
		}
		return jdbcTemplate.batchUpdate(
				"update t_actor set first_name = ?, last_name = ? where id = ?", batch)
	}

	// ... additional methods
}

我们之前描述的所有批处理更新方法都返回一个int数组 包含每个批处理条目受影响的行数。此计数由 JDBC 驱动程序。如果计数不可用,JDBC 驱动程序将返回-2.spring-doc.cadn.net.cn

在这种情况下,通过自动设置底层PreparedStatement, 每个值的相应 JDBC 类型需要从给定的 Java 类型派生。 虽然这通常运行良好,但可能会出现问题(例如,使用 包含地图null值)。默认情况下,Spring 调用ParameterMetaData.getParameterType在这种情况下,JDBC 驱动程序可能会很昂贵。您应该使用最近的驱动程序 版中,并考虑设置spring.jdbc.getParameterType.ignoreproperty 设置为true(作为 JVM 系统属性或通过SpringProperties机制) 如果您的应用程序遇到特定的性能问题。spring-doc.cadn.net.cn

从 6.1.2 开始,Spring 绕过了默认的getParameterTypePostgreSQL 上的 resolution 和 MS SQL 服务器。这是一种常见的优化,可避免进一步往返于 DBMS 对于已知对 特别是 PostgreSQL 和 MS SQL Server,特别是用于批处理作。如果你 碰巧看到副作用,例如,将 Byte 数组设置为没有特定类型的 null 时 指示,您可以显式设置spring.jdbc.getParameterType.ignore=false旗 作为系统属性(见上文)以恢复完整getParameterType分辨率。spring-doc.cadn.net.cn

或者,您可以考虑显式指定相应的 JDBC 类型。 要么通过BatchPreparedStatementSetter(如前所示),通过显式的 type 数组分配给List<Object[]>based call, throughregisterSqlType调用 习惯MapSqlParameterSource实例,通过BeanPropertySqlParameterSource从 Java 声明的属性类型派生 SQL 类型,即使对于 null 值也是如此,或者 通过提供个人SqlParameterValue实例,而不是普通的 null 值。spring-doc.cadn.net.cn

具有多个批次的批量作

前面的批处理更新示例处理的批处理非常大,以至于您希望 将它们分成几个较小的批次。您可以使用以下方法执行此作 前面提到的,通过多次调用batchUpdate方法,但现在有一个 更方便的方法。除了 SQL 语句之外,此方法还需要一个Collection的对象中,要为每个对象进行的更新 batch 和ParameterizedPreparedStatementSetter设置参数的值 的 Script。框架会遍历提供的值并断开 将调用更新为指定大小的批处理。spring-doc.cadn.net.cn

以下示例显示了使用批处理大小 100 的批处理更新:spring-doc.cadn.net.cn

public class JdbcActorDao implements ActorDao {

	private JdbcTemplate jdbcTemplate;

	public void setDataSource(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	}

	public int[][] batchUpdate(final Collection<Actor> actors) {
		int[][] updateCounts = jdbcTemplate.batchUpdate(
				"update t_actor set first_name = ?, last_name = ? where id = ?",
				actors,
				100,
				(PreparedStatement ps, Actor actor) -> {
					ps.setString(1, actor.getFirstName());
					ps.setString(2, actor.getLastName());
					ps.setLong(3, actor.getId().longValue());
				});
		return updateCounts;
	}

	// ... additional methods
}
class JdbcActorDao(dataSource: DataSource) : ActorDao {

	private val jdbcTemplate = JdbcTemplate(dataSource)

	fun batchUpdate(actors: List<Actor>): Array<IntArray> {
		return jdbcTemplate.batchUpdate(
					"update t_actor set first_name = ?, last_name = ? where id = ?",
					actors, 100) { ps, argument ->
			ps.setString(1, argument.firstName)
			ps.setString(2, argument.lastName)
			ps.setLong(3, argument.id)
		}
	}

	// ... additional methods
}

此调用的 batch update 方法返回一个int数组中包含 数组条目,其中包含每个更新受影响的行数的数组。 顶级数组的长度表示运行的批处理数,第二级 数组的长度表示该批次中的更新数量。中的更新数量 每个批次应为为所有批次提供的批次大小(最后一个批次除外) 这可能更少),具体取决于提供的 Update 对象总数。更新 count 是 JDBC 驱动程序报告的 count 语句。如果计数为 not available,则 JDBC 驱动程序返回值-2.spring-doc.cadn.net.cn