1. 次要的三种流传机制
https://www.cnblogs.com/xzwbl…
REQUIRED、REQUIRES_NEW、NESTED
留神:以下所有的 testA()
办法与 testB()
办法是在两个类之中且都讲 bean 交给 spring 治理,如非如此会造成事务生效问题请看第二局部
1.1 REQUIRED(默认)
以后没有事务,则新建一个事务,如存在事务,则退出此事务
// 例子
// A.class
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void testA() {insertDataToTableA(dataList);
testB();}
// B.class
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void testB(){insertDataToTableB(dataList);
int i = 1/0;
insertDataToTableC(dataList);
}
// 后果 testB()退出 testA()的事务,testB()产生异样,两个办法均回滚
// 如果 testA()不加事务,只有 testB()加, 则 testA()胜利,testB()回滚
1.2 REQUIRES_NEW
创立一个新事务,如果存在以后事务,则挂起该事务。即无论是否已存在事务都新建事务
// 例子
// A.class
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void testA() {insertDataToTableA(dataList);
testB();
int i = 1/0;
}
// B.class
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
public void testB(){insertDataToTableB(dataList);
insertDataToTableC(dataList);
}
// 后果 testA() 回滚 testB() 胜利,阐明不是一个事物里
1.3 NESTED
如果以后事务存在,则在嵌套事务中执行(子事务),否则 REQUIRED 的操作一样(开启一个事务)
这里须要留神两点:
- 和 REQUIRES_NEW 的区别
REQUIRES_NEW 是新建一个事务并且新开启的这个事务与原有事务无关,而 NESTED 则是以后存在事务时(咱们把以后事务称之为父事务)会开启一个嵌套事务(称之为一个子事务)。
在 NESTED 状况下父事务回滚时,子事务也会回滚,而在 REQUIRES_NEW 状况下,原有事务回滚,不会影响新开启的事务。
- 和 REQUIRED 的区别
REQUIRED 状况下,调用方存在事务时,则被调用方和调用方应用同一事务,那么被调用方出现异常时,因为共用一个事务,所以无论调用方是否 catch 其异样,事务都会回滚
而在 NESTED 状况下,被调用方产生异样时,调用方能够 catch 其异样,这样只有子事务回滚,父事务不受影响
// 例子 1
// A.class
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void testA() {insertDataToTableA(dataList);
testB();
int i = 1/0;
}
// B.class
@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
public void testB(){insertDataToTableB(dataList);
insertDataToTableC(dataList);
}
// 后果 二者皆回滚,因为是嵌套的父子级事务,所以有一个有问题就都回滚
// 例子 2
// A.class
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void testA() {insertDataToTableA(dataList);
try{testB();
}catch(Exception e){log.error(e)
}
}
// B.class
@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
public void testB(){insertDataToTableB(dataList);
int i = 1/0;
insertDataToTableC(dataList);
}
// 后果 testA 胜利、testB 回滚,阐明绝对 REQUIRED 异样能够被捕捉解决
2. 事务生效
2.1 外部调用问题
1. A 带事务调 B 不带事务
// 例 0 无异样 -> 两个都能顺利插入表
// 例 1 B 办法异样
// 后果 事务失效(A,B 专用一个事务),两个办法皆回滚
@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)
public Integer insertUserInfo() {jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" +
"(5,'Jack5')");
// 外部调用问题
insertUserInfo2();
return null;
}
//@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.NESTED)
public Integer insertUserInfo2() {jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" +
"(9,'tom9')");
int i = 1 / 0;
return null;
}
// 例 2 A 办法调用完 B 后再异样 -> 后果雷同事务失效
2. A 带事务调 B 带事务(REQUIRES_NEW)
// 例 1 B 异样 且 B 的流传机制为 REQUIRES_NEW(新建事务)// 后果 B 事务失败(追加到 A 事务里),A,B 皆回滚,阐明 A,B 还是在一个事务里,A 办法里异样时同样状况
@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)
public Integer insertUserInfo() {jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" +
"(5,'Jack5')");
// 外部调用问题
insertUserInfo2();
return null;
}
@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRES_NEW)
public Integer insertUserInfo2() {jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" +
"(9,'tom9')");
int i = 1 / 0;
return null;
}
3. A 带事务调 B 带事务(NESTED)
// 例 1 B 异样 且 B 的流传机制为 NESTED,在 A 中捕捉异样
// 后果 A 事务胜利,B 事务失败,异样后仍能插入数据
@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)
public Integer insertUserInfo() {jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" +
"(5,'Jack5')");
// 外部调用问题
try {insertUserInfo2();
}catch (Exception e){e.printStackTrace();
}
return null;
}
@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.NESTED)
public Integer insertUserInfo2() {jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" +
"(9,'tom9')");
int i = 1 / 0;
return null;
}
// 例 2 B 异样 且 B 的流传机制为 NESTED,在 A 中不捕捉异样
// 后果 A,B 皆回滚
// 例 3 A 异样 且 B 的流传机制为 NESTED,在 A 中捕捉异样与否都一样
// 后果 A,B 皆回滚
4. A 不带事务调 B 带事务
// 例 0 无异样 -> 两个都能顺利插入表
// 例 1 B 办法异样 -> 事务生效,A,B 都不会回滚,都会插入数据
// 第一局部中的三种流传机制都会是这种状况
//@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)
public Integer insertUserInfo() {jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" +
"(5,'Jack5')");
// 外部调用问题
insertUserInfo2();
return null;
}
@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)
public Integer insertUserInfo2() {jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" +
"(9,'tom9')");
int i = 1 / 0;
return null;
}
// 例 2 A 办法调用完 B 后异样 -> 与例 1 雷同,事务生效,A,B 都不会回滚,都会插入数据
5. 综述
尽量避免外部调用,最多用 A 带事务调 B 不带事务,尤其是不能用 A 不带事务调 B 带
2.2 外部调用问题事务失败起因
- A 带事务调 B 带事务:放在一个代理类里,追加到 A 的事务,只会代理 A,而后 this 调用 B
- A 不带事务调 B 带事务:外部调用没走 Spring 的 AOP 加强代理机制,也就没做事务加强操作,实质是 this 实例调用