在项目开发中,如果涉及到多张表操作时,为了保证业务数据的一致性,大家都会采用事务机制;
但是好多小伙伴可能只是简单了解一下,一旦遇到事务失效的情况,便会无从下手,此篇文章给大家整理了一下常见Spring事务失效的场景,希望开发过程尽量避免踩坑,造成时间精力的浪费。
目录
1、失效场景
-
@Transactional配置的方法非public权限修饰;
-
@Transactional所在类非Spring容器管理的bean;
-
@Transactional所在类中,注解修饰的方法被类内部方法调用;
-
业务代码抛出异常类型非RuntimeException,事务失效;
-
业务代码中存在异常时,使用try…catch…语句块捕获,而catch语句块没有throw new RuntimeExecption异常;(最难被排查到问题且容易忽略)
-
注解@Transactional中Propagation属性值设置错误即Propagation.NOT_SUPPORTED(一般不会设置此种传播机制)
-
mysql关系型数据库,且存储引擎是MyISAM而非InnoDB,则事务会不起作用(隐藏最深,但基本开发中不会遇到);
2、 解决方案
2.1、非public权限修饰
When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.
使用代理时,您应该只将@Transactional注释应用于具有公共可见性的方法。如果使用@Transactional注释对受保护的、私有的或包可见的方法进行注释,则不会引发错误,但带注释的方法不会显示配置的事务设置。如果需要注释非公共方法,请考虑使用AspectJ(见下文)。
@Transactional 只能用于 public 的方法上,否则事务不会失效,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。
2.2、非Spring容器管理的bean
基于这种失效场景,有工作经验的大佬基本上是不会存在这种错误的;@Service 注解注释,StudentServiceImpl 类则不会被Spring容器管理,因此即使方法被@Transactional注解修饰,事务也亦然不会生效。
错误的例子:
import org.springframework.stereotype.Service;
@Service
public interface AreaService {
//添加地区
public int addArea(Area area);
}
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
public class AreaServiceImpl implements AreaService {
@Autowired
private AreaDao areaDao;
//此处的事务将不会生效
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public int addArea(Area area) {
areaDao.insert(area);
}
}
正确的例子:
public interface AreaService {
//添加地区
public int addArea(Area area);
}
/****************************************分隔线***********************************/
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service //Spring容器管理的bean
public class AreaServiceImpl implements AreaService {
@Autowired
private AreaDao areaDao;
//此处的事务会生效
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public int addArea(Area area) {
areaDao.insert(area);
}
}
2.3、注解修饰的方法被类内部方法调用
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AreaServiceImpl implements AreaService {
@Autowired
private AreaDao areaDao;
public int check(Area area) {
addArea(area);
}
//此处的事务不会生效
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public int addArea(Area area) {
areaDao.insert(area);
}
}
2.4、异常类型非RuntimeException
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AreaServiceImpl implements AreaService {
@Autowired
private AreaDao areaDao;
//此处的事务不会生效
@Transactional(propagation = Propagation.REQUIRED)
public int addArea(Area area) {
areaDao.insert(area);
//抛出非RuntimeException类型
throw new Exception();
}
}
解决方案:
@Transactional注解修饰的方法,加上rollbackfor属性值,指定回滚异常类型:
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
2.5、捕获异常后,却未抛出异常
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AreaServiceImpl implements AreaService {
@Autowired
private AreaDao areaDao;
//此处的事务不会生效
@Transactional(propagation = Propagation.REQUIRED)
public int addArea(Area area) {
try {
areaDao.insert(area);
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.6、事务传播行为设置异常
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AreaServiceImpl implements AreaService {
@Autowired
private AreaDao areaDao;
//此处的事务不会生效
@Transactional(propagation = Propagation.NOT_SUPPORTED,rollbackFor = Exception.class)
public int addArea(Area area) {
try {
areaDao.insert(area);
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.7、数据库存储引擎不支持事务
以MySQL关系型数据为例,如果其存储引擎设置为 MyISAM,则事务失效,因为MyISMA 引擎是不支持事务操作的;
故若要事务生效,则需要设置存储引擎为InnoDB ;目前 MySQL 从5.5.5版本开始默认存储引擎是:InnoDB;