一文解析spring中事务的传播机制
本文讲解"一文解析spring中事务的传播机制",希望能够解决相关问题。
spring中的事务
spring的事务其实就是数据库的事务操作,符合acid标准,也具有标准的事务隔离级别。
spring中的事务只是对jdbc事务进行一些封装与扩展,其底层最终还是会使用到jdbc的这套api。但是spring事务有自己的特点,也就是事务传播机制。
所谓事务传播机制,也就是在事务在多个方法的调用中是如何传递的,是重新创建事务还是使用父方法的事务?父方法的回滚对子方法的事务是否有影响?这些都是可以通过事务传播机制来决定的。
准备工作
实体类area
package com.morris.spring.entity;import lombok.data;import java.io.serializable;@datapublic class area implements serializable {private integer id;private string areaname;private integer areacode;}
good
package com.morris.spring.entity;import lombok.data;import java.io.serializable;import java.math.bigdecimal;@datapublic class good implements serializable {private integer id;private string goodname;private bigdecimal price;}
dao层areadao
package com.morris.spring.dao;import com.morris.spring.entity.area;import org.springframework.beans.factory.annotation.autowired;import org.springframework.jdbc.core.jdbctemplate;public class areadao {@autowiredprivate jdbctemplate jdbctemplate;public boolean insert(area area) {string sql = "insert into t_area(area_name, area_code) values(?,?)";return jdbctemplate.update(sql, area.getareaname(), area.getareacode()) > 0;}}
gooddao
package com.morris.spring.dao;import com.morris.spring.entity.good;import org.springframework.beans.factory.annotation.autowired;import org.springframework.jdbc.core.jdbctemplate;public class gooddao {@autowiredprivate jdbctemplate jdbctemplate;public boolean insert(good good) {string sql = "insert into t_good(good_name, price) values(?,?)";return jdbctemplate.update(sql, good.getgoodname(), good.getprice()) > 0;}}
service层areaserviceimpl
package com.morris.spring.service;import com.morris.spring.dao.areadao;import com.morris.spring.entity.area;import org.springframework.beans.factory.annotation.autowired;import org.springframework.transaction.annotation.propagation;import org.springframework.transaction.annotation.transactional;public class areaserviceimpl implements areaservice {@autowiredprivate areadao areadao;@transactional(propagation = propagation.required)@overridepublic boolean addarea(int i) {int y = 1000000 / i;area area = new area();area.setareacode(y);area.setareaname("shenzhen");return areadao.insert(area);}}
goodserviceimpl
package com.morris.spring.service;import com.morris.spring.dao.gooddao;import com.morris.spring.entity.good;import org.springframework.beans.factory.annotation.autowired;import org.springframework.transaction.annotation.propagation;import org.springframework.transaction.annotation.transactional;import java.math.bigdecimal;public class goodserviceimpl implements goodservice {@autowiredprivate gooddao gooddao;@transactional(propagation = propagation.required)@overridepublic boolean addgood() {good good = new good();good.setgoodname("iphone");good.setprice(bigdecimal.valueof(99999));return gooddao.insert(good);}}
transactionservice
package com.morris.spring.service;import org.springframework.beans.factory.annotation.autowired;import org.springframework.stereotype.component;import org.springframework.transaction.annotation.propagation;import org.springframework.transaction.annotation.transactional;@componentpublic class transactionservice {@autowiredprivate goodservice goodservice;@autowiredprivate areaservice areaservice;@transactional(propagation = propagation.required)public void addgoodandarea() {system.out.println("------addgoodandarea-------");areaservice.addarea(10);goodservice.addgood();}}测试类
transactionpropagationdemo
package com.morris.spring.demo.jdbc;import com.morris.spring.config.jdbcconfig;import com.morris.spring.dao.areadao;import com.morris.spring.dao.gooddao;import com.morris.spring.service.areaserviceimpl;import com.morris.spring.service.goodserviceimpl;import com.morris.spring.service.transactionservice;import org.junit.jupiter.api.test;import org.springframework.context.annotation.annotationconfigapplicationcontext;/** * 事务的传播机制 */public class transactionpropagationdemo {@testpublic void test() {annotationconfigapplicationcontext applicationcontext = new annotationconfigapplicationcontext();applicationcontext.register(gooddao.class);applicationcontext.register(areadao.class);applicationcontext.register(goodserviceimpl.class);applicationcontext.register(areaserviceimpl.class);applicationcontext.register(jdbcconfig.class);applicationcontext.register(transactionservice.class);applicationcontext.refresh();transactionservice transactionservice = applicationcontext.getbean(transactionservice.class);transactionservice.addgoodandarea();}}
传播机制
具体选项可以参考枚举类org.springframework.transaction.annotation.propagation。
选项 | 说明 |
required | 默认选项。如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。 |
supports | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
mandatory | 使用当前的事务,如果当前没有事务,就抛出异常。 |
requires_new | 新建事务,如果当前存在事务,把当前事务挂起。 |
not_supported | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
never | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
nested | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与required类似的操作。 |
默认选项。如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。
配置如下:
- transactionservice:required
- areaserviceimpl:required
- goodserviceimpl:required
运行上面的demo,运行结果如下:
// 创建第一个事务 debug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.transactionservice.addgoodandarea]: propagation_required,isolation_default // 创建第一个连接 debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@8ef162] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] to manual commit ------addgoodandarea------- // 在第一个事务中执行 debug datasourcetransactionmanager:487 - participating in existing transaction debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)] // 在第一个事务中执行 debug datasourcetransactionmanager:487 - participating in existing transaction debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_area(area_name, area_code) values(?,?)] // 提交第一个事务 debug datasourcetransactionmanager:763 - initiating transaction commit debug datasourcetransactionmanager:329 - committing jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@8ef162] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] after transaction
总结:
- 当外层没有事务的时候,transactionservice.addgoodandarea()方法执行发现没有事务可用,自己新建事务。
- goodservice.addgood()和areaservice.addarea()执行时发现已有事务,就使用当前事务执行。
新建事务,如果当前存在事务,把当前事务挂起。
配置如下:
- transactionservice:required
- goodserviceimpl:requires
- areaserviceimpl:requires_new
运行结果如下:
// 创建第一个事务 debug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.transactionservice.addgoodandarea]: propagation_required,isolation_default // 创建第一个连接 debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@f9aa66] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@f9aa66] to manual commit ------addgoodandarea------- // 挂起第一个事务并创建第二个事务 debug datasourcetransactionmanager:446 - suspending current transaction, creating new transaction with name [com.morris.spring.service.areaserviceimpl.addarea] // 创建第二个连接 debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@116fc68] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@116fc68] to manual commit debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_area(area_name, area_code) values(?,?)] // 提交第二个事务 debug datasourcetransactionmanager:763 - initiating transaction commit debug datasourcetransactionmanager:329 - committing jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@116fc68] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@116fc68] after transaction // 恢复第一个事务 debug datasourcetransactionmanager:1043 - resuming suspended transaction after completion of inner transaction // 在第一个事务中执行 debug datasourcetransactionmanager:487 - participating in existing transaction debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)] debug datasourcetransactionmanager:763 - initiating transaction commit debug datasourcetransactionmanager:329 - committing jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@f9aa66] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@f9aa66] after transaction
总结:areaserviceimpl.addarea()执行时发现已有事务,就把当前事务挂起,执行完后再恢复。
supports支持当前事务,如果当前没有事务,就以非事务方式执行。
配置如下:
- transactionservice:supports
- areaserviceimpl:required
- goodserviceimpl:supports
运行结果如下:
// transactionservice.addgoodandarea以非事务方式运行 ------addgoodandarea------- // 开启第一个事务 debug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.areaserviceimpl.addarea]: propagation_required,isolation_default // 创建第一个连接 debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@1691f3d] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@1691f3d] to manual commit debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_area(area_name, area_code) values(?,?)] // 提交第一个事务 debug datasourcetransactionmanager:763 - initiating transaction commit debug datasourcetransactionmanager:329 - committing jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@1691f3d] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@1691f3d] after transaction debug datasourcetransactionmanager:1043 - resuming suspended transaction after completion of inner transaction // areaserviceimpl.addarea以非事务方式运行 debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)]
总结:当前没有事务,transactionservice.addgoodandarea()和areaserviceimpl.addarea()以非事务方式运行。
修改配置如下:
- transactionservice:required
- areaserviceimpl:supports
- goodserviceimpl:supports
运行结果如下:
// 开启第一个事务 debug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.transactionservice.addgoodandarea]: propagation_required,isolation_default // 创建第一个连接 debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@11158fb] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@11158fb] to manual commit ------addgoodandarea------- // 在第一个事务中执行 debug datasourcetransactionmanager:487 - participating in existing transaction debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_area(area_name, area_code) values(?,?)] // 在第一个事务中执行 debug datasourcetransactionmanager:487 - participating in existing transaction debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)] // 提交第一个事务 debug datasourcetransactionmanager:763 - initiating transaction commit debug datasourcetransactionmanager:329 - committing jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@11158fb] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@11158fb] after transaction
总结:当前有事务,goodserviceimpl.addgood()和areaserviceimpl.addarea()以事务方式运行。
mandatory使用当前的事务,如果当前没有事务,就抛出异常。
配置如下:
- transactionservice:required
- areaserviceimpl:required
- goodserviceimpl:mandatory
运行结果如下:
// 创建第一个事务 ebug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.transactionservice.addgoodandarea]: propagation_required,isolation_default // 创建第一个连接 debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@11c4a3f] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@11c4a3f] to manual commit ------addgoodandarea------- // 在第一个事务中执行 debug datasourcetransactionmanager:487 - participating in existing transaction debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_area(area_name, area_code) values(?,?)] // 在第一个事务中执行 debug datasourcetransactionmanager:487 - participating in existing transaction debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)] // 提交第一个事务 debug datasourcetransactionmanager:763 - initiating transaction commit debug datasourcetransactionmanager:329 - committing jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@11c4a3f] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@11c4a3f] after transaction
总结:当前有事务,goodserviceimpl.addgood()以事务方式运行。
修改配置如下:
- transactionservice:supports
- areaserviceimpl:mandatory
- goodserviceimpl:supports
运行结果如下:
------addgoodandarea------- debug datasourcetransactionmanager:888 - should roll back transaction but cannot - no transaction available org.springframework.transaction.illegaltransactionstateexception: no existing transaction found for transaction marked with propagation 'mandatory' at org.springframework.transaction.support.abstractplatformtransactionmanager.gettransaction(abstractplatformtransactionmanager.java:372) at org.springframework.transaction.interceptor.transactionaspectsupport.createtransactionifnecessary(transactionaspectsupport.java:595) at org.springframework.transaction.interceptor.transactionaspectsupport.invokewithintransaction(transactionaspectsupport.java:374) at org.springframework.transaction.interceptor.transactioninterceptor.invoke(transactioninterceptor.java:118) at org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:205) at org.springframework.aop.framework.jdkdynamicaopproxy.invoke(jdkdynamicaopproxy.java:219) at com.sun.proxy.$proxy23.addarea(unknown source) at com.morris.spring.service.transactionservice.addgoodandarea(transactionservice.java:20)
总结:当前没有有事务,areaserviceimpl.addarea()会抛出异常。
not_supported以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
配置如下:
- transactionservice:required
- areaserviceimpl:not_supported
- goodserviceimpl:not_supported
运行结果如下:
// 开启第一个事务 debug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.transactionservice.addgoodandarea]: propagation_required,isolation_default debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@8ef162] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] to manual commit ------addgoodandarea------- // 挂起第一个事务 debug datasourcetransactionmanager:436 - suspending current transaction // 以非事务方式运行 debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_area(area_name, area_code) values(?,?)] debug datasourceutils:115 - fetching jdbc connection from datasource debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] // 恢复第一个事务 debug datasourcetransactionmanager:1043 - resuming suspended transaction after completion of inner transaction // 挂起第一个事务 debug datasourcetransactionmanager:436 - suspending current transaction // 以非事务方式运行 debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)] debug datasourceutils:115 - fetching jdbc connection from datasource debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] // 恢复第一个事务 debug datasourcetransactionmanager:1043 - resuming suspended transaction after completion of inner transaction // 提交第一个事务 debug datasourcetransactionmanager:763 - initiating transaction commit debug datasourcetransactionmanager:329 - committing jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@8ef162] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] after transaction
总结:执行goodserviceimpl.addgood()和areaserviceimpl.addarea()时当前存在事务,就把当前事务挂起。
never以非事务方式执行,如果当前存在事务,则抛出异常。
配置如下:
- transactionservice:never
- areaserviceimpl:never
- goodserviceimpl:never
运行结果如下:
// 没有事务都以非事务方式运行 ------addgoodandarea------- debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_area(area_name, area_code) values(?,?)] debug datasourceutils:115 - fetching jdbc connection from datasource debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)]
总结:当前没有事务都以非事务方式执行。
修改配置如下:
- transactionservice:required
- areaserviceimpl:never
- goodserviceimpl:never
运行结果如下:
// 开启第一个事务 debug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.transactionservice.addgoodandarea]: propagation_required,isolation_default debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@8ef162] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] to manual commit ------addgoodandarea------- debug datasourcetransactionmanager:864 - initiating transaction rollback debug datasourcetransactionmanager:345 - rolling back jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@8ef162] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] after transaction org.springframework.transaction.illegaltransactionstateexception: existing transaction found for transaction marked with propagation 'never' at org.springframework.transaction.support.abstractplatformtransactionmanager.handleexistingtransaction(abstractplatformtransactionmanager.java:430) at org.springframework.transaction.support.abstractplatformtransactionmanager.gettransaction(abstractplatformtransactionmanager.java:362) at org.springframework.transaction.interceptor.transactionaspectsupport.createtransactionifnecessary(transactionaspectsupport.java:595) at org.springframework.transaction.interceptor.transactionaspectsupport.invokewithintransaction(transactionaspectsupport.java:374) at org.springframework.transaction.interceptor.transactioninterceptor.invoke(transactioninterceptor.java:118) at org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:205) at org.springframework.aop.framework.jdkdynamicaopproxy.invoke(jdkdynamicaopproxy.java:219) at com.sun.proxy.$proxy23.addarea(unknown source) at com.morris.spring.service.transactionservice.addgoodandarea(transactionservice.java:20)
总结:areaserviceimpl.addarea()执行时存在事务就会抛出异常。
nested如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与required类似的操作。
配置如下:
- transactionservice:required
- goodserviceimpl:nested
- areaserviceimpl:nested
运行结果如下:
// 创建第一个事务 ebug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.transactionservice.addgoodandarea]: propagation_required,isolation_default // 创建第一个连接 debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@8ef162] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] to manual commit ------addgoodandarea------- // 创建回滚点 debug datasourcetransactionmanager:466 - creating nested transaction with name [com.morris.spring.service.areaserviceimpl.addarea] debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_area(area_name, area_code) values(?,?)] // 释放回滚点 debug datasourcetransactionmanager:754 - releasing transaction savepoint // 创建回滚点 debug datasourcetransactionmanager:466 - creating nested transaction with name [com.morris.spring.service.goodserviceimpl.addgood] debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)] // 释放回滚点 debug datasourcetransactionmanager:754 - releasing transaction savepoint debug datasourcetransactionmanager:763 - initiating transaction commit debug datasourcetransactionmanager:329 - committing jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@8ef162] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] after transaction
如果不抛出异常,使用required与nested都差不多,区别在于发生异常,下面演示required与nested发生异常时的区别:
配置如下:
- transactionservice:required
- areaserviceimpl:required
- goodserviceimpl:required
transactionservice.addgoodandarea修改如下:
@transactional(propagation = propagation.required) public void addgoodandarea() { system.out.println("------addgoodandarea-------"); try { areaservice.addarea(0); } catch (exception e) { e.printstacktrace(); } goodservice.addgood(); }
运行结果如下:
debug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.transactionservice.addgoodandarea]: propagation_required,isolation_default debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@f9aa66] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@f9aa66] to manual commit ------addgoodandarea------- debug datasourcetransactionmanager:487 - participating in existing transaction debug datasourcetransactionmanager:877 - participating transaction failed - marking existing transaction as rollback-only debug datasourcetransactionmanager:360 - setting jdbc transaction [com.mysql.cj.jdbc.connectionimpl@f9aa66] rollback-only java.lang.arithmeticexception: / by zero at com.morris.spring.service.areaserviceimpl.addarea(areaserviceimpl.java:18) ... ... debug datasourcetransactionmanager:487 - participating in existing transaction debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)] debug datasourcetransactionmanager:723 - global transaction is marked as rollback-only but transactional code requested commit debug datasourcetransactionmanager:877 - participating transaction failed - marking existing transaction as rollback-only debug datasourcetransactionmanager:360 - setting jdbc transaction [com.mysql.cj.jdbc.connectionimpl@f9aa66] rollback-only debug datasourcetransactionmanager:723 - global transaction is marked as rollback-only but transactional code requested commit debug datasourcetransactionmanager:864 - initiating transaction rollback debug datasourcetransactionmanager:345 - rolling back jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@f9aa66] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@f9aa66] after transaction org.springframework.transaction.unexpectedrollbackexception: transaction rolled back because it has been marked as rollback-only at org.springframework.transaction.support.abstractplatformtransactionmanager.processrollback(abstractplatformtransactionmanager.java:905) ... ...
从运行结果可以发现事务全部都回滚了。
将上面的配置修改如下:
- transactionservice:required
- areaserviceimpl:nested
- goodserviceimpl:nested
运行结果如下:
debug datasourcetransactionmanager:381 - creating new transaction with name [com.morris.spring.service.transactionservice.addgoodandarea]: propagation_required,isolation_default debug drivermanagerdatasource:144 - creating new jdbc drivermanager connection to [jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull] debug datasourcetransactionmanager:265 - acquired connection [com.mysql.cj.jdbc.connectionimpl@8ef162] for jdbc transaction debug datasourcetransactionmanager:283 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] to manual commit ------addgoodandarea------- debug datasourcetransactionmanager:466 - creating nested transaction with name [com.morris.spring.service.areaserviceimpl.addarea] debug datasourcetransactionmanager:857 - rolling back transaction to savepoint java.lang.arithmeticexception: / by zero at com.morris.spring.service.areaserviceimpl.addarea(areaserviceimpl.java:18) ... ... debug datasourcetransactionmanager:466 - creating nested transaction with name [com.morris.spring.service.goodserviceimpl.addgood] debug jdbctemplate:860 - executing prepared sql update debug jdbctemplate:609 - executing prepared sql statement [insert into t_good(good_name, price) values(?,?)] debug datasourcetransactionmanager:754 - releasing transaction savepoint debug datasourcetransactionmanager:763 - initiating transaction commit debug datasourcetransactionmanager:329 - committing jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@8ef162] debug datasourcetransactionmanager:390 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] after transaction
从运行结果可以发现areaservice.addarea()回滚了(本来就没有提交内容),goodservice.addgood()的内容提交了。
注意只有运行时异常以及rollbakcfor指定的异常才会回滚。