在TransactionTemplate回调中执行的方法也可能在内部再次被TransactionTemplate嵌套,且使用REQUIRED等复用外层事务的方式,此时如内层事务发生回滚,则在外层即便捕获了异常后再更新数据库也是失败的(如插入异常日志场景);
此时可嵌套新事物保障插入成功
//测试代码
package local.my.sb.demospringboot
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.InitializingBean
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.stereotype.Component
import org.springframework.transaction.PlatformTransactionManager
import org.springframework.transaction.UnexpectedRollbackException
import org.springframework.transaction.support.TransactionTemplate
@SpringBootTest @Component
class DoTestJdbcTrGroovy implements InitializingBean{
@Autowired JdbcTemplate jdbcTemp;@Autowired PlatformTransactionManager trM
/**复用已有事务*/
def trReq = new TransactionTemplate().tap{propagationBehavior=PROPAGATION_REQUIRED}
/**暂停已有事务并开启新事物*/
def trNew = new TransactionTemplate().tap {propagationBehavior=PROPAGATION_REQUIRES_NEW}
/**如果已有事务可用就复用,否则开新事务*/
def trNes = new TransactionTemplate().tap {propagationBehavior=PROPAGATION_NESTED}
void afterPropertiesSet() throws Exception {
trReq.transactionManager=trNew.transactionManager=trNes.transactionManager=trM
jdbcTemp.execute('drop table if exists tab;create table tab(id int)')
}
@Test //错误场景
void testErr() {
try {
trReq.execute {
try{
trReq.execute {
jdbcTemp.execute('insert into tab values(1)')
throw new RuntimeException()
}
}catch (ignore){
jdbcTemp.execute('insert into tab values(2)')//此处插入将失败,将导致trReq抛异常
}
assert it.rollbackOnly
}
}catch (UnexpectedRollbackException e){
assert e.message.contains('rollback-only')//事务已回滚
}
assert jdbcTemp.queryForList('select id from tab',Integer).empty //一条都没插入成功
}
@Test //正确场景
void testOk(){
//REQUIRES_NEW和NESTED都可以保障新事务,根据业务逻辑独立性需要选择,一般要求独立事务则用REQUIRES_NEW
[trNew,trNes].forEach {
try{
trReq.execute {
try{
trReq.execute {
jdbcTemp.execute('insert into tab values(1)')
throw new RuntimeException()
}
}catch (ignore){
trNew.execute {jdbcTemp.execute('insert into tab values(2)')}
}
assert it.rollbackOnly
}
}catch (ignore){}
assert [2]==jdbcTemp.queryForList('select id from tab',Integer)//1插入失败,但2插入成功
jdbcTemp.execute('truncate table tab')
}
}
}