spring的事务传播属性required_nested的原理介绍
本文讲解"spring的事务传播属性required_nested的原理介绍",希望能够解决相关问题。
传统事务中回滚点的使用
package com.morris.spring.demo.jdbc; import java.sql.*; /** * 传统jdbc中回滚点的使用 */ public class traditionsavepointdemo { public static void main(string[] args) throws sqlexception { string url = "jdbc:mysql://127.0.0.1:3306/test?useunicode=true&allowmultiqueries=true&characterencoding=utf-8&usefastdateparsing=false&zerodatetimebehavior=converttonull"; string username = "user"; string password = "user"; connection connection = drivermanager.getconnection(url, username, password); connection.setautocommit(false); // 不自动提交 savepoint one = connection.setsavepoint("one"); savepoint two = null; try { statement statement = connection.createstatement(); statement.execute("insert into t_good(good_name, price) values('iphone14', 9999)"); statement.close(); two = connection.setsavepoint("two"); } catch (exception e) { e.printstacktrace(); connection.rollback(one); // 回滚事务 } try { statement statement = connection.createstatement(); statement.execute("insert into t_good(good_name, price) values('iphone15', 9999)"); statement.close(); boolean flag = true; if(flag) { throw new runtimeexception("xxxx"); } } catch (exception e) { e.printstacktrace(); connection.rollback(two); // 回滚事务 } connection.commit(); } }
在一个事务中可以指定回滚事务到某一个阶段,实现精确控制事务。
事务的传播属性nested
在spring中,要想使用事务中的回滚点,可以使用传播属性nested。
com.morris.spring.service.transactionservice#addgoodandarea
@transactional(propagation = propagation.required) public void addgoodandarea() { system.out.println("------addgoodandarea-------"); goodservice.addgood(); areaservice.addarea(0); }
com.morris.spring.service.areaserviceimpl#addarea
@transactional(propagation = propagation.nested) @override public boolean addarea(int i) { int y = 1000000 / i; area area = new area(); area.setareacode(y); area.setareaname("shenzhen"); return areadao.insert(area); }
com.morris.spring.service.goodserviceimpl#addgood
@transactional(propagation = propagation.nested) @override public boolean addgood() { good good = new good(); good.setgoodname("iphone"); good.setprice(bigdecimal.valueof(99999)); return gooddao.insert(good); }
运行结果如下:
debug datasourcetransactionmanager:384 - 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:267 - acquired connection [com.mysql.cj.jdbc.connectionimpl@8ef162] for jdbc transaction debug datasourcetransactionmanager:285 - switching jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] to manual commit ------addgoodandarea------- debug datasourcetransactionmanager:477 - 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:767 - releasing transaction savepoint debug datasourcetransactionmanager:477 - creating nested transaction with name [com.morris.spring.service.areaserviceimpl.addarea] debug datasourcetransactionmanager:870 - rolling back transaction to savepoint debug datasourcetransactionmanager:877 - initiating transaction rollback debug datasourcetransactionmanager:347 - rolling back jdbc transaction on connection [com.mysql.cj.jdbc.connectionimpl@8ef162] debug datasourcetransactionmanager:392 - releasing jdbc connection [com.mysql.cj.jdbc.connectionimpl@8ef162] after transaction java.lang.arithmeticexception: / by zero ... ...
发现整个事务都已经回滚了,按照回滚点的逻辑,addarea()方法抛出异常,不是应该只回滚到addarea()前吗,也就是addgood()应该被提交,这是为什么呢?
如果我们将addarea()方法try catch起来,就能得到我们想要的结果,addgood()被提交,而addarea()回滚,这又是为什么呢?我们带着这几个问题来分析源码。
addareaandgood()开启事务
addareaandgood()开启事务,最外层方法使用传播属性propagation_required、propagation_requires_new、propagation_nested效果都一样,都是开启一个新的事务。
org.springframework.transaction.support.abstractplatformtransactionmanager#gettransaction
else if (def.getpropagationbehavior() == transactiondefinition.propagation_required || def.getpropagationbehavior() == transactiondefinition.propagation_requires_new || def.getpropagationbehavior() == transactiondefinition.propagation_nested) { // 第一次进来 suspendedresourcesholder suspendedresources = suspend(null); if (debugenabled) { logger.debug("creating new transaction with name [" + def.getname() + "]: " + def); } try { // 开启新事务 return starttransaction(def, transaction, debugenabled, suspendedresources); } catch (runtimeexception | error ex) { resume(null, suspendedresources); throw ex; } }
addgood()获得事务并创建回滚点
addgood()从threadlocal中获得addareaandgood()创建的事务,然后发现自己的传播属性为propagation_nested,就创建了一个回滚点。
org.springframework.transaction.support.abstractplatformtransactionmanager#handleexistingtransaction
if (definition.getpropagationbehavior() == transactiondefinition.propagation_nested) { if (!isnestedtransactionallowed()) { throw new nestedtransactionnotsupportedexception( "transaction manager does not allow nested transactions by default - " + "specify 'nestedtransactionallowed' property with value 'true'"); } if (debugenabled) { logger.debug("creating nested transaction with name [" + definition.getname() + "]"); } if (usesavepointfornestedtransaction()) { // create savepoint within existing spring-managed transaction, // through the savepointmanager api implemented by transactionstatus. // usually uses jdbc 3.0 savepoints. never activates spring synchronization. defaulttransactionstatus status = preparetransactionstatus(definition, transaction, false, false, debugenabled, null); // 创建回滚点 status.createandholdsavepoint(); return status; } else { // nested transaction through nested begin and commit/rollback calls. // usually only for jta: spring synchronization might get activated here // in case of a pre-existing jta transaction. return starttransaction(definition, transaction, debugenabled, null); } }
addgood()提交事务时释放回滚点
addgood()并不会真正的提交事务,因为事务并不是addgood()创建的,只是在提交时会将之前创建的回滚点释放。
org.springframework.transaction.support.abstractplatformtransactionmanager#processcommit
if (status.hassavepoint()) { // nested的提交 if (status.isdebug()) { logger.debug("releasing transaction savepoint"); } unexpectedrollback = status.isglobalrollbackonly(); // 只是释放回滚点 status.releaseheldsavepoint(); }
addarea()获得事务并创建回滚点
流程与addgood()一致。
addarea()回滚事务释放回滚点
addarea()发生异常,会执行回滚事务的逻辑,并没有真正的回滚事务,因为事务并不是addarea()创建的,,只是将之前创建的回滚点释放。 org.springframework.transaction.support.abstractplatformtransactionmanager#processrollback
if (status.hassavepoint()) { // 用于nested传播机制,发生异常 // 回滚至回滚点 if (status.isdebug()) { logger.debug("rolling back transaction to savepoint"); } status.rollbacktoheldsavepoint(); }
addareaandgood()回滚这个事务
addarea()发生异常后继续往外抛,addareaandgood()也会捕获到异常,然后执行回滚逻辑,这样整个事务都回滚了。 org.springframework.transaction.support.abstractplatformtransactionmanager#processrollback
else if (status.isnewtransaction()) { // 只有最外层的事务newtransaction=true if (status.isdebug()) { logger.debug("initiating transaction rollback"); } // 事务的回滚 /** * @see org.springframework.jdbc.datasource.datasourcetransactionmanager#dorollback(org.springframework.transaction.support.defaulttransactionstatus) */ dorollback(status); }
为什么将addarea()方法try catch起来,整个事务就不会回滚了呢?
因为将addarea()方法try catch起来后,addareaandgood()就会执行提交事务的逻辑,这样addgood()就被提交了。
关于 "spring的事务传播属性required_nested的原理介绍" 就介绍到此。希望多多支持硕编程。