使用@Transactional注解时,可以指定propagation参数。
Spring支持7种事务传播特性,如下:
事务行为 | 说明 |
---|---|
required | 默认值,支持当前事务;如果没有事务,新建一个事务 |
supports | 支持当前事务;如果没有事务,以非事务方式执行 |
mandatory | 支持当前事务;如果没有事务,抛异常 |
requires_new | 新建事务;如果当前存在事务,则挂起 |
not_supported | 以非事务方式运行;如果当前存在事务,则挂起 |
never | 以非事务方式运行;如果当前存在事务,抛异常 |
nested | 如果当前存在事务,则在嵌套事务内执行;无则创建新的事务 |
目前只有这三种传播特性才会创建新事务:required,requires_new,nested。
不支持事务的传播机制:supports、not_supported、never。如果配置了这三种传播方式的话,在发生异常的时候,事务是不会回滚的。
@Transactional
private void createUserPrivate(User user){
userRepository.save(user);
}
由于使用Spring AOP代理实现的,因为只有当事务方法被spring代理对象直接调用时,才会由Spring生成的代理对象来管理。
@Component
public class TestServiceImpl implements TestService {
@Resource
TestMapper testMapper;
@Transactional
public void insertTestInnerInvoke() {
testMapper.insert(new Test(210,20,30));
int i = 1/0;
}
public void testInnerInvoke(){
insertTestInnerInvoke();
}
}
测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Resource
TestServiceImpl testService;
@Test
public void testInnerInvoke(){
// 正常回滚
//testService.insertTestInnerInvoke();
// 不会回滚
testService.testInnerInvoke();
}
}
testService.insertTestInnerInvoke();正常回滚,因为 调用 insertTestInnerInvoke事务方法 是spring代理对象(@Resource、@Autowired等注入) testService 直接调用的。
testService.testInnerInvoke(); 不会回滚,因为 调用 insertTestInnerInvoke事务方法 是一个普通方法中 间接通过 this 对象调用的。
@Transactional
public void insertTestInnerInvoke() {
try {
testMapper.insert(new Test(210,20,30));
int i = 1/0;
} catch (Exception e) {
e.printStackTrace();
}
}
事务的传播行为,默认值为 Propagation.REQUIRED。
这种失效是由于配置错误,若是错误的配置以下三种 propagation,事务将不会发生回滚。
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
@Transactional注解默认只会对RuntimeException、Error及其子类进行回滚。
@Transactional 相当于 @Transactional(rollbackFor=RuntimeException.class) ,只对抛出的 RuntimeException 异常,才会事务回滚。
也可以配置多个异常
@Transactional(rollbackFor = {RuntimeException.class, Exception.class})
public void insertTestInnerInvoke() {
try {
testMapper.insert(new Test(210,20,30));
int i = 1/0;
} catch (Exception e) {
e.printStackTrace();
throw new Exception();
}
}
因为DDL操作会隐式提交。
DML(Data Manipulation Language)数据操作语言,用于操作数据库对象中包含的数据,操作的对象是记录。
主要命令:insert、delete、update。
DDL(Data Definition Language)数据定义语言,主要用于定义或改变表结构。操作对象包括数据库本身以及数据库对象,如表、视图等等。
主要命令:create、alter、drop、truncate。
@Transactional(rollbackFor = Exception.class)
@Override
public void add(User user) throws Exception {
// 步骤1
userMapper.insertUser(user);
// 步骤2
userMapper.createTable(tableName,columnList);
// 步骤3
userMapper.insertUserRole(user.getRole());
}
结论:如果步骤2的DDL语句错误,步骤1不会回滚,因为DDL操作隐式提交。
简单逻辑解决方案:(调整执行顺序)将DDL操作放在最后
最终解决方案:可以将步骤3和步骤1放到别的类中以嵌套事物的方式,内部事务的回滚不影响外部事务(DDL不支持事务,需要自己使用逻辑回滚比如步骤4),代码如下:
方法一:(嵌套事务)
@Service
public class UserServiceImpl implements userService {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleService roleService;
@Transactional(rollbackFor = Exception.class)
public void add(User user) throws Exception {
// 步骤2
userMapper.createTable(tableName,columnList);
try{
roleService.doOtherThing();
}catch(Exception e){
// 步骤4 删除步骤2创建的表
userMapper.dropTable(tableName);
// 防止异常被catch掉事务失效
throw new RuntimeException("操作错误");
}
}
}
@Service
public class RoleServiceImpl implements RoleService{
@Autowired
private UserMapper userMapper;
@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
public void doOtherThing() {
// 步骤1
userMapper.insertUser(user);
// 步骤3
userMapper.insertUserRole(user.getRole());
}
}
方法二:(新建事务)
@Service
public class UserServiceImpl implements userService {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleService roleService;
@Transactional(rollbackFor = Exception.class)
public void add(User user) throws Exception {
// 步骤2
userMapper.createTable(tableName,columnList);
try {
roleService.doOtherThing();
} catch(Exception e){
// 步骤4 删除步骤2创建的表
userMapper.dropTable(tableName);
// 防止异常被catch掉事务失效
throw new RuntimeException("操作错误");
}
}
}
@Service
public class RoleServiceImpl implements RoleService{
@Autowired
private UserMapper userMapper;
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
@Override
public void doOtherThing() {
// 步骤1
userMapper.insertUser(user);
// 步骤3
userMapper.insertUserRole(user.getRole());
}
}
参考:
当@Transactional遇上DDL的事务回滚问题_ddl事务回滚_W@Lucky的博客-CSDN博客