一、未指定回滾異常
二逻族、異常被捕獲
三蜻底、方法內(nèi)部直接調(diào)用
四、方法被private或者final修飾
五聘鳞、使用了錯(cuò)誤的事務(wù)傳播機(jī)制
六薄辅、當(dāng)前類沒有被Spring容器托管
七、數(shù)據(jù)庫不支持事務(wù)
一抠璃、未指定回滾異常
@Transactional注解默認(rèn)的回滾異常類型是運(yùn)行時(shí)異常(RuntimeException)站楚,如果我們自定義了一個(gè)異常直接繼承了Exception,例如:
public class CustomException extends Exception
如果@Transactional未指定異常類型搏嗡,當(dāng)程序中拋出CustomException異常則不會(huì)回滾,例如:
@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);
}
}
結(jié)果如下:
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]
雖然程序當(dāng)中拋出了異常采盒,但是數(shù)據(jù)庫還是成功入庫了旧乞。
此時(shí)我們需要在@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捕獲時(shí)尺栖,事務(wù)也會(huì)失效:
@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){
}
}
所以我們需要主動(dòng)將此異常拋出: 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包裹的代碼烦租,以此來達(dá)到回滾的目的延赌。
@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();
}
}
三、方法內(nèi)部直接調(diào)用
在Spring的Aop代理下叉橱,只有目標(biāo)方法在外部進(jìn)行調(diào)用挫以,目標(biāo)方法才會(huì)由Spring生成的代理對象來進(jìn)行管理,如果是其他不包含@Transactional注解的方法中調(diào)用包含@Transactional注解的方法時(shí)候赏迟,有@Transactional注解的方法的事務(wù)會(huì)被忽略屡贺,則不會(huì)發(fā)生回滾。
@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();
}
}
修改方式,把當(dāng)前類自己注入一下調(diào)用即可甩栈。
@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 后泻仙,把當(dāng)前類自己注入 會(huì)報(bào)循環(huán)引用問題 ×棵唬可添加配置打破循環(huán) 玉转。
spring:
main:
allow-circular-references: true
四、方法被private修飾
此種情況下殴蹄,事務(wù)也是會(huì)失效的究抓。
@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];
}
五、使用了錯(cuò)誤的事務(wù)傳播機(jī)制
六袭灯、當(dāng)前類沒有被Spring容器托管
七刺下、數(shù)據(jù)庫不支持事務(wù)
文章持續(xù)更新中、希望對各位有所幫助稽荧、有問題可留言 大家共同學(xué)習(xí).