琪姐问了个问题,原来的方法逻辑,相似以下伪代码:数据库
updateTableA.setStatus(0); rpcOperateAccount(); updateTableA.setStatus(1);
相信很好理解。优化
琪姐说将这段逻辑放到线程池中一块儿提交就OK(意思应该是DB落库成功)。ui
executorService.execute(()=>{ try{ // 上面那段逻辑 } catch(Exception e){ LOGGER.error("") } })
可是把那段逻辑拿出来就落库失败:线程
func doSomething() throw BizException { // 上面那段逻辑 }
最后琪姐说,修改了Spring的事务传播机制好了(应该是从默认的Required => Requires_New)。code
虽然没有源码,可是能够基于这个问题说说Spring的事务传播机制。接口
并且事务会由于RuntimeException和Error回滚。事务
传播记住有以下几种:rpc
public interface TransactionDefinition { int PROPAGATION_REQUIRED = 0; int PROPAGATION_SUPPORTS = 1; int PROPAGATION_MANDATORY = 2; int PROPAGATION_REQUIRES_NEW = 3; int PROPAGATION_NOT_SUPPORTED = 4; int PROPAGATION_NEVER = 5; int PROPAGATION_NESTED = 6; }
经常使用的主要有Required,RequiresNew,Nested三种。源码
若是想事务一块儿执行能够用Required知足大部分场景,若是不想让执行的子事务的结果影响到父事务的提交能够将子事务设置为RequiresNew。it
嵌套是子事务套在父事务中执行,子事务是父事务的一部分,在进入子事务以前,父事务创建一个回滚点,叫save point,而后执行子事务,这个子事务的执行也算是父事务的一部分,而后子事务执行结束,父事务继续执行。重点就在于那个save point。看几个问题就明了了:
若是子事务回滚,会发生什么? 父事务会回滚到进入子事务前创建的save point,而后尝试其余的事务或者其余的业务逻辑,父事务以前的操做不会受到影响,更不会自动回滚。
若是父事务回滚,会发生什么? 父事务回滚,子事务也会跟着回滚!为何呢,由于父事务结束以前,子事务是不会提交的,咱们说子事务是父事务的一部分,正是这个道理。那么:
事务的提交,是什么状况? 是父事务先提交,而后子事务提交,仍是子事务先提交,父事务再提交?答案是第二种状况,仍是那句话,子事务是父事务的一部分,由父事务统一提交。
PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别:
它们很是相似,都像一个嵌套事务,若是不存在一个活动的事务,都会开启一个新的事务。使用 PROPAGATION_REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务同样,一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。两个事务不是一个真正的嵌套事务。同时它须要JTA事务管理器的支持。
使用PROPAGATION_NESTED时,外层事务的回滚能够引发内层事务的回滚。而内层事务的异常并不会致使外层事务的回滚,它是一个真正的嵌套事务。DataSourceTransactionManager使用savepoint支持PROPAGATION_NESTED时,须要JDBC 3.0以上驱动及1.4以上的JDK版本支持。其它的JTA TrasactionManager实现可能有不一样的支持方式。
PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 “内部” 事务. 这个事务将被彻底 commited 或 rolled back 而不依赖于外部事务, 它拥有本身的隔离范围, 本身的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行。
另外一方面, PROPAGATION_NESTED 开始一个 “嵌套的” 事务, 它是已经存在事务的一个真正的子事务. 潜套事务开始执行时, 它将取得一个 savepoint. 若是这个嵌套事务失败, 咱们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交。
因而可知, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 彻底是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 若是外部事务 commit, 嵌套事务也会被 commit, 这个规则一样适用于 roll back.
PROPAGATION_REQUIRED应该是咱们首先的事务传播行为。它可以知足咱们大多数的事务需求。
事务状态TransactionStatus
TransactionStatus接口的一个实现,这个接口的内容以下:
public interface TransactionStatus{ boolean isNewTransaction(); // 是不是新的事物 boolean hasSavepoint(); // 是否有恢复点 void setRollbackOnly(); // 设置为只回滚 boolean isRollbackOnly(); // 是否为只回滚 boolean isCompleted; // 是否已完成 }
能够发现这个接口描述的是一些处理事务提供简单的控制事务执行和查询事务状态的方法,在回滚或提交的时候须要应用对应的事务状态。
事务注解
@Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.READ_COMMITTED, noRollbackFor={UserAccountException.class}, readOnly=true, timeout=3)
1.添加事务注解 使用propagation 指定事务的传播行为,即当前的事务方法被另一个事务方法调用时如何使用事务。 默认取值为REQUIRED,即便用调用方法的事务 REQUIRES_NEW:使用本身的事务,调用的事务方法的事务被挂起。
2.使用isolation 指定事务的隔离级别,最经常使用的取值为READ_COMMITTED。
3.默认状况下 Spring 的声明式事务对全部的运行时异常进行回滚,也能够经过对应的属性进行设置。一般状况下,默认值便可。
4.使用readOnly 指定事务是否为只读。 表示这个事务只读取数据但不更新数据,这样能够帮助数据库引擎优化事务。若真的是一个只读取数据库值得方法,应设置readOnly=true。
5.使用timeOut 指定强制回滚以前事务能够占用的时间。
回到琪姐那个问题上,既然修改了传播机制能够正常提交了,那么说明须要开启两个事务才能够提交,并且捕获的异常是Excepiton(RollbackFor)。还存在几个问题待解决: