多数据源和事务

2018-12-17 18:43:55 +08:00
 choice4
各位在用 mybatis 的时候,如果碰到多数据源的时候是不是使用 aop 的方式做动态多数据源呢? 如果是的话一般切入点是什么?我是在网上看了一些,主要是两部分。有的是通过 MapperScan 分包。为每个 DataSource 都去做单独的 SqlSessionFactory 以及 TransactionManager 等。 还有一种就是从 AbstractRoutingDataSource 上下功夫,大部分都起名字叫 DynamicDataSource 这种。然后将每个数据源放入 map,set 到这个 bean 里面。然后定义 SQLSessionFactory 将这个类 set 进去。其中看着比较舒服的是做个注解。然后切入点直接走注解。但是我写下来之后,增删改查的确是按照预期分开了。但是由于切入点是在 dao 层。事务的话 @Transactional 一般都会写在 service 层。 这个时候就会发生设置数据源发生在设置事务之后然后就凉了(这个结论正确性不敢打保票,看了一些文章暂下的结论)。特来取经。。问下多数据源都是怎么弄的。 其中还看到一个 AtomikosDataSource 还没有细琢磨。暂时是默认的 HikariDataSource。 springboot 2
3676 次点击
所在节点    Java
15 条回复
abcbuzhiming
2018-12-17 23:51:52 +08:00
多数据源时的事务一致性属于跨库事务,跨库事务非常麻烦而且绝对不是你描述的这种结构能摆平的,一般的注解式事务只能针对单库,所以你用法就是错的,如果你再 service 层用了 @Transactional,那你就要保证这个 service 里调用的 mapper 都是一个库的,否则的话,就不要在 service 上用 @Transactional
helloZwq
2018-12-18 09:15:56 +08:00
同楼主,用的 AbstractRoutingDataSource,注解+切面 切换数据源
choice4
2018-12-18 10:15:22 +08:00
@abcbuzhiming
/*
如果你再 service 层用了 @Transactional,那你就要保证这个 service 里调用的 mapper 都是一个库的
*/
我是在 service 层用了 @Transactional, 同时我在测试的时候也保证了这个 service 中的 mapper 都是一个库的,并且手动抛出了异常。 但是不行。因为是根据 dao 层方法上的注解去选择数据源。但是事务的开启是在 service 层, 但是开启一个事务又需要有确定的数据源。由于现在数据源还没有确定下来,也就是 AbstractRoutingDataSource 里面那个 determinkey 方法还没有执行,动态数据源还没有确定下来,(即开启事务的 aop 先于动态选择数据源的 aop 执行了)就出现了如下错误:
Could not open JDBC Connection for transaction; nested exception is java.lang.IllegalStateException: Cannot determine target DataSource for lookup key [null]
就是这样了。不知道有没有说明白
choice4
2018-12-18 10:15:52 +08:00
@helloZwq 事务呢?怎么加的
abcbuzhiming
2018-12-18 10:47:57 +08:00
@choice4 我明白了,你的事务管理器不止一个,那你必须明确的在 service 层的 @Transactional 里指定特定的事务管理器,否则就出错
choice4
2018-12-18 11:15:00 +08:00
@abcbuzhiming 对 最开始想弄成一个事务管理器。大概如这中 retrun new DataSourceManager(dynamicDataSource)。
然后就会出现我在 #3 出现的那种错误。
后来我干脆就创建两个 DataSourceManager 的 bean。 比如分别指定 beanName 为 A, B。
然后在 service 层比如我用到 ADao 类。 就使用 @Transactional(transactionManager="A", rollbackFor=xxx.class)
并在 service 层抛出相应异常,但是不能回滚。我 debug 跟断点是
TransactionInterceptor.invoke(MethodInvocation) ---->
TransactionAspectJSupport.invokeWithinTransaction(Method, Class, final InvocationCallback) ----> TransactionAspectJSupport.completeTransactionAfterThrowing(TransactionInfo, Throwable)
看到了代码执行了
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
这一行 。并且是正常执行(这行没有出现异常)。但是事务就是没有回滚,想不明白为什么。难道我插入数据时使用的连接,和抛出异常事务回滚时的连接不一样?
choice4
2018-12-18 11:16:10 +08:00
楼上应该是 DataSourceTransactionManager 不是 DataSourceManager。 打错了
abcbuzhiming
2018-12-18 13:10:58 +08:00
@choice4 不回滚?你确定你的数据库事务设置没问题吗?变成单数据库,单事务管理器,同样的 serviceImpl 代码看是否回滚
choice4
2018-12-18 13:34:40 +08:00
@abcbuzhiming 确定,不过不知道是不是别的哪里写错了 大致代码意思如下吧

@Configuration
class Config{
@Bean("a")
@PropertiesConfiguration("...")
public DataSource a() {return new DataSource;}

@Bean("b")
@PropertiesConfiguration("...")
public DataSource a() {return new DataSource;}

@Bean("dynamicDS")
public DataSource dataSource() {new DynamicDataSource(); setMap(asMap(a(), b())); return;}

@Bean
public SqlSessionFactory fac() {return new SqlSessionFactoryBean(dataSource()).getObject();}

@Bean("at")
public TransactionManager ma() {
return new TransactionManager(a());
// return new TransactionManager(dataSource()); //这种就会出现我在 #3 写的那种情况
}
}

@Service
class Service {
@Autowired
ADao aDao;
@Transactional(transaction="at", rollbackFor=RuntimeException.class)
public void insert() {
aDao.insert();
throw new RuntimeException();
}
}

@Mapper
interface aDao{
@DataSource("a") //自定义注解,aop 会动态选择 a 数据源
@Insert("......")
int insert();
}

@SpringbootTest
class Test() {
@Autowired
Service aService;

@Junit.Test
void test() {
aService.insert();
}
}

大概就是这个意思的代码吧。
choice4
2018-12-18 13:34:58 +08:00
空格被吃了
abcbuzhiming
2018-12-18 16:52:16 +08:00
@choice4 感觉没写错,你单数据库,单事务管理器,确认一下是否能回滚
choice4
2018-12-18 17:36:41 +08:00
@abcbuzhiming 单数据源可以回滚, (我是直接是在创建 SqlSessionFactory bean 的时候 bean.setDataSource(a()))
这种方式设置的 (成功回滚)。
原来动态多数据源的时候相当于是 bean.setDataSource(dynamicDataSource()) (无法回滚)
afsun
2019-10-29 10:41:49 +08:00
@choice4 请问后面解决了吗
choice4
2019-10-29 12:43:21 +08:00
@afsun 当时最后应该是 mapperscan 分包了,没用动态数据源,同时事务里不要有跨库的数据源 ,这个做不了分布式事务
afsun
2019-12-19 13:50:23 +08:00
@choice4 谢谢。用 jta 实现了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/518365

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX