当前位置: 首页 > 后端技术 > Java

解决:MyBatis-plus在多数据源方法上面添加了事务,数据源切换失败

时间:2023-04-01 17:29:31 Java

说明:MyBatis-plus配置了多数据源。添加事务后,数据源切换失败...1.项目中使用的场景描述对于多数据源,Impl中有一个方法:MethodA。@Service@AllArgsConstructor@DS("tableA")publicclassXXXXServiceImplextendsServiceImplimplementsXXXXService{@OverridepublicRMethodA(XXXXxxxx){}}该方法同时操作两个表:tableA,tableB(tableA,tableB来自两个数据源)。@Service@AllArgsConstructor@DS("tableA")publicclassXXXXServiceImplextendsServiceImplimplementsXXXXService{//.........@OverridepublicRMethodA(XXXXxxxx){//操作tableA的方法operate1(xxxx);//操作表B的方法operate2(xxxx);}}为了数据的一致性,楼主加了@Transactional(rollbackFor=Exception.class)@Service@AllArgsConstructor@DS("tableA")publicclassXXXXServiceImplextendsServiceImplimplementsXXXXService{//........@Override@Transactional(rollbackFor=Exception.class)publicRMethodA(XXXXxxxx){//操作tableA的方法operate1(xxxx);//操作表B的方法operate2(xxxx);}}运行项目后发现,在操作tableB的数据时,并没有切换数据源。但是去掉事务注解后,数据源切换正常。但是,无法保证数据的一致性。这个.....二、相关问题1.为什么在这个方法上面加@Transactional(rollbackFor=Exception.class)注解会导致切换数据源失败?因为在开启一个事务的同时,会从数据库连接池中获取一个数据库连接。内部服务虽然使用了@DS来切换数据源,但实际上并没有改变整个事务的连接。一个事务内的所有数据库操作都是在事务连接建立后进行的,所以会出现数据源没有切换的问题。2、如何保证数据源的正常切换和交易的正常使用?要使内部调用切换@DS起作用,必须更换数据库连接,即改变事务传播机制,产生新的事务,获取新的数据库连接。可以通过在外部方法上方添加@Transactional注解,在内部方法上方添加@Transactional(propagation=Propagation.REQUIRES_NEW)注解来解决。3、解决方法@Transactional(propagation=Propagation.REQUIRES_NEW)的意思是新建一个事务。如果有当前事务,则暂停当前事务。注意:带有该注解的方法需要在业务结束时进行处理,以确保所有挂起的交易方法都已执行成功,然后再处理开启新交易的方法。因为当内部事务方法异常时,会导致外部事务回滚。但是外层事务异常不会回滚内层事务方法。以我们的例子为例,在MethodA上面添加@Transactional(rollbackFor=Exception.class)注解,内部调用操作tableB的方法operate2();在上面添加@Transactional(propagation=Propagation.REQUIRES_NEW)注解。@Service@AllArgsConstructor@DS("tableB")publicclassXXXXServiceImplextendsServiceImplimplementsXXXXService{//.........@Override@Transactional(propagation=Propagation.REQUIRES_NEW)publicBooleanoperate2()复制代码(XXXXxxxx){返回保存(xxxx);}}如果定义先调用operate2(),则再调用operate1()。如果operate1()方法异常,operate2()方法不会回滚。@Service@AllArgsConstructor@DS("tableA")publicclassXXXXServiceImplextendsServiceImplimplementsXXXXService{//.........@Override@Transactional(rollbackFor=Exception.class)publicRMethodA(XXXXxxxx){//操作表B的方法operate2(xxxx);//操作tableA的方法operate1(xxxx);}}如果定义先调用operate1(),再调用operate2()。如果方法调用出现异常,则回滚。@Service@AllArgsConstructor@DS("tableA")publicclassXXXXServiceImplextendsServiceImplimplementsXXXXService{//.........@Override@Transactional(rollbackFor=Exception.class)publicRMethodA(XXXXxxxx){//操作tableA的方法operate1(xxxx);//操作表B的方法operate2(xxxx);因此需要注意的是,配置了@Transactional(propagation=Propagation.REQUIRES_NEW)注解的方法调用,应该在业务的最后处理。