该篇博客主要介绍@Transactional注解失效的12种情况,我是看b站的一个up主进行搬运总结的,希望对我、对你都有一点一点的帮助。
1、访问权限的问题
private、default、protected、public,他们的权限从左到右,依次变大。
把某些事务方法,定义成错误的访问权限,就会导致事务功能出问题。即不是public类型就会失效
2、方法用final修饰
事务是通过AOP完成,即通过代理的方式完成,将一个方法定义成final意味着该方法不能被重写了,那么事务就失效了。
注意:如果某个方法是static时,同样无法通过动态代理,变成事务方法。
3、方法内部调用
在某个Service类的某个方法中,调用另外一个是事务方法,比如:
@Service
public class UserService{
@Autowired
private UserService userService;
//@Transactional
public void add(UserModel userModel){
userMapper.insertUser(userModel);
updateStatus(userModel);
}
@Transactional
public void updateStatus(UserModel userModel){
doSameThing();
}
}
以上方法中采用的是this对updateStatus()方法进行调用的,因此updateStatus不能生成事务。
解决以上问题可以将自身进行注入的方式
@Service
public class ServiceA{
@AutoWired
private ServiceA serviceA;
public void save(User user){
queryData1();
queryData2();
serviceA.daSave(user);
}
@Transactional(rollbackFor=Exception.class)
public void daSave(User user){
addData1();
uodateData2();
}
}
4、未被spring管理
在开发过程中容易忽略一些细节问题,比如忘记了@Controller、@Service、@Component、@Repository等注解
没使用以上注解交给spring进行管理,那么事务就不会生效。
5、多线程调用
在实际项目开发中,多线程的使用场景还是挺多的。如果spring事务用在多线程场景中,会有什么问题
我们说的同一事物,其实是指同一个数据库连接,只有拥有同一个数据库连接才能同时提交和回滚。如果在不同的线程,拿到的数据库连接肯定是不一样的,所以是不同的事务。
6、表不支持事务
mysql5之前,默认的数据库引擎是MYISAM,它的好处是:索引文件和数据文件是分开存储的,对于查多写少的单表操作,性能比innodb更好。
7、未开启事务
如果你使用的是springboot项目,那么springboot通过DataSourceTransactionaManagerAutoConfiguration类,已经默默地帮你开启了事务,你只需要配置spring.datasource相关参数即可。
如果使用的还是传统的spring项目,那么你需要在applicationContext.xml文件中,手动配置事务相关参数。如果忘了配置,事务肯定是不会生效的。
二、事务不回滚
8、错误的传播特性
其实,在使用@Transactional注解时,是可以指定propagation参数的。
该参数得作用是指定事务的传播特性,spring目前支持7中传播特性:
REQUIRED
SUPPORTS
MANDATORY
REQUIRES_NEW
NOT_SUPPORTED
NEVER
NESTED
如果事务设置成Propagation.NEVER,这种类型的传播特性不支持事务,如果有事务则抛出异常。
9、自己吞了异常
事物不会回滚,最常见的问题是:开发者在代码中手动try...catch了异常。比如:
@slf4j
@Service
public class UserService{
@Transactional
public void add(UserModel userModel){
try{
saveData(userModel);
updateData(userModel);
}catch(Exception e){
log.error(e.getMessage,e);
}
}
}
这种情况下spring事务当然不会回滚,因为开发者自己捕获了异常,又没有手动抛出,换句话说就是把异常吞掉了。
如果想要spring事务能够正常回滚,必须抛出它能够处理的异常,如果没有抛出异常,则spring认为程序是正常的。
10、手动抛出了别的异常
即使开发者没有手动捕获异常,但是抛出的异常不正确,spring事务也不会回滚。
@slf4j
@Service
public class UserService{
@Transactional
public void add(UserModel userModel) throws Exception{
try{
saveData(userModel);
updateData(userModel);
}catch(Exception e){
log.error(e.getMessage,e);
throw new Exception(e);
}
}
}
上面的情况,开发人员自己捕获了异常:Exception,事务同样不会回滚。
因为spring事务,默认情况下只会回滚RuntimeException(运行时异常)和Error(错误),对于普通的Exception(非运行时异常),它不会回滚。
11、自定了回滚异常
在使用@Transactional注解声明事务时,有时我们想自定义回滚异常,spring也是支持的。可以通过设置rollbackFor参数,来完成这个功能。
@slf4j
@Service
public class UserService{
@Transactional(rollbackFor = BusinessException.class)
public void add(UserModel userModel) throws Exception{
saveData(userModel);
updateData(userModel);
}
}
如果在执行上面这段代码,保存和更新数据时,程序报错了,抛出了SqlException、DuplicateKeyException等异常。而BusinessException是我们自定义的异常,报错的异常不属于BusinessException,所以事务也不会回滚。
12、嵌套事务回滚多了
public class UserService{
@AutoWired
private UserMapper userMapper;
@AutoWired
private RoleService reloService;
@Transactional
public void add(UserModel userModel) throws Exception{
userMapper.insertUser(userModel);
roleService.doOtherThing();
}
}
@Service
public class RoleService{
@Transactional(propagation = Propagation.NESTED)
public void doOtherThing(){
System.out.println("保存role表数据");
}
}
这种情况使用了嵌套的内部事务,原本是希望roleService.doOtherThing方法时,如果出现了异常,只回滚doOtherThing方法里的内容,不回滚userMapper.insertUser里的内容,即回滚保存点,但事实是,insertUser也回滚了。
可以使用下面的方式进行事务的回滚。
public class UserService{
@AutoWired
private UserMapper userMapper;
@AutoWired
private RoleService reloService;
@Transactional
public void add(UserModel userModel) throws Exception{
userMapper.insertUser(userModel);
try{
roleService.doOtherThing();
}catch(Exception e){
log.error(e.getMessage(),e);
}
}
}
@Service
public class RoleService{
@Transactional(propagation = Propagation.NESTED)
public void doOtherThing(){
System.out.println("保存role表数据");
}
}
学习之所以会想睡觉,是因为那是梦开始的地方。
ଘ(੭ˊᵕˋ)੭ (开心) ଘ(੭ˊᵕˋ)੭ (开心)ଘ(੭ˊᵕˋ)੭ (开心)ଘ(੭ˊᵕˋ)੭ (开心)ଘ(੭ˊᵕˋ)੭ (开心)
------不写代码不会凸的小刘
版权归原作者 网恋褙骗八万 所有, 如有侵权,请联系我们删除。