让我们来详细讲解 Spring @Transactional事务失效的原因分析。事务是应用程序中非常重要的概念,对于保证数据一致性具有至关重要的作用。Spring框架提供了@Transactional注解作为声明式事务管理的方式,可以极大的减轻我们对事务的控制。然而,有时候我们会发现@Transactional失效了,这时候我们需要对其原因进行分析。
一. 什么是 @Transactional?
@Transactional是Spring框架的注解,用于声明式的事务管理。在使用@Transactional注解的方法中,Spring会为其生成一个动态代理,这个代理会在方法开始调用时开启一个事务,在方法执行结束后,根据方法的执行情况决定是提交事务,还是回滚事务。
二. @Transactional 失效的原因
在实际的应用场景中,我们很容易发现@Transactional注解失效了的情况。下面我们将逐个解释事务失效的原因。
1. 对于只有一个@Transactional的方法内部调用了其他带有事务注解的方法
示例1
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void addUser(User user) {
userDao.addUser(user);
try {
// 在当前方法内调用带有事务注解的方法
// 由于事务的传播行为默认为REQUIRED,所以会继承当前的事务
// 这个调用的方法执行了回滚操作,导致当前事务也回滚了
addUserWithError(user);
} catch (Exception e) {
e.printStackTrace();
}
}
@Transactional
public void addUserWithError(User user) {
userDao.addUser(user);
throw new RuntimeException("模拟数据重复添加异常");
}
}
如果这个时候调用UserService.addUser方法,在执行addUserWithError方法时发生了异常,则由于事务传播行为的默认设置为Required,当前事务也会回滚。在addUser方法结束以后,针对于User的事务逻辑并没有生效,数据没有被插入进去。
示例2
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Autowired
private RoleService roleService;
@Override
public void addUser(User user, Role role) {
userDao.addUser(user);
// 在当前方法内调用另一个 Service 方法
// 默认情况下,这个 Service 方法会在当前事务内进行操作
roleService.addRole(role);
}
}
@Service
@Transactional
public class RoleServiceImpl implements RoleService {
@Autowired
private RoleDao roleDao;
@Override
public void addRole(Role role) {
roleDao.addRole(role);
}
}
因为在调用addRole方法时,当前事务并没有被关闭,所以实际上数据并没有被提交。因此,如果发现在@Transactional注解声明的方法内部调用了另一个带有事务注解的方法,务必保证被调用的方法不会回滚事务。
2. 对于一个类中的方法相互调用
假如一个类中有多个方法,在其中一个方法中使用了@Transactional注解,而另一个方法却直接调用了该方法。这时候事务就会失效。这是因为在这个类中,Spring通过生成代理类来管理事务,因此在同一个类中的方法中的@Transactional注解不会被代理类所拦截,从而导致事务失效。
示例
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
public void addUsers(List<User> users) {
users.forEach(this::addUser);
}
public void addUser(User user) {
userDao.addUser(user);
}
}
如果在调用addUsers方法之前没有开启事务,则在addUser方法内插入数据库的数据并不会回滚。要解决这个问题,我们可以使用编程式事务的方式,在每个方法内部都手动开启和关闭事务。
三. 总结
在使用@Transactional注解时,务必要注意各个事务之间的传播行为。对于只有一个@Transactional的方法内部调用了其他带有事务注解的方法,务必保证被调用的方法不会回滚事务。对于一个类中的方法相互调用,需要注意事务的传播行为。如果遇到事务失效的问题,可以使用编程式事务的方式手动开启和关闭事务。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring @Transactional事务失效的原因分析 - Python技术站