目录
一、事务的传播属性
一、传播属性的概述
二、Propagation.REQUIRED属性
①、实例代码:
②、Debug调试
③、正常情况伪代码流程
④、代码添加异常
⑤、经典案例一
⑦、分析:
⑧、经典案例二
⑨、伪代码案列二
⑩、分析二
二、Propagation.REQUIRED_NEW属性
①、实例代码:
②、源码跟读:
二、Propagation.REQUIRED_NSTED属性
①、示例代码:
②、核心源码
二、总结:
一、事务的传播属性
一、传播属性的概述
- Propagation.REQUIRED(required):支持当前事务,如果当前有事务, 那么加入事务, 如果当前没有事务则新建一个(默认情况)
- Propagation.NOT_SUPPORTED(not_supported) : 以非事务方式执行操作,如果当前存在事务就把当前事务挂起,执行完后恢复事务(忽略当前事务);
- Propagation.SUPPORTS (supports) :如果当前有事务则加入,如果没有则不用事务。
- Propagation.MANDATORY (mandatory) :支持当前事务,如果当前没有事务,则抛出异常。(当前必须有事务)
- PROPAGATION_NEVER (never) :以非事务方式执行,如果当前存在事务,则抛出异常。(当前必须不能有事务)
- Propagation.REQUIRES_NEW (requires_new) :支持当前事务,如果当前有事务,则挂起当前事务,然后新创建一个事务,如果当前没有事务,则自己创建一个事务。
- Propagation.NESTED (nested 嵌套事务) :如果当前存在事务,则嵌套在当前事务中。如果当前没有事务,则新建一个事务自己执行(和required一样)。嵌套的事务使用保存点作为回滚点,当内部事务回滚时不会影响外部事物的提交;但是外部回滚会把内部事务一起回滚回去。(这个和新建一个事务的区别)
ps:本文就常用的 Rropagation.REQUIRED、Propagation.REQUIRES_NEW、Propagation.NESTED这三种进行案例演示。
二、Propagation.REQUIRED属性
①、实例代码:
@Service
public class TransactionService {
@Autowired
private UserService userService;
@Autowired
private EmpleeService empleeService;
@Transactional
public int transaction(User user, Emplyee emplyee) {
int j = userService.addUser(user);
int i = empleeService.addEmplyee(emplyee);
System.out.println("userService-->" + j);
System.out.println("empleeService-->" + i);
return i;
}
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private EmplyeeMapper emplyeeMapper;
@Autowired
private UserMapper userMapper;
@Override
@Transactional
public int addUser(User user) {
int i = userMapper.insert(user);
return i;
}
}
@Service
public class EmpleeServiceImpl implements EmpleeService {
@Autowired
private EmplyeeMapper emplyeeMapper;
@Transactional
@Override
public int addEmplyee(Emplyee emplyee) {
int i = emplyeeMapper.insert(emplyee);
return i;
}
}
②、Debug调试
当调用transactionService.transaction(user,emplyee)方法的时候,因为transactionService是代理对象,所以会调用到事务切面入口,如果外层方法上的事务传播属性Rropagation.REQUIRED、Propagation.REQUIRES_NEW、Propagation.NESTED是这三种,一开始逻辑代码是一摸一样的
下面直接到AbstractPlatformTransactionManager类getTransaction方法中的逻辑
接下来来到开启事务方法中的获取数据库连接并设置相关的属性,此时进入到 DataSourceTransactionManager类中的doBegin()
doBegin方法逻辑总结:
- 创建事务对象,获取数据库连接对象并封装成connectHolder对象并设置到事务对象中。
- 设置事务的隔离级别
- 关闭自动提交事务
- ThreadLocal找那个建立数据源和连接的绑定关系
下面直接到了具体方法的调用逻辑代码:
接着分析userService.addUser(user)调用的逻辑,此时也会进入到事务切面中,此时不会进入到doBegin方法中,会走到 类AbstractPlatformTransactionManager 的handleExistingTransaction方法,这个方法中所有的if判断都没进,只是把事务状态中newTransaction属性变为false
下面走到了addUser方法的commit逻辑中,
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
boolean globalRollbackOnly = false;
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
globalRollbackOnly = status.isGlobalRollbackOnly();
}
//事务传播属性为NESTED,才会有回滚点
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
//擦除回滚点
status.releaseHeldSavepoint();
}
//只有是新事务才可以提交
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
//提交事务
doCommit(status);
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (globalRollbackOnly) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
// can only be caused by doCommit
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
// can only be caused by doCommit
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException ex) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, ex);
throw ex;
}
catch (Error err) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, err);
throw err;
}
// Trigger afterCommit callbacks, with an exception thrown there
// propagated to callers but the transaction still considered as committed.
try {
triggerAfterCommit(status);
}
finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
cleanupAfterCompletion(status);
}
}
根据源码addUser不会自己提交事务,下面接着反射调用empleeService.addEmplyee方法,第三次进入事务切面
此时的empleeService.addEmplyee调用跟addUser方法的流程是一样的。此处省略。。
最后的事务提交交给最外层中的Transaction方法来完成的。
③、正常情况伪代码流程
transaction外层方法的开始
try{
createTransactionIfNecessary 开启事务
userService.addUser(user)
addUser开始执行
try{
createTransactionIfNecessary 开启事务
int i = userMapper.insert(user);
return i;
}catch (Throwable ex) {
//回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
//提交事务
commitTransactionAfterReturning(txInfo);
addUser结束
empleeService.addEmplyee(emplyee)
addEmplyee开始执行
try{
createTransactionIfNecessary 开启事务
int i = emplyeeMapper.insert(emplyee);
return i;
}catch (Throwable ex) {
//回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
//提交事务
commitTransactionAfterReturning(txInfo);
addEmplyee结束
}catch (Throwable ex) {
//回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
//提交事务
commitTransactionAfterReturning(txInfo);
transaction外层方法的结束
④、代码添加异常
其他的不变。。。。
@Override
@Transactional
public int addUser(User user) {
int i = userMapper.insert(user);
if (true) {
throw new RuntimeException("sss");
}
return i;
}
运行结果: 两张表中都没有添加数据,
原因分析:addUser中发生异常,会调到自己的回滚代码,然而这时因为当前事务中的事务newTransaction是为false的,他自己不能进行事务的回滚操作,只能往外层继续抛,这时addEmplyee方法根本就没执行所以到了外层transaction方法的回滚。
⑤、经典案例一
代码示例:
@Transactional
public void transaction(User user, Emplyee emplyee) {
try {
TransactionSynchronizationManager.getResource(dataSourced);
userService.addUser(user);
System.out.println("userService-->");
empleeService.addEmplyee(emplyee);
System.out.println("empleeService-->");
} catch (Exception e) {
e.printStackTrace();
}
}
addUser:
@Transactional
public int addUser(User user) {
int i = userMapper.insert(user);
return i;
}
addEmplyee:
@Transactional
public int addEmplyee(Emplyee emplyee) {
int i = emplyeeMapper.insert(emplyee);
if (true) {
throw new RuntimeException("sss");
}
return i;
}
运行的伪代码:
transaction外层方法的开始
try{ try{
createTransactionIfNecessary 开启事务
userService.addUser(user)
addUser开始执行
try{
createTransactionIfNecessary 开启事务
int i = userMapper.insert(user);
return i;
}catch (Throwable ex) {
//回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
//提交事务
commitTransactionAfterReturning(txInfo);
addUser结束
empleeService.addEmplyee(emplyee)
addEmplyee开始执行
try{
createTransactionIfNecessary 开启事务
int i = emplyeeMapper.insert(emplyee); if (true) { throw new RuntimeException("sss"); }
return i;
}catch (Throwable ex) {
//回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
//提交事务
commitTransactionAfterReturning(txInfo);
addEmplyee结束
}catch (Throwable e) {
e.printStackTrace();
}
}catch (Throwable ex) {
//回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
//提交事务
commitTransactionAfterReturning(txInfo); //最终执行到这一步进入最外层提交
transaction外层方法的结束
结果:最终执行到Transaction方法的commitTransactionAfterReturning 逻辑中。但是在最后的结果中,两个表中的都没有插入数据。 why?源码跟读
⑦、分析:
在代码执行的过程中,addUser方法正常但执行没有资格提交事务,接着执行addEmplyee方法,发生了异常,会走到addEmplyee方法的回滚操作,debug来看他的回滚中做了哪些操作?
进入到TransactionAspectSupport类中completeTransactionAfterThrowing方法:
继续跟进回滚中做了哪些操作,来到了AbstractPlatformTransactionManager类中processRollback方法
private void processRollback(DefaultTransactionStatus status) {
try {
try {
triggerBeforeCompletion(status);
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
}
else if (status.isNewTransaction()) {
//平时会走这个
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
doRollback(status);
}
else if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
//会走到这里
doSetRollbackOnly(status);
}
省略。。。
}
下面是doSetRollbackOnly方法具体逻辑:
接着来到了transaction方法commitTransactionAfterReturning方法进行提交
接着来到了AbstractPlatformTransactionManager类中commit方法
@Override
public final void commit(TransactionStatus status) throws TransactionException {
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus);
return;
}
//有全局的异常,就会进入到这个if
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
//回滚
processRollback(defStatus);
// Throw UnexpectedRollbackException only at outermost transaction boundary
// or if explicitly asked to.
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
return;
}
//正常情况进入到这个commit
processCommit(defStatus);
}
所以结果会全部进行回滚操作,这个是在Spring源码comiit操作中做了异常的回滚。
总结:
前提:所有的方法传播属性都是默认的时候
在Spring源码中,commit提交过程中不一定是做了真正的提交代码逻辑,当方法中有异常,并且在自己的代码中try cath捕获了,这时在自己方法中虽然不能回滚事务,但是它会最后的回滚逻辑中设置一个属性rollbackOnly为true,到了最往外层的提交事务逻辑中,会有对这个属性判断是否进行回滚操作。所以验证了,在Spring源码提交事务的过程中,不一定会真正的提交事务
⑧、经典案例二
示例代码:
transaction方法:
@Transactional()
public void transaction(User user, Emplyee emplyee) throws Exception {
System.out.println("userService-->");
userService.addUser(user);
TransactionSynchronizationManager.getResource(dataSourced);
empleeService.addEmplyee(emplyee);
System.out.println("empleeService-->");
}
addEmplyee:
@Transactional
@Override
public int addEmplyee(Emplyee emplyee) {
int i = emplyeeMapper.insert(emplyee);
return i;
}
addUser代码:
@Override
@Transactional()
public int addUser(User user) throws Exception {
int i = userMapper.insert(user);
if (true) throw new Exception("aa");
return i;
}
结论:addUser表中插入成功有一条数据,addEmplyee没有执行,所以没数据。
⑨、伪代码案列二
transaction外层方法的开始
try{ createTransactionIfNecessary 开启事务
userService.addUser(user)
addUser开始执行
try{
createTransactionIfNecessary 开启事务
int i = userMapper.insert(user);if (true) { throw new Exception("sss"); }
return i;
}catch (Throwable ex) {
//回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
//提交事务
commitTransactionAfterReturning(txInfo);
addUser结束
empleeService.addEmplyee(emplyee)
addEmplyee开始执行
try{
createTransactionIfNecessary 开启事务
int i = emplyeeMapper.insert(emplyee);
return i;
}catch (Throwable ex) {
//回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
//提交事务
commitTransactionAfterReturning(txInfo);
addEmplyee结束
}catch (Throwable ex) {
//回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
//提交事务
commitTransactionAfterReturning(txInfo); //最终执行到这一步进入最外层提交
transaction外层方法的结束
⑩、分析二
首先第一次进入切面中逻辑是一样的,到了addUser方法执行逻辑,因为addUser中抛出了异常所以直接进入到回滚代码逻辑中,
protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.hasTransaction()) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
//如果抛出的异常事属性中定义的异常或者他的子类则回滚,否则判断父类中的方法,父类中判断的异常类型是运行时异常或者erro则返回true
if (txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by rollback error", ex);
throw err;
}
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
//否则进入提交事务
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by commit error", ex);
throw err;
}
}
}
}
从回滚的逻辑代码中可以看出,回滚是要判断异常的类型的,因为addUser中抛出异常不匹配,所以会进入到commit提交的逻辑,但是此时addUser中的事务状态newTransaction为false,所以提交事物的操作还得交给外层的Transaction方法中。因此addUser会插如数据成功。
总结:
在Spring事务回滚中,不一定就是真正的回滚事务,也可能会做事务的提交。在回滚的逻辑中会做方法异常类型判断,只有是异常类型匹配才会进行相应的回滚操作。
二、Propagation.REQUIRED_NEW属性
①、实例代码:
@Transactional
public void transaction(User user, Emplyee emplyee) {
try {
TransactionSynchronizationManager.getResource(dataSourced);
userService.addUser(user);
System.out.println("userService-->");
empleeService.addEmplyee(emplyee);
System.out.println("empleeService-->");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public int addUser(User user) throws Exception {
int i = userMapper.insert(user);
return i;
}
@Transactional
@Override
public int addEmplyee(Emplyee emplyee) {
int i = emplyeeMapper.insert(emplyee);
return i;
}
②、源码跟读:
前面的操作基本都是相同的,只不过是addUser方法中的隔离级别变成了Propagation.REQUIRES_NEW,接下来直接到addUser方法的调用切面中
在类中 AbstractPlatformTransactionManager中handleExistingTransaction方法
//省略。。。。
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
//从这创建的是addUser的绑定关系
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
catch (Error beginErr) {
resumeAfterBeginException(transaction, suspendedResources, beginErr);
throw beginErr;
}
}
省略。。。
唯一不同的是在doBegin方法前面中做了挂起的操作,下面分析挂起逻辑:类 DataSourceTransactionManager 方法doSuspend
protected final SuspendedResourcesHolder suspend(Object transaction) throws TransactionException {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
try {
Object suspendedResources = null;
if (transaction != null) {
//拿到挂起的连接对象
suspendedResources = doSuspend(transaction);
}
String name = TransactionSynchronizationManager.getCurrentTransactionName();
TransactionSynchronizationManager.setCurrentTransactionName(null);
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
TransactionSynchronizationManager.setActualTransactionActive(false);
return new SuspendedResourcesHolder(
suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
}
省略。。。。
}
核心代码:
@Override
protected Object doSuspend(Object transaction) {
//上方法的连接对象置null
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
txObject.setConnectionHolder(null);
//解除数据源和连接对象的绑定关系
return TransactionSynchronizationManager.unbindResource(this.dataSource);
}
然后就是addUser自己提交自己的事务了,他是一个单独的事务连接,新建的,newTransaction为true,addUser在提交完事务以后需要做那些事情呢?
在提交代码中的逻辑
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
//省略。。。。。
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) {
//提交事务
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
doCommit(status);
}
省略。。。。。。
finally {
cleanupAfterCompletion(status);
}
}
cleanupAfterCompletion方法做了哪些事情,类
下面是释放资源中的 releaseConnection
下面看这个resume方法:
protected final void resume(Object transaction, SuspendedResourcesHolder resourcesHolder)
throws TransactionException {
//如果存在挂起资源
if (resourcesHolder != null) {
Object suspendedResources = resourcesHolder.suspendedResources;
if (suspendedResources != null) {
//将挂起的资源,恢复绑定关系
doResume(transaction, suspendedResources);
}
List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
if (suspendedSynchronizations != null) {
TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
doResumeSynchronization(suspendedSynchronizations);
}
}
}
到此后面的逻辑就一样了,后面就不一一说明了。
二、Propagation.REQUIRED_NSTED属性
①、示例代码:
@Transactional()
public void transaction(User user, Emplyee emplyee) throws Exception {
System.out.println("empleeService-->");
userService.addUser(user);
TransactionSynchronizationManager.getResource(dataSourced);
empleeService.addEmplyee(emplyee);
System.out.println("userService-->");
}@Transactional(propagation = Propagation.NESTED)
public int addUser(User user) {
int i = userMapper.insert(user);
return i;
}@Transactional(propagation = Propagation.NESTED)
public int addEmplyee(Emplyee emplyee) {
int i = emplyeeMapper.insert(emplyee);
if (true) throw new RuntimeException("sss");
return i;
}
②、核心源码
在创建事务的方法中会有属性为REQUIRED_NSTED的处理逻辑,再类中 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() + "]");
}
//useSavepointForNestedTransaction 默认为true
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.
//事务状态为false
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
//创建回滚点,里面就是java手动创建回滚点的APi
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.
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, null);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
}
省略。。。
其他的操作是一样的,在addUser中来到事务提交的逻辑中,此时addUser中是有回滚点的,看提交事务中做了哪些操作。
接着到了addEmplyee方法执操作,这时也会创建回滚点,到被代理方法调用方法之后有异常则进入回滚代码中,此时会进入到红色代码段中
private void processRollback(DefaultTransactionStatus status) {
try {
try {
triggerBeforeCompletion(status);
//是否有回滚点
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
//回滚到回滚点,并将回滚点设置为空
status.rollbackToHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
doRollback(status);
}
else if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
doSetRollbackOnly(status);
}
else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
}
省略。。。。。。
}
结果:两个表中都没数据,why? 那么addEmplyee方法中出现异常回滚了在我们意料之中,此时这个addUser也回滚了,这不就违背了Nested按照回滚点回滚的作用了么?改造代码
@Transactional()
public void transaction(User user, Emplyee emplyee) throws Exception {
try{
System.out.println("empleeService-->");
userService.addUser(user);
TransactionSynchronizationManager.getResource(dataSourced);
empleeService.addEmplyee(emplyee);
System.out.println("userService-->");
} catch (Exception e){
e.printStackTrace();
}
}其他的代码不变
上述改造后的代码运行结果,addUser执行成功,addEmplyee照常失败。实现了按照回滚点回滚了。
分析:
NESTED和EQUIRED区别
- 首先上述的中(经典案例一),当代码中有异常并且在外层调用方法中添加了try catch捕获了会修改rollbackOnly属性为true,导致在最后外层中commit提交事务做了回滚操作。
- 而在NESTED传播属性中,代码一样的前提下,实现了按照回滚点回滚的操作。其他的基本和REQUIRE一样。
二、总结:
- 事务跟连接对象挂钩的
- 事务的传播属性就是方法中事务的流转的
- 只要被Spring内部try catch捕获到,并且这个异常RuntimeException或者Error才会执行回滚操作(很重要!!!)
- 事务中的提交逻辑中不一定会真正的提交事务也有可能回滚事务,在回滚事务中也会判断异常的类型是否匹配,如果匹配才会进行回滚操作。
- 如果最外层方法上的事务传播属性Rropagation.REQUIRED、Propagation.REQUIRES_NEW、Propagation.NESTED是这三种,一开始逻辑代码是一摸一样的。这一点很重要!!
- 还有就是在原理不台熟悉的一定要写伪代码来看事务是怎么传播的