TransactionSynchronizationManager
由于项目当中需要用到多个数据源,所以想到了使用spring自带的AbstractRoutingDataSource数据源路由。 使用方法百度上一大把。
大概流程就是使用spring的aop切面在请求进来之前设置一个数据源标示到线程变量当中,在AbstractRoutingDataSource.determineCurrentLookupKey方法当中返回前面放入线程变量的值,通过这个值去取对应的数据源。
出现问题
但是真正用起来以后发现数据源并没有切换,一直是使用的默认数据源。发觉很奇怪,一开始以为是线程变量上出了问题,但是通过debug,sysout发现线程并没有被切换
发现问题
最后没办法,只有通过debug跟踪spring源码才发现TransactionSynchronizationManager这个类当中有一个本地线程变量会放置connection,因为spring在一个事务当中会缓存当前数据源的链接。
private static final ThreadLocal
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管理事务,应该就可以解决,不过因为我们的业务需求是两个库相互之间没有什么关联,所以就没必要这么麻烦,而直接采用了简单粗暴的方式去解决这个问题,如果有需要的话,可以试试上述方式。