多数据源切换踩到的坑。

TransactionSynchronizationManager

由于项目当中需要用到多个数据源,所以想到了使用spring自带的AbstractRoutingDataSource数据源路由。 使用方法百度上一大把。

大概流程就是使用spring的aop切面在请求进来之前设置一个数据源标示到线程变量当中,在AbstractRoutingDataSource.determineCurrentLookupKey方法当中返回前面放入线程变量的值,通过这个值去取对应的数据源。

出现问题

但是真正用起来以后发现数据源并没有切换,一直是使用的默认数据源。发觉很奇怪,一开始以为是线程变量上出了问题,但是通过debug,sysout发现线程并没有被切换

发现问题

最后没办法,只有通过debug跟踪spring源码才发现TransactionSynchronizationManager这个类当中有一个本地线程变量会放置connection,因为spring在一个事务当中会缓存当前数据源的链接。

private static final ThreadLocal> resources =
			new NamedThreadLocal>("Transactional resources");

DataSourceUtils.doGetConnection

/**
	 * Actually obtain a JDBC Connection from the given DataSource.
	 * Same as {@link #getConnection}, but throwing the original SQLException.
	 * 

Is aware of a corresponding Connection bound to the current thread, for example * when using {@link DataSourceTransactionManager}. Will bind a Connection to the thread * if transaction synchronization is active (e.g. if in a JTA transaction). *

Directly accessed by {@link TransactionAwareDataSourceProxy}. * @param dataSource the DataSource to obtain Connections from * @return a JDBC Connection from the given DataSource * @throws SQLException if thrown by JDBC methods * @see #doReleaseConnection */ public static Connection doGetConnection(DataSource dataSource) throws SQLException { Assert.notNull(dataSource, "No DataSource specified"); ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource); if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) { conHolder.requested(); if (!conHolder.hasConnection()) { logger.debug("Fetching resumed JDBC Connection from DataSource"); conHolder.setConnection(dataSource.getConnection()); } return conHolder.getConnection(); } // Else we either got no holder or an empty thread-bound holder here. logger.debug("Fetching JDBC Connection from DataSource"); Connection con = dataSource.getConnection(); if (TransactionSynchronizationManager.isSynchronizationActive()) { logger.debug("Registering transaction synchronization for JDBC Connection"); // Use same Connection for further JDBC actions within the transaction. // Thread-bound object will get removed by synchronization at transaction completion. ConnectionHolder holderToUse = conHolder; if (holderToUse == null) { holderToUse = new ConnectionHolder(con); } else { holderToUse.setConnection(con); } holderToUse.requested(); TransactionSynchronizationManager.registerSynchronization( new ConnectionSynchronization(holderToUse, dataSource)); holderToUse.setSynchronizedWithTransaction(true); if (holderToUse != conHolder) { TransactionSynchronizationManager.bindResource(dataSource, holderToUse); } } return con; }

以上是发现问题的过程 记录一下,以免以后遇到类似的问题忘记了

获取connection的入口在DefaultSqlSession

此问题只会出现在事务管理的请求中,如果没有事务,不会出现问题。

public Connection getConnection() {
    try {
      return executor.getTransaction().getConnection();
    } catch (SQLException e) {
      throw ExceptionFactory.wrapException("Error getting a new connection.  Cause: " + e, e);
    }
  }

最后我的解决方式是配两个数据源,sqlsession,通过不同的包名去区分

这个问题应该还有另外的解决办法,比如通过mybaits去管理事务,而不使用spring管理事务,应该就可以解决,不过因为我们的业务需求是两个库相互之间没有什么关联,所以就没必要这么麻烦,而直接采用了简单粗暴的方式去解决这个问题,如果有需要的话,可以试试上述方式。

转载于:https://my.oschina.net/u/2415799/blog/865522

你可能感兴趣的:(java)