`

Spring源码学习-JdbcTemplate batchUpdate批量操作

阅读更多

Spring JdbcTemplate的batch操作最后还是利用了JDBC提供的方法,Spring只是做了一下改造和封装

JDBC的batch操作:


String sql = "INSERT INTO CUSTOMER " +
				  "(CUST_ID, NAME, AGE) VALUES (?, ?, ?)";
				  
List<Customer> customers = getCustomersToInsert();

PreparedStatement pstmt = conn.prepareStatement(sql);

//默认情况下auto-commit=true,会认为一个statement就是一个transaction。批量操作中要执行多个statement,因此要设置为false
conn.setAutoCommit(false);	

for (Customer customer : customers) {
	pstmt.setLong(1, customer.getCustId());
	pstmt.setString(2, customer.getName());
	pstmt.setInt(3, customer.getAge() );
	pstmt.addBatch();
}

int[] count = stmt.executeBatch();

conn.commit();



分析上述代码可知,实际应用中只有两部分是会变的:一是sql语句,二是要插入的数据
Spring做的工作就是把“变”与“不变”的部分抽离开来
sql语句就作为一个String类型的参数传递好了,而插入数据的写入提取为BatchPreparedStatementSetter接口:

class MyBatchPreparedStatementSetter implements BatchPreparedStatementSetter{
    
    private List<Customer> customers;
    
    public MyBatchPreparedStatementSetter(List<Customer> customers) {
        this.customers = customers;
    }

    @Override
    public void setValues(PreparedStatement ps, int i) throws SQLException {
        Customer customer = customers.get(i);
        ps.setLong(1, customer.getCustId());
        ps.setString(2, customer.getName());
        ps.setInt(3, customer.getAge() );
    }
 
    @Override
    public int getBatchSize() {
        return customers.size();
    }
    
}

BatchPreparedStatementSetter通常是以匿名内部类的形式出现(模板模式):
  String sql = ...;
  List<Customer> customers = ...;
  
  getJdbcTemplate().batchUpdate(sql, new BatchPreparedStatementSetter() {
 
	@Override
	public void setValues(PreparedStatement ps, int i) throws SQLException {
		Customer customer = customers.get(i);
		ps.setLong(1, customer.getCustId());
		ps.setString(2, customer.getName());
		ps.setInt(3, customer.getAge() );
	}
 
	@Override
	public int getBatchSize() {
		return customers.size();
	}
  });


接下来就是“不变”的部分了,开启PreparedStatement并执行batch操作:

JdbcTemplate的batchUpdate方法:
public int[] batchUpdate(String sql, final BatchPreparedStatementSetter pss) throws DataAccessException {

		return execute(sql, new PreparedStatementCallback<int[]>() {
			public int[] doInPreparedStatement(PreparedStatement ps) throws SQLException {
				try {
					int batchSize = pss.getBatchSize();
					InterruptibleBatchPreparedStatementSetter ipss =
							(pss instanceof InterruptibleBatchPreparedStatementSetter ?
							(InterruptibleBatchPreparedStatementSetter) pss : null);
					if (JdbcUtils.supportsBatchUpdates(ps.getConnection())) {
						for (int i = 0; i < batchSize; i++) {
							pss.setValues(ps, i);
							if (ipss != null && ipss.isBatchExhausted(i)) {
								break;
							}
							ps.addBatch();
						}
						return ps.executeBatch();
					}
					else {
						List<Integer> rowsAffected = new ArrayList<Integer>();
						for (int i = 0; i < batchSize; i++) {
							pss.setValues(ps, i);
							if (ipss != null && ipss.isBatchExhausted(i)) {
								break;
							}
							rowsAffected.add(ps.executeUpdate());
						}
						int[] rowsAffectedArray = new int[rowsAffected.size()];
						for (int i = 0; i < rowsAffectedArray.length; i++) {
							rowsAffectedArray[i] = rowsAffected.get(i);
						}
						return rowsAffectedArray;
					}
				}
				finally {
					if (pss instanceof ParameterDisposer) {
						((ParameterDisposer) pss).cleanupParameters();
					}
				}
			}
		});
	}

可以看到pss.setValues(ps, i)、ps.addBatch() ps.executeBatch()等操作,是跟JDBC的一样
而且它还判断了如果不支持批量操作,则一条一条地执行

重点在PreparedStatementCallback:也是以匿名内部类的形式提供,它定义的doInPreparedStatement在execute方法中回调:
	public <T> T execute(String sql, PreparedStatementCallback<T> action) throws DataAccessException {
		return execute(new SimplePreparedStatementCreator(sql), action);
	}

这一步,sql作为参数,利用PreparedStatementCreator来创建PreparedStatement
SimplePreparedStatementCreator的createPreparedStatement方法:
		public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
			return con.prepareStatement(this.sql);
		}


public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action)
			throws DataAccessException {

		Connection con = DataSourceUtils.getConnection(getDataSource());
		PreparedStatement ps = null;
		try {
			Connection conToUse = con;
			if (this.nativeJdbcExtractor != null &&
					this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()) {
				conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
			}
			ps = psc.createPreparedStatement(conToUse);
			applyStatementSettings(ps);
			PreparedStatement psToUse = ps;
			if (this.nativeJdbcExtractor != null) {
				psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps);
			}
			T result = action.doInPreparedStatement(psToUse);
			handleWarnings(ps);
			return result;
		}
		
		//omitted
	}

这个execute方法主要就是创建PreparedStatement并回调PreparedStatementCallback的doInPreparedStatement方法,简单理解为:
		Connection con = DataSourceUtils.getConnection(getDataSource());
		PreparedStatement ps = null;
		try {
			ps = psc.createPreparedStatement(con);
			T result = action.doInPreparedStatement(ps);
			return result;
		}


其中为什么要用到nativeJdbcExtractor,官方文档是这样:
Sometimes you need to access vendor specific JDBC methods that differ from the standard JDBC API. This can be problematic if you are running in an application server or with a DataSource that wraps the Connection, Statement and ResultSet objects with its own wrapper objects. To gain access to the native objects you can configure yourJdbcTemplate or OracleLobHandler with a NativeJdbcExtractor.
因此,主要是为了取得原始的、标准的Connection, Statement and ResultSet(而不是经过包装之后的)

最后梳理一下思路,以sql语句和待插入数据(customers)这两个变量为线索:
首先,sql语句,最后会通过它创建一个PreparedStatement
其次,把数据写入的设置提取为一个接口,使用时创建匿名内部类,也就是说数据由BatchPreparedStatementSetter持有
再者,PreparedStatementCallback持有BatchPreparedStatementSetter(也就持有了数据),那它还需要有一个PreparedStatement
来执行batch操作。那这个PreparedStatement怎么提供呢?在execute方法里面回调时提供

还有一个问题,为什么在Spring JdbcTemplate的batchUpdate中,没有看到conn.setAutoCommit(false)的操作?
这是因为Spring有它自己的事务管理机制
如果你配置了JDBC的事务管理,那么DataSourceTransactionManager会自动设置
DataSourceTransactionManagerr的doBegin方法:
			if (con.getAutoCommit()) {
				txObject.setMustRestoreAutoCommit(true);
				if (logger.isDebugEnabled()) {
					logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
				}
				con.setAutoCommit(false);
			}


1
1
分享到:
评论

相关推荐

    spring-springMvc-jdbctemplate.rar

    在本项目中,"spring-springMvc-jdbctemplate.rar" 是一个包含了使用Spring框架、Spring MVC和JdbcTemplate实现的Web应用示例。这个压缩包可能包含了一系列的配置文件、源代码和数据库脚本,旨在展示如何整合这些...

    Spring JdbcTemplate.batchUpdate 例子

    总之,`Spring JdbcTemplate.batchUpdate`是处理批量数据库操作的强大工具,它可以提高效率,减少与数据库的交互次数。然而,正确地使用它需要对事务管理、异常处理和性能优化有深入的理解。通过合理的配置和良好的...

    spring-jdbcTemplate实例工程

    《深入解析Spring JdbcTemplate》 Spring JDBC Template是Spring框架中...这个实例工程为我们提供了一个学习和实践Spring JdbcTemplate的良好平台,通过对其中代码的分析和运行,可以更好地理解和掌握其用法和精髓。

    Spring--JdbcTemplate.pdf

    对于批量操作,JdbcTemplate提供了以下支持: 1. 执行批量添加操作:通过遍历List集合,将每个数组作为参数执行SQL语句进行添加。 2. 执行批量删除和修改操作:也是遍历List集合,对每个元素执行相应的SQL语句。 3. ...

    Spring-JdbcTemplate

    它提供了一系列高级的数据库操作模板方法,封装了JDBC API的常见模式,如查询、更新和批量操作,从而避免了常见的资源管理、异常处理和SQL语句执行的重复性编码工作,提高了代码的可读性和可维护性。 #### 二、...

    spring源码spring-framework-4.3.2.RELEASE

    2. **Batch Processing**:Spring Batch提供了批量处理框架,适用于大数据量的批处理任务。 3. **Message Broker**:Spring对消息中间件如RabbitMQ、Kafka等提供了集成,方便构建消息驱动的应用。 通过对Spring ...

    一个简单的spring-jdbctemplate扩展

    5. **批量操作**:优化了批处理功能,使得一次性执行多条SQL语句更加高效。 总的来说,这个“一个简单的spring-jdbctemplate扩展”项目旨在通过提供更高级别的抽象和便捷的API,使得开发人员在使用Spring ...

    Spring4--3.jdbcTemplate事务

    文件`spring_3_1`、`spring_3`和`spring_3_2`可能包含了Spring框架关于JdbcTemplate和事务管理的更多示例代码和配置信息,学习这些资源有助于深入理解Spring4中如何有效地利用JdbcTemplate进行事务控制。通过实践,...

    spring-jdbc-4.2.4.RELEASE.jar,spring-tx-4.2.4.RELEASE.jar,jdbcTemplate使用的jar包

    4. 编写数据库操作代码:利用`JdbcTemplate`提供的方法执行SQL语句,如`update()`, `query()`, `execute()`等。 总的来说,`spring-jdbc-4.2.4.RELEASE.jar`和`spring-tx-4.2.4.RELEASE.jar`这两个jar包是Spring...

    spring-framework-1.0-m2.zip

    Spring的JdbcTemplate提供了一种模板方法模式,避免了繁琐的JDBC代码,降低了错误率,提高了代码的可读性和可维护性。同时,它支持SQL语句的动态构造,使得数据访问更加灵活。 在MVC方面,Spring 1.0-m2提供了初步...

    在Java的Servlet中使用Spring JdbcTemplate-Eclipse版

    本资源是一个完整的通过Servlet-Service-Dao-JdbcTemplate访问MySQL数据库的JavaWeb Project,可以直接导入到Eclipse中进行调试运行,注意默认编译器是JDK1.8。

    springMVC-jdbcTemplate jar包

    而`jdbcTemplate`是Spring框架的一个核心组件,专为简化数据库操作而设计。在SpringMVC中集成`jdbcTemplate`,可以让我们在处理数据库交互时避免直接编写JDBC代码,减少出错的可能性,并提高代码的可读性和可维护性...

    Spring-With-JdbcTemplate

    Spring-With-JdbcTemplate 使用JdbcTemplate完成CRUD操作的完整代码弹簧需要执行哪些操作从GitHub上导入Eclipse进行Maven安装(Git Pull)#Create DB 删除数据库manishjdbc; 创建数据库manishjdbc; 使用...

    基于Spring-jdbc-update、query方法操作数据库

    本篇文章将深入探讨如何使用Spring的JDBC模块,特别是`update`和`query`方法来执行数据库的更新与查询操作。我们将围绕以下几个关键知识点展开: 1. **Spring JDBC模块**:Spring JDBC是Spring框架的一部分,它简化...

    官方原版源码spring-framework-5.2.1.RELEASE.zip

    3. **JDBC增强**:Spring的JdbcTemplate和JpaTemplate在5.2.1版本中得到了进一步优化,提供了更好的SQL执行性能和更丰富的查询API,简化了数据库操作。 4. **Java 11兼容性**:Spring 5.2.1全面支持Java 11,开发者...

    spring-framework-5.2.7.RELEASE.zip

    5. **JDBC增强**:Spring JDBC模块在5.0版本中进行了优化,提供了新的JdbcTemplate和NamedParameterJdbcTemplate方法,使数据库操作更简单、更安全。 6. **Spring Data升级**:Spring Data项目与Spring Framework 5...

    spring-jdbc-4.2.xsd.zip

    `spring-jdbc-4.2.xsd`是Spring 4.2版本的JDBC配置XML Schema定义,它定义了一系列元素和属性,用来描述如何配置Spring的JdbcTemplate、NamedParameterJdbcTemplate、SimpleJdbcInsert等核心组件,以及事务管理相关...

    SSH笔记-Spring JdbcTemplate

    6. **批量操作**:JdbcTemplate还支持批量更新,使用`batchUpdate()`方法可以一次执行多条SQL更新语句,提高了性能。 7. **预编译的SQL(PreparedStatement)**:JdbcTemplate默认使用PreparedStatement,这是一个...

    spring-framework-3.1.2.RELEASE jar包

    例如,它增强了JdbcTemplate,使其更加易用,同时增加了对JPA和Hibernate等ORM框架的支持,使得数据库操作更加简便。 在AOP(面向切面编程)方面,Spring 3.1引入了注解驱动的切面支持,使得开发者可以更容易地定义...

Global site tag (gtag.js) - Google Analytics