Spring 事务传播机制(生命周期)
note:以下举例中 methodA 属于外部方法,和 methodB , methodC , methodD 不在同一个类中。
1 required
required:如果当前所处外围方法已有事务,则加入到外围方法的事务中。若外围方法没有声明事务,则新开启一个事务执行。
/** * Support a current transaction, create a new one if none exists. */
- 外部方法未声明事务,内部方法间不受影响
public void methodA() throws Exception {
methodB();
methodC();
throw new Exception("exception in method a");
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
TestEntity test = new TestEntity("required-b", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodC() {
TestEntity test = new TestEntity("required-c", "25");
TestEntity result = testRepository.save(test);
}
// B和C方法均可以正常插入
//methodA方法不处于事务内,所有在methodA中抛出异常不影响其他两个独立的事务
public void methodA() throws Exception {
methodB();
methodC();
throw new Exception("exception in method a");
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
TestEntity test = new TestEntity("required-b", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodC() throws Exception {
TestEntity test = new TestEntity("required-c", "25");
TestEntity result = testRepository.save(test);
throw new Exception("exception in method c");
}
// B插入成功,C插入失败;二者都在自己的事务里执行,methodC不影响methodB的执行结果。
- 外部方法声明事务,所有的方法都会加入到同一个事物中执行
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA(){
methodB();
try {
methodC();
} catch (Exception e) {
System.out.println("catch exception c");
}
}
public void methodB() {
TestEntity test = new TestEntity("required-b", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodC() throws Exception {
TestEntity test = new TestEntity("required-c", "25");
TestEntity result = testRepository.save(test);
throw new Exception("exception in method c");
}
//B,C均插入失败;A,B,C属于同一个事物
//当外围方法methodA添加上事务声明时,mehtodB和methodC会加入到外围方法methodA的事务中执行。此时无论methodA,methodB,methodC哪一个方法抛出异常都会导致所有方法的回滚。
- 当外部方法A声明了事务,即使被调用方法B抛出的异常在A中被捕获,也会导致事务回滚。因为REQUIRED修饰的方法C和A属于同一个事务。
- 当外部方法A声明了事务,在A中产生的异常在A中被捕获,不会导致事务回滚。因为此时异常并未被事务感知。
2 requires_new
requires_new 声明一个新的事务,和其他事务独立执行;即使外部方法已经存在事务,会开启一个新的的事务,两个事务互不影响。
/** * Create a new transaction, and suspend the current transaction if one exists. */
- 外部方法未开启事务,被调用方法事务传播机制为requires_new
此时各个方法运行在自己独立的事务中,互不影响。
- 外部方法开启事务
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA() throws Exception{
methodB();
mehtodC();
methodD();
throw new Exception();
}
public void methodB() {
TestEntity test = new TestEntity("required-b", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodC() {
TestEntity test = new TestEntity("required-c", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodD() throws Exception {
TestEntity test = new TestEntity("required-d", "25");
TestEntity result = testRepository.save(test);
throw new Exception("exception in method d");
}
// methodB失败,methodC成功,methodD失败
// 其中B加入到了A的事务中,随着A的事务回滚,B也跟着回滚。C开启一个新的事务,不受其他事务的影响。D开启新事务。
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA() throws Exception{
methodB();
mehtodC();
methodD();
}
public void methodB() {
TestEntity test = new TestEntity("required-b", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodC() {
TestEntity test = new TestEntity("required-c", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodD() throws Exception {
TestEntity test = new TestEntity("required-d", "25");
TestEntity result = testRepository.save(test);
throw new Exception("exception in method d");
}
// B失败,C成功,D失败
// 由于C开启新的事务,不受其他事务的影响。D开启新的事务,D中抛出异常回滚。A受到D抛出异常的影响回滚,由于B加入A的事务,所以B一同回滚。
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA() throws Exception{
methodB();
mehtodC();
try{
methodD();
}catch(Exception e){
System.out.print(e.getMessage());
}
}
//B成功,C成功,D失败
//此时A和D处于不同的事务中,且D的异常被捕获,A和B不受影响。
3 nested
嵌入事务:当外部方法没有声明事务时,nested和required一致;当外部方法声明了事务时,声明了nested事务类型的子方法会以子事务的方式嵌入到外部事务,父级事务影响子事务,如果子事务抛出异常不被父事务感知,则父级事务不受影响;子事务可以单独回滚,并级的子事务间不会相互影响。
/** * Execute within a nested transaction if a current transaction exists, */
- 外部方法未声明事务
public void methodA() throws Exception{
methodB();
mehtodC();
methodD();
throw new Exception();
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
TestEntity test = new TestEntity("required-b", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void methodC() {
TestEntity test = new TestEntity("required-c", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void methodD() throws Exception {
TestEntity test = new TestEntity("required-d", "25");
TestEntity result = testRepository.save(test);
throw new Exception("exception in method d");
}
// B成功,C成功,D失败
//外部方法未开启事务,NESTED类型的事务会以独立事务的方式执行(类似REQUIRED),互不影响。
- 外部方法声明事务
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
TestEntity test = new TestEntity("required-b", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void methodC() {
TestEntity test = new TestEntity("required-c", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void methodD() throws Exception {
TestEntity test = new TestEntity("required-d", "25");
TestEntity result = testRepository.save(test);
throw new Exception("exception in method d");
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA() throws Exception{
methodB();
mehtodC();
methodD();
}
// 全部失败
//B加入到A的事务中,C和D以子事务的方式嵌入到A事务。D抛出异常导致A-B事务回滚,同样子事务C也会跟随父级事务回滚。
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
TestEntity test = new TestEntity("required-b", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void methodC() {
TestEntity test = new TestEntity("required-c", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void methodD() throws Exception {
TestEntity test = new TestEntity("required-d", "25");
TestEntity result = testRepository.save(test);
throw new Exception("exception in method d");
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA() throws Exception{
methodB();
try{
methodD();
}catch(Exception e){
System.out.println(e.getMessage());
}
mehtodC();
}
// B,C成功,D失败
// D子事务异常被捕获,A-B父事务无感知,C子事务不受影响
required
和nested
的区别在于:如果存在外部事务,required是加入已经存在的事务,和外部事物属于同一个事务。而nested是以子事务的方式嵌入到外部事务,只要子事务的异常不被外部事务感知,就不会影响外部事物。
requires_new
和nested
的区别在于:二者相同的地方在于,内部(子)事务异常不被外部事物感知时不影响外部事物;不同点在于,外部事物的异常不会影响内部事务。而外部事物的异常一定会影响nested子事务,导致nested子事务同样回滚。
4 supports
supports:如果当前存在外部事务,则加入该外部事务;如果不存在外部事务,则以非事务模式执行。
/** * Support a current transaction, execute non-transactionally if none exists. */
- 外部事物不存在
public void methodA() throws Exception{
methodB();
mehtodC();
methodD();
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
TestEntity test = new TestEntity("test-b", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public void methodC() {
TestEntity test = new TestEntity("test-c", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public void methodD() throws Exception {
TestEntity test = new TestEntity("test-d", "25");
TestEntity result = testRepository.save(test);
throw new Exception("exception in method d");
}
// B,C,D均成功
// B在自己事务里执行,C,D以非事务方式执行
- 存在外部事物
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA() throws Exception{
methodB();
mehtodC();
try{
methodD();
}catch(Exception e){...}
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
TestEntity test = new TestEntity("test-b", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public void methodC() {
TestEntity test = new TestEntity("test-c", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public void methodD() throws Exception {
TestEntity test = new TestEntity("test-d", "25");
TestEntity result = testRepository.save(test);
throw new Exception("exception in method d");
}
// B,C,D均失败
// 存在外部事物,required和support均会加入到外部事务,A-B-C-D属于同一个事务。
required
和supports
的区别在于:当不存在外部事务时,rquired
会创建一个新的事务,而supports
则以非事务的方式执行。
5 not_supported
not_supported:以非事务的模式执行,如果存在外部事物则暂时挂起外部事物
/** * Execute non-transactionally, suspend the current transaction if one exists. */
- 不存在外部事物,以非事务方式执行
- 存在外部事物
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA() {
methodB();
mehtodC();
methodD();
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
TestEntity test = new TestEntity("test-b", "25");
TestEntity result = testRepository.save(test);
}
public void methodC() {
TestEntity test = new TestEntity("test-c", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.NOT_SUPPORTED, rollbackFor = Exception.class)
public void methodD() {
TestEntity test = new TestEntity("test-d", "25");
TestEntity result = testRepository.save(test);
throw new RuntimeException("exception in method d");
}
// B,C均失败,D插入成功
// 存在外部事物,D抛出运行时异常导致外部事务A-B-C回滚,而D声明为NOT_SUPPORTED,会使外部事物暂时挂起以非事务的方式执行,不受外部事务的影响。
not_supported
和不声明事务的区别:当存在外部事物时,如果一个被调用的方法没有申明事务,其实是处于外部事物中的;而被not_supported
声明的话,会暂时将外部事物挂起,即使外部事务回滚了,也不影响not_supported
修饰的方法执行;存在外部事务时,方法声明为
required
和不声明事务的效果一样,都是在外部 事务中执行。
6 never
never:强制以非事务的方式执行。如果当前存在事务,则抛出异常
/** * Execute non-transactionally, throw an exception if a transaction exists. */
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA() {
methodB();
mehtodC();
methodD();
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
TestEntity test = new TestEntity("test-b", "25");
TestEntity result = testRepository.save(test);
}
public void methodC() {
TestEntity test = new TestEntity("test-c", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.NEVER, rollbackFor = Exception.class)
public void methodD() {
TestEntity test = new TestEntity("test-d", "25");
TestEntity result = testRepository.save(test);
}
//B,C,D均失败
//抛出异常:org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
7 mandatory
mandatory:强制方法在事务中执行,如果当前不存在事务,则抛出异常。
/** * Support a current transaction, throw an exception if none exists. */
- 存在外部事物,正常执行
- 不存在外部事物,抛出异常
public void methodA() {
methodB();
mehtodC();
methodD();
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() {
TestEntity test = new TestEntity("test-b", "25");
TestEntity result = testRepository.save(test);
}
public void methodC() {
TestEntity test = new TestEntity("test-c", "25");
TestEntity result = testRepository.save(test);
}
@Transactional(propagation = Propagation.NEVER, rollbackFor = Exception.class)
public void methodD() {
TestEntity test = new TestEntity("test-d", "25");
TestEntity result = testRepository.save(test);
}
//B,C,成功,D失败
//B以独立的事务执行;C以非事务的方式执行;D未通过事务校验,没有进入方法体就异常终止执行。
//抛出异常:org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
Spring事务隔离级别
同各种数据库的隔离级别
Spring 事务和 try-catch:
checked
异常:在代码中需要主动捕获或者声明继续往上抛;
unchecked
异常:属于RuntimeException,在编码中无法感知;
通过try-catch可以使异常不被事务切面感知,不影响事务的执行。当异常抛到切面可以感知到的地方时会引起事务的回滚。
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveA() throws Exception {
TestEntity test = new TestEntity("test-A", "25");
testMapper.save(test);
throw new Exception("throw exception in a");
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveB() throws Exception {
TestEntity test = new TestEntity("test-B", "25");
testMapper.save(test);
throw new RuntimeException("throw runtime exception in B");
}
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = RuntimeException.class)
public void saveC() {
TestEntity test = new TestEntity("test-C", "25");
testMapper.save(test);
throw new Exception("throw runtime exception in C");
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void saveD() {
TestEntity test = new TestEntity("test-D", "25");
testMapper.save(test);
throw new RuntimeException("throw runtime exception in D ");
}
//A C成功; B D均失败
由上面结果可知:
- 当@Transactional注解不指定rollbackFor时,默认只对RuntimeException异常回滚,对于checked异常不会回滚;
/** * <p>By default, a transaction will be rolling back on {@link RuntimeException} * and {@link Error} but not on checked exceptions (business exceptions). See * {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)} * for a detailed explanation. */ Class<? extends Throwable>[] rollbackFor() default {}; //DefaultTransactionAttribute.rollbackOn(Throwable) public boolean rollbackOn(Throwable ex) { return (ex instanceof RuntimeException || ex instanceof Error); }
当指定了rollbackFor时,只会对声明的Class和其继承子类的异常进行回滚。
RuntimieException extends Exception
Spring 事务失效场景:
1 方法所属的bean并没有被spring容器管理
2 数据库引擎不支持事务
3 方法私有
4 自身调用:同一个类中的方法调用
spring的事务管理是通过aop切面实现的,切面的实现是基于代理模式,通过生成一个代理类实现对被代理对象功能的增强。同一个类内的方法间通过this调用,并不会涉及代理类,所以当自身调用时spring的事务管理并不会生效。
5 异常在方法内部被捕获
6 捕获异常类型错误