多数据源切换失败问题解决
@Transactional注解(事务)会导致多数据源切换失败!
在一个方法中,有多个SQL语句执行,我们习惯性的会加上@Transactional注解,但是!这只局限于这多个SQL语句(不论增删改查),都只访问一个库!(大坑!)
例如:
@Transactional
public void test(){
mysqlMapperOne.insert(....);
//此处为切换数据源的操作。。。。省略
mysqlMapperTwo.insert(.....);
}
这个会导致mysqlMapperTwo的SQL语句被映射到第一个SQL语句的库中。导致报错,在第一个数据源中找不到第二个数据源的表。。。。这个问题我一直琢磨了大半天。。。
原因:
在org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin 这个类的源代码中找到了答案:
@Override
2 protected void doBegin(Object transaction, TransactionDefinition definition) {
3 DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
4 Connection con = null;
5
6 try {
7 if (txObject.getConnectionHolder() == null ||
8 txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
9 Connection newCon = this.dataSource.getConnection();
10 if (logger.isDebugEnabled()) {
11 logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
12 }
13 txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
14 }
15
16 txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
17 con = txObject.getConnectionHolder().getConnection();
18
19 Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
20 txObject.setPreviousIsolationLevel(previousIsolationLevel);
21
22 // Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
23 // so we don't want to do it unnecessarily (for example if we've explicitly
24 // configured the connection pool to set it already).
25 if (con.getAutoCommit()) {
26 txObject.setMustRestoreAutoCommit(true);
27 if (logger.isDebugEnabled()) {
28 logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
29 }
30 con.setAutoCommit(false);
31 }
32
33 prepareTransactionalConnection(con, definition);
34 txObject.getConnectionHolder().setTransactionActive(true);
35
36 int timeout = determineTimeout(definition);
37 if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
38 txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
39 }
40
41 // Bind the connection holder to the thread.
42 if (txObject.isNewConnectionHolder()) {
43 TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
44 }
45 }
46
47 catch (Throwable ex) {
48 if (txObject.isNewConnectionHolder()) {
49 DataSourceUtils.releaseConnection(con, this.dataSource);
50 txObject.setConnectionHolder(null, false);
51 }
52 throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
53 }
54 }
第7-16行,在开始一个事务前,如果当前上下文的连接对象为空,获取一个连接对象,然后保存起来,下次doBegin再调用时,就直接用这个连接了,根本不做任何切换(类似于缓存命中!),说白了,就是 “他” 发现你之前调用过连接,他就不管另一个了
在方法被调用前,已经和另一个数据库发生了关系,所以他就直接从这个里面拿了,所以第二个他就无视了。
解决办法:把事务删了!!或者,先切到主库上来,这样后面再调用有事务的方法时,就仍然保持在主库的连接上。
原文链接:https://yq.aliyun.com/articles/250654
感谢作者:杨俊明
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/5677.html