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

网友投稿 1055 2022-09-29


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

目录@Transaction注解导致动态切换更改数据库失效使用场景遇到问题解决@Transactional失效的场景及原理1.@Transactional修饰的方法2.在类内部没有添加@Transactional的方法3.就是在@Transactional方法内部捕获了异常

@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方法时,当你调用是,他也不会回滚

测试代码如下

@Service

public class UserServiceImpl extends BaseServiceImpl 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代码块里面重新抛出异常,事务也不会回滚。

代码如下:

@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的方法里面捕获了异常,必须要手动回滚,

代码如下:

@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方法

TransactionAspectSupport

@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里面没有抛出新的异常,那么事务将不会回滚。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:网站代码安全审计之人工审计强势(源代码安全审计工具)
下一篇:app渗透测试服务之人工审计的优点
相关文章

 发表评论

暂时没有评论,来抢沙发吧~