传播特性:service:REQUIRED,dao:REQUIRED
两个都是required使用的是同一个事务,正常情况,在service提交commit
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="checkout" propagation="REQUIRED" />
<tx:method name="updateStock" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
service:
public void checkout(String username,int id){
try {
bookDao.updateStock(id);
} catch (Exception e) {
e.printStackTrace();
}
}
dao:
public void updateStock(int id){
String sql = "update book_stock set stock=stock-1 where id=?";
jdbcTemplate.update(sql,id);
for (int i = 1 ;i>=0 ;i--)
System.out.println(10/i);
}
dao层抛出异常导致
service层事务:外层开始没有事务所以这里是true
dao内层事务因为传播特性是required所以复用的是外层service事务
newSynchronization变为false
old是外层service,this是当前dao的
这里执行的sql不会实际生效因为没有commit,下面for会有异常 看看后续怎么处理异常再事务
completeTransactionAfterThrowing异常回滚
先处理回滚的规则,就是事务注解里的rollbackFor,rollbackForClassName,noRollbackFor,noRollbackForClassName属性如果没有设置就会调用父类的rollbackOn
super.rollbackOn(ex);
开始completeTransactionAfterThrowing的txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
把外层事务信息恢复回去
开始打印异常信息
在service层捕获了异常没有抛 所以外层事务不回滚
在这里完成回滚
UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only异常在这里抛出
数据库数据不会被修改
service:REQUIRED,dao:NESTED
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="checkout" propagation="REQUIRED" />
<tx:method name="updateStock" propagation="NESTED" />
</tx:attributes>
</tx:advice>
PROPAGATION_NESTED为事务设置一个回退点
设置mysql连接的保存点
保存点设置完成
createAndHoldSavepoint:140, AbstractTransactionStatus (org.springframework.transaction.support)
handleExistingTransaction:490, AbstractPlatformTransactionManager (org.springframework.transaction.support)
getTransaction:356, AbstractPlatformTransactionManager (org.springframework.transaction.support)
createTransactionIfNecessary:588, TransactionAspectSupport (org.springframework.transaction.interceptor)
invokeWithinTransaction:367, TransactionAspectSupport (org.springframework.transaction.interceptor)
invoke:125, TransactionInterceptor (org.springframework.transaction.interceptor)
proceed:199, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:780, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
invoke:100, ExposeInvocationInterceptor (org.springframework.aop.interceptor)
proceed:199, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:780, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
intercept:721, CglibAopProxy$DynamicAdvisedInterceptor (org.springframework.aop.framework)
updateStock:-1, BookDao$$EnhancerBySpringCGLIB$$421d7dae (com.mashibing.tx.xml.dao)
checkout:25, BookService (com.mashibing.tx.xml.service)
invoke:-1, BookService$$FastClassBySpringCGLIB$$66a1e40d (com.mashibing.tx.xml.service)
invoke:218, MethodProxy (org.springframework.cglib.proxy)
invokeJoinpoint:802, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
proceed:172, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:780, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
proceedWithInvocation:-1, 1079125839 (org.springframework.transaction.interceptor.TransactionInterceptor$$Lambda$47)
invokeWithinTransaction:374, TransactionAspectSupport (org.springframework.transaction.interceptor)
invoke:125, TransactionInterceptor (org.springframework.transaction.interceptor)
proceed:199, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:780, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
invoke:100, ExposeInvocationInterceptor (org.springframework.aop.interceptor)
proceed:199, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:780, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
intercept:721, CglibAopProxy$DynamicAdvisedInterceptor (org.springframework.aop.framework)
checkout:-1, BookService$$EnhancerBySpringCGLIB$$d6fab840 (com.mashibing.tx.xml.service)
main:17, TxTest (com.mashibing.tx.xml)
代码执行到这里没有新建连接,用的是外层方法事务和连接。
在mysql中保存点的使用:
stock=100 设置保存点p1 后面会回滚到P1 就能实现一个事务中部分成功部分失败。stock最后变为97,说明前三个修改语句生效,后三个失败,保存点的作用就是保存保存点之前的sql生效。后边的会回滚不会实际生效
执行dao的内层方法抛异常,此时事务有保存点在这里执行回滚保存点的操作,并且把rollbackOnly变为false,来进行重置保存点。
外层service捕获异常,继续后续执行
直接执行下面的commit,上面的两个if进不去
此时执行的是service的外层事务,没有保存点直接执行docommit,外层方法正常提交结束。
service:REQUIRED,dao:REQUIRED_NEW
BookDao的内层方式这里能获取到threadlocal里的连接。
doGetTransaction:275, DataSourceTransactionManager (org.springframework.jdbc.datasource)
getTransaction:349, AbstractPlatformTransactionManager (org.springframework.transaction.support)
createTransactionIfNecessary:588, TransactionAspectSupport (org.springframework.transaction.interceptor)
invokeWithinTransaction:367, TransactionAspectSupport (org.springframework.transaction.interceptor)
invoke:125, TransactionInterceptor (org.springframework.transaction.interceptor)
proceed:199, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:780, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
invoke:100, ExposeInvocationInterceptor (org.springframework.aop.interceptor)
proceed:199, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:780, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
intercept:721, CglibAopProxy$DynamicAdvisedInterceptor (org.springframework.aop.framework)
updateStock:-1, BookDao$$EnhancerBySpringCGLIB$$46f7c792 (com.mashibing.tx.xml.dao)
checkout:25, BookService (com.mashibing.tx.xml.service)
invoke:-1, BookService$$FastClassBySpringCGLIB$$66a1e40d (com.mashibing.tx.xml.service)
invoke:218, MethodProxy (org.springframework.cglib.proxy)
invokeJoinpoint:802, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
proceed:172, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:780, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
proceedWithInvocation:-1, 1988939205 (org.springframework.transaction.interceptor.TransactionInterceptor$$Lambda$47)
invokeWithinTransaction:374, TransactionAspectSupport (org.springframework.transaction.interceptor)
invoke:125, TransactionInterceptor (org.springframework.transaction.interceptor)
proceed:199, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:780, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
invoke:100, ExposeInvocationInterceptor (org.springframework.aop.interceptor)
proceed:199, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:780, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
intercept:721, CglibAopProxy$DynamicAdvisedInterceptor (org.springframework.aop.framework)
checkout:-1, BookService$$EnhancerBySpringCGLIB$$dbd50224 (com.mashibing.tx.xml.service)
main:17, TxTest (com.mashibing.tx.xml)
存在外部事务
当前dao是PROPAGATION_REQUIRES_NEW,先挂起外层事务
SuspendedResourcesHolder 挂起
/**
* 有些传播机制需要挂起当前的事务,比如NOT_SUPPORTED,REQUIRES_NEW首先会清除所有线程相关的同步状态,如果当前事务存在的话,就进行一些属性的清除,比如清空连接持有器,清空线程私有变量的同步状态,
* 最后把当前事务清除的属性保存到一个SuspendedResourcesHolder里,以便于恢复的时候设置会去
*/
@Nullable
protected final SuspendedResourcesHolder suspend(@Nullable 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);
}
catch (RuntimeException | Error ex) {
// doSuspend failed - original transaction is still active...
doResumeSynchronization(suspendedSynchronizations);
throw ex;
}
}
else if (transaction != null) {
// Transaction active but no synchronization active.
Object suspendedResources = doSuspend(transaction);
return new SuspendedResourcesHolder(suspendedResources);
}
else {
// Neither transaction nor synchronization active.
return null;
}
}
doSuspend
清空连接持有器
/**
* 实际挂起资源的方法
* @param transaction the transaction object returned by {@code doGetTransaction}
* @return
*/
@Override
protected Object doSuspend(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// 清空连接持有器
txObject.setConnectionHolder(null);
// 解绑线程私有的资源
return TransactionSynchronizationManager.unbindResource(obtainDataSource());
}
实际处理挂起事务 清除threadlocal的副本
被挂起的历史事务相关属性信息
doBegin(transaction, definition);// 开启事务和连接
开启连接和事务
service层的事务和dao层的不关联,各自有各自的事务和数据库连接,内层回滚不影响外层的了
通过数据源获取一个数据库连接对象 <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
// 通过数据源获取一个数据库连接对象 <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
Connection newCon = obtainDataSource().getConnection();//从DruidDataSource获取连接
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
// 把我们的数据库连接包装成一个ConnectionHolder对象 然后设置到我们的txObject对象中去
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
。。。。。。
// 关闭自动提交 如果不i关闭每次sql执行就自动提交了
if (con.getAutoCommit()) {
//设置需要恢复自动提交
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
// 关闭自动提交
con.setAutoCommit(false);
}
。。。。。。
// Bind the connection holder to the thread.
// 绑定我们的数据源和连接到我们的同步管理器上,把数据源作为key,数据库连接作为value 设置到线程变量中
if (txObject.isNewConnectionHolder()) {
// 将当前获取到的连接绑定到当前线程 数据源和连接持久器进行绑定
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
后续执行dao层抛出异常
completeTransactionAfterThrowing:668, TransactionAspectSupport (org.springframework.transaction.interceptor)
invokeWithinTransaction:379, TransactionAspectSupport (org.springframework.transaction.interceptor)
invoke:125, TransactionInterceptor (org.springframework.transaction.interceptor)
proceed:199, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:780, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
invoke:100, ExposeInvocationInterceptor (org.springframework.aop.interceptor)
proceed:199, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:780, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
intercept:721, CglibAopProxy$DynamicAdvisedInterceptor (org.springframework.aop.framework)
updateStock:-1, BookDao$$EnhancerBySpringCGLIB$$46f7c792 (com.mashibing.tx.xml.dao)
checkout:25, BookService (com.mashibing.tx.xml.service)
invoke:-1, BookService$$FastClassBySpringCGLIB$$66a1e40d (com.mashibing.tx.xml.service)
invoke:218, MethodProxy (org.springframework.cglib.proxy)
invokeJoinpoint:802, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
proceed:172, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:780, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
proceedWithInvocation:-1, 1988939205 (org.springframework.transaction.interceptor.TransactionInterceptor$$Lambda$47)
invokeWithinTransaction:374, TransactionAspectSupport (org.springframework.transaction.interceptor)
invoke:125, TransactionInterceptor (org.springframework.transaction.interceptor)
proceed:199, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:780, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
invoke:100, ExposeInvocationInterceptor (org.springframework.aop.interceptor)
proceed:199, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:780, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
intercept:721, CglibAopProxy$DynamicAdvisedInterceptor (org.springframework.aop.framework)
checkout:-1, BookService$$EnhancerBySpringCGLIB$$dbd50224 (com.mashibing.tx.xml.service)
main:17, TxTest (com.mashibing.tx.xml)
/**
* 真正回滚的处理方法,也就是获取JDBC连接,然后回滚
* @param status the status representation of the transaction
*/
@Override
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
// jdbc的回滚
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
cleanupAfterCompletion
/**
* 回滚后的处理工作,如果是新的事务同步状态的话,要把线程的同步状态清除了,
* 如果是新事务的话,进行数据清除,线程的私有资源解绑,重置连接自动提交,隔离级别,是否只读,释放连接等。
* 如果有挂起的事务,还要把这个事务给恢复,其实就是把属性设置回去
*
* Clean up after completion, clearing synchronization if necessary,
* and invoking doCleanupAfterCompletion.
* @param status object representing the transaction
* @see #doCleanupAfterCompletion
*/
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
// 设置完成状态
status.setCompleted();
if (status.isNewSynchronization()) {
// 线程同步状态清除
TransactionSynchronizationManager.clear();
}
// 如果是新事务的话,进行数据清除,线程的私有资源解绑,重置连接自动提交,隔离级别,是否只读,释放连接等
if (status.isNewTransaction()) {
doCleanupAfterCompletion(status.getTransaction());
}
// 有挂起的事务要恢复
if (status.getSuspendedResources() != null) {
if (status.isDebug()) {
logger.debug("Resuming suspended transaction after completion of inner transaction");
}
Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
// 结束之前事务的挂起状态
resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
}
}
resume恢复外层事务到本地线程,如果service方法有多个事务dao执行完一个dao需要将service的事务恢复到线程
/**
* 如果前面有事务被挂起,现在就要回复,其实就是把一些属性设置回去
*
* Resume the given transaction. Delegates to the {@code doResume}
* template method first, then resuming transaction synchronization.
* @param transaction the current transaction object
* @param resourcesHolder the object that holds suspended resources,
* as returned by {@code suspend} (or {@code null} to just
* resume synchronizations, if any)
* @see #doResume
* @see #suspend
*/
protected final void resume(@Nullable Object transaction, @Nullable 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);
}
}
}
service中对异常catch后续就不会处理异常 直接commit
public void checkout(String username,int id){
try {
bookDao.updateStock(id);
} catch (Exception e) {
e.printStackTrace();
}
}
1.如果service不catch异常会继续向外抛,最终会导致service层也会回滚 ,如此一来,service和dao一共回滚两次;即dao抛出没捕获,dao回滚,此时service捕获了就不回滚,没捕获也会回滚;
2.如果外层有异常抛出,内层正常执行,外层不会导致内层回滚,内层会影响外层,外层不会影响内层;即内外两层是不同事务,内层提交过了,外层有异常没捕获,外层事务回滚,此时内层事务不受影响