一文解析spring中事务的传播机制

一文解析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类似的操作。
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()执行时发现已有事务,就使用当前事务执行。

requires_new

新建事务,如果当前存在事务,把当前事务挂起。

配置如下:

  • 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指定的异常才会回滚。

相关文章