好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

解决@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 测试数据pleteTransactionAfterThrowing(txInfo, var17);

                 throw var17;

             } finally {

                 this .cleanupTransactionInfo(txInfo);

             }

             this 测试数据mitTransactionAfterReturning(txInfo);

             return result;

         }

     }

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

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

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

查看更多关于解决@Transaction注解导致动态切换更改数据库失效问题的详细内容...

  阅读:42次