详解SpringBoot中JdbcTemplate的事务控制
发布日期:2025-01-03 18:09 点击次数:116
前言
JdbcTemplate是spring-jdbc提供的数据库核心操作类,那对JdbcTemplate进行事务控制呢?
我的环境:spring-boot-2.1.3,druid-1.1.3。
原生Jdbc的事务控制
即,批处理+自动提交的控制方式,
Spring的声明式事务控制
Bean的类或方法上加@Transactional,事务控制粒度较大,只能控制在方法级别,不能控制到代码粒度级别。
尝试JdbcTemplate的事务控制
采取跟原生jdbc事务控制一样的方法试试,在批处理前关闭自动提交,若批处理失败则回滚的思路。
期望结果:id=1的因为主键冲突,所以id=22的也要回滚。
实际结果:id=1的插入失败,id=22的插入成功,未回滚。
原因分析:自始至终都是同一个connection连接对象,按道理不应该无法控制自动提交,唯一的解释是jdbcTemplate.batchUpdate()中真正使用的连接对象并非代码中的conn,于是一方面把conn打印出来,另一方面准备调试jdbcTemplate.batchUpdate()源码内部,看看是否使用了另外获取到的connection。
调试流程:jdbcTemplate.batchUpdate()
→execute(new BatchUpdateStatementCallback())
→DataSourceUtils.getConnection(obtainDataSource())
对比两个connection,确非同一对象,因此对我们的conn进行事务的控制不会影响jdbcTemplate内部真正使用的con,
→紧接着进入源码376行,回调函数action.doInStatement(stmt)
在回调函数中,真正进行数据库操作。至此,便明白了这样的方法为何不能成功控制jdbcTemplate事务的原因,即我们控制的conn和jdbcTemplate真正使用的con不是同一个对象。那如果Druid数据库连接池里只有1个conn呢,这样的方法会不会成功控制?
于是修改druid配置,将initial-size、max-active、min-idle都设置为1,这样,你jdbcTemplate里获取到的跟我的conn总该是同一对象了吧?然而,方法运行约1min后,抛出异常:
Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60001, active 1, maxActive 1, creating 0
继续跟了一下源码,原来是池子里最大只有一个连接conn,而它又未被释放,导致jdbcTemplate内部再去从池子里获取con时,一直在等待已有连接conn的释放,一直等不到释放,所以等待了max-wait=60000ms的时间,最后报错。
所以这样的控制也是不合理的,那究竟如何控制JdbcTemplate的事务呢?答案就是TransactionTemplate。
TransactionTemplate的编程式事务控制
注册事务相关bean:TransactionTemplate,如下:
然后注入TransactionTemplate,使用transactionTemplate.execute(new TransactionCallback<> action)或者transactionTemplate.execute(new TransactionCallbackWithoutResult<> action)执行多条sql,最后可以通过transactionStatus的setRollbackOnly()或rollbackToSavepoint(savepoint) 控制事务,如下:
上面是不带参数的多条sql的事务执行,若是带参数的多条sql,可以实现如下:
到此这篇关于SpringBoot中JdbcTemplate的事务控制的文章就介绍到这了,更多相关SpringBoot JdbcTemplate事务控制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
|
----------------------------------