gpt4 book ai didi

解决@Transaction注解导致动态切换更改数据库失效问题

转载 作者:qq735679552 更新时间:2022-09-27 22:32:09 35 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章解决@Transaction注解导致动态切换更改数据库失效问题由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

@Transaction注解导致动态切换更改数据库失效

使用场景

  • 给所有的Controller方法上加切点
  • 在@Before注解的方法里,根据http请求中携带的header,动态切换数据源
  • 使用mybatis或者jpa执行操作

遇到问题

当给Controller方法加上@Transaction注解后,动态切换数据源就失效了,原因是每次@Before注解的方法运行之前,protected abstract Object determineCurrentLookupKey();就已经运行了,而这个方法是切换数据源的关键.

解决

其实也算不上解决,就是不要在Controller方法上加事务注解,非要加事务,中间的Service层就不要省了.

@Transactional失效的场景及原理

1.@Transactional修饰的方法

为非public方法,这个时候@Transactional会实现.

失败的原理是:@Transactional是基于动态代理来实现的,非public的方法,他@Transactional的动态代理对象信息为空,所以不能回滚.

2.在类内部没有添加@Transactional的方法

调用了@Transactional方法时,当你调用是,他也不会回滚 。

测试代码如下 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class UserServiceImpl extends BaseServiceImpl<UserEntity> implements UserService {
     @Autowired
     private UserMapper userMapper;
     @Override
     @Transactional
     public void insertOne() {
         UserEntity userEntity = new UserEntity();
         userEntity.setUsername( "Michael_C_2019" );
         //插入到数据库
         userMapper.insertSelective(userEntity);
         //手动抛出异常
         throw new IndexOutOfBoundsException();
     }
     @Override
     public void saveOne() {
         insertOne();
     }
}

失败的原理:@Transactional是基于动态代理对象来实现的,而在类内部的方法的调用是通过this关键字来实现的,没有经过动态代理对象,所以事务回滚失效.

3.就是在@Transactional方法内部捕获了异常

没有在catch代码块里面重新抛出异常,事务也不会回滚.

代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
     @Transactional
     public void insertOne() {
         try {
             UserEntity userEntity = new UserEntity();
             userEntity.setUsername( "Michael_C_2019" );
             //插入到数据库
             userMapper.insertSelective(userEntity);
             //手动抛出异常
             throw new IndexOutOfBoundsException();
         } catch (IndexOutOfBoundsException e) {
             e.printStackTrace();
         }
     }

所以在阿里巴巴的Java开发者手册里面有明确规定,在 @Transactional的方法里面捕获了异常,必须要手动回滚, 。

代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
    @Transactional
    public void insertOne() {
        try {
            UserEntity userEntity = new UserEntity();
            userEntity.setUsername( "Michael_C_2019" );
            //插入到数据库
            userMapper.insertSelective(userEntity);
            //手动抛出异常
            throw new IndexOutOfBoundsException();
        } catch (IndexOutOfBoundsException e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }

失败原理:这时候我们来看看spring的源码:

TransactionAspectSupport类里面的invokeWithinTransaction方法 。

?
1
TransactionAspectSupport
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
@Nullable
     protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, TransactionAspectSupport.InvocationCallback invocation) throws Throwable {
         TransactionAttributeSource tas = this .getTransactionAttributeSource();
         TransactionAttribute txAttr = tas != null ? tas.getTransactionAttribute(method, targetClass) : null ;
         PlatformTransactionManager tm = this .determineTransactionManager(txAttr);
         String joinpointIdentification = this .methodIdentification(method, targetClass, txAttr);
         Object result;
         if (txAttr != null && tm instanceof CallbackPreferringPlatformTransactionManager) {
             TransactionAspectSupport.ThrowableHolder throwableHolder = new TransactionAspectSupport.ThrowableHolder( null );
             try {
                 result = ((CallbackPreferringPlatformTransactionManager)tm).execute(txAttr, (status) -> {
                     TransactionAspectSupport.TransactionInfo txInfo = this .prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
                     Object var9;
                     try {
                         Object var8 = invocation.proceedWithInvocation();
                         return var8;
                     } catch (Throwable var13) {
                         if (txAttr.rollbackOn(var13)) {
                             if (var13 instanceof RuntimeException) {
                                 throw (RuntimeException)var13;
                             }
                             throw new TransactionAspectSupport.ThrowableHolderException(var13);
                         }
                         throwableHolder.throwable = var13;
                         var9 = null ;
                     } finally {
                         this .cleanupTransactionInfo(txInfo);
                     }
                     return var9;
                 });
                 if (throwableHolder.throwable != null ) {
                     throw throwableHolder.throwable;
                 } else {
                     return result;
                 }
             } catch (TransactionAspectSupport.ThrowableHolderException var19) {
                 throw var19.getCause();
             } catch (TransactionSystemException var20) {
                 if (throwableHolder.throwable != null ) {
                     this .logger.error( "Application exception overridden by commit exception" , throwableHolder.throwable);
                     var20.initApplicationException(throwableHolder.throwable);
                 }
                 throw var20;
             } catch (Throwable var21) {
                 if (throwableHolder.throwable != null ) {
                     this .logger.error( "Application exception overridden by commit exception" , throwableHolder.throwable);
                 }
                 throw var21;
             }
         } else {
             TransactionAspectSupport.TransactionInfo txInfo = this .createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
             result = null ;
             try {
                 result = invocation.proceedWithInvocation();
             } catch (Throwable var17) {
               //异常时,在catch逻辑中回滚事务
                 this .completeTransactionAfterThrowing(txInfo, var17);
                 throw var17;
             } finally {
                 this .cleanupTransactionInfo(txInfo);
             }
             this .commitTransactionAfterReturning(txInfo);
             return result;
         }
     }

他是通过捕获异常然后在catch里面进行事务的回滚的,所以如果你在自己的方法里面catch了异常,catch里面没有抛出新的异常,那么事务将不会回滚.

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我.

原文链接:https://blog.csdn.net/m0_37659871/article/details/81672373 。

最后此篇关于解决@Transaction注解导致动态切换更改数据库失效问题的文章就讲到这里了,如果你想了解更多关于解决@Transaction注解导致动态切换更改数据库失效问题的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

35 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com