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的原理介绍" 就介绍到此。希望多多支持硕编程。


