一、未指定回滚异常
二、异常被捕获
三、方法内部直接调用
四、方法被private或者final修饰
五、使用了错误的事务传播机制
六、当前类没有被Spring容器托管
七、数据库不支持事务
一、未指定回滚异常
@Transactional注解默认的回滚异常类型是运行时异常(RuntimeException),如果我们自定义了一个异常直接继承了Exception,例如:
public class CustomException extends Exception
如果@Transactional未指定异常类型,当程序中抛出CustomException异常则不会回滚,例如:
@Transactional
public void save(BaseDTO dto) throws CustomException{
try {
BasePO po = new BasePO();
BeanUtil.copyProperties(dto, po,true);
int insert = baseTestMapper.insert(po);
String[] a = new String[]{"1","2"};
String s = a[3];
}catch (Exception e){
throw new CustomException(CustomErrorEnum.FAIL);
}
}
结果如下:
2024-08-26 10:48:03.306 INFO 4771 --- [nio-8081-exec-6] o.a.d.remoting.transport.AbstractClient : [DUBBO] Start NettyClient /10.1.186.51 connect to the server /10.1.186.51:20880, dubbo version: 2.7.15, current host: 10.1.186.51
2024-08-26 10:48:03.936 ERROR 4771 --- [nio-8081-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is CustomException(code=50000, msg=请求失败!, data=null)] with root cause
exception.CustomException: 请求失败!
at com.base.dubboservice.iservice.IBaseServiceImpl.save(IBaseServiceImpl.java:49) ~[na:na]
at com.base.dubboservice.iservice.IBaseServiceImpl$$FastClassBySpringCGLIB$$652c9b76.invoke(<generated>) ~[na:na]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.25.jar:5.3.25]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.25.jar:5.3.25]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.25.jar:5.3.25]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.25.jar:5.3.25]
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[na:na]
虽然程序当中抛出了异常,但是数据库还是成功入库了。
此时我们需要在@Transactional指定回滚异常的类型,遇到指定类型异常就要回滚:@Transactional(rollbackFor = CustomException.class)
@Override
@Transactional(rollbackFor = CustomException.class)
public void save(BaseDTO dto) throws CustomException{
try {
BasePO po = new BasePO();
BeanUtil.copyProperties(dto, po,true);
int insert = baseTestMapper.insert(po);
String[] a = new String[]{"1","2"};
String s = a[3];
}catch (Exception e){
throw new CustomException(CustomErrorEnum.FAIL);
}
}
二、异常被捕获
异常被try-catch捕获时,事务也会失效:
@Override
@Transactional
public void save(BaseDTO dto) throws CustomException{
try {
BasePO po = new BasePO();
BeanUtil.copyProperties(dto, po,true);
int insert = baseTestMapper.insert(po);
String[] a = new String[]{"1","2"};
String s = a[3];
}catch (Exception e){
}
}
所以我们需要主动将此异常抛出: throws CustomException。
@Override
@Transactional(rollbackFor = CustomException.class)
public void save(BaseDTO dto) throws CustomException{
try {
BasePO po = new BasePO();
BeanUtil.copyProperties(dto, po,true);
int insert = baseTestMapper.insert(po);
String[] a = new String[]{"1","2"};
String s = a[3];
}catch (Exception e){
throw new CustomException(CustomErrorEnum.FAIL);
}
}
或者我们也可以修改catch包裹的代码,以此来达到回滚的目的。
@Override
@Transactional
public void save(BaseDTO dto){
try {
BasePO po = new BasePO();
BeanUtil.copyProperties(dto, po,true);
int insert = baseTestMapper.insert(po);
String[] a = new String[]{"1","2"};
String s = a[3];
}catch (Exception e){
e.printStackTrace();
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
三、方法内部直接调用
在Spring的Aop代理下,只有目标方法在外部进行调用,目标方法才会由Spring生成的代理对象来进行管理,如果是其他不包含@Transactional注解的方法中调用包含@Transactional注解的方法时候,有@Transactional注解的方法的事务会被忽略,则不会发生回滚。
@Override
public void save(BaseDTO dto){
saveTransactional(dto);
}
@Transactional
public void saveTransactional(BaseDTO dto){
try {
BasePO po = new BasePO();
BeanUtil.copyProperties(dto, po,true);
int insert = baseTestMapper.insert(po);
String[] a = new String[]{"1","2"};
String s = a[3];
}catch (Exception e){
e.printStackTrace();
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
修改方式,把当前类自己注入一下调用即可。
@Autowired
IBaseServiceImpl iBaseService;
@Override
public void save(BaseDTO dto){
iBaseService.saveTransactional(dto);
}
@Transactional
public void saveTransactional(BaseDTO dto){
try {
BasePO po = new BasePO();
BeanUtil.copyProperties(dto, po,true);
int insert = baseTestMapper.insert(po);
String[] a = new String[]{"1","2"};
String s = a[3];
}catch (Exception e){
e.printStackTrace();
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
Spring Boot 2.6 后,把当前类自己注入 会报循环引用问题 。可添加配置打破循环 。
spring:
main:
allow-circular-references: true
四、方法被private修饰
此种情况下,事务也是会失效的。
@Transactional
private void saveTransactional(BaseDTO dto){
asePO po = new BasePO();
BeanUtil.copyProperties(dto, po,true);
int insert = baseTestMapper.insert(po);
dto.setTranName("999");
String[] a = new String[]{"1","2"};
String s = a[3];
}
五、使用了错误的事务传播机制
六、当前类没有被Spring容器托管
七、数据库不支持事务
文章持续更新中、希望对各位有所帮助、有问题可留言 大家共同学习.