AbstractRoutingDataSource不会更改连接 [英] AbstractRoutingDataSource doesn't change connection
问题描述
我使用 AbstractRoutingDataSource
动态更改数据源,并使用 ThreadLocal
设置currentLookupKey。当每个http请求仅使用一个数据源时,它的效果很好。我使用 JpaRepository
I use AbstractRoutingDataSource
to change data source dynamically and ThreadLocal
to set up currentLookupKey. It works nice when I use only one data source per http request. I use JpaRepository
@Component
@Primary
public class RoutingDataSource extends AbstractRoutingDataSource {
@Autowired
private DatabaseMap databaseMap;
@Override
public void afterPropertiesSet() {
setTargetDataSources(databaseMap.getSourcesMap());
setDefaultTargetDataSource(databaseMap.getSourcesMap().get("DEFAULT"));
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey() {
return DatabaseContextHolder.getDatabaseType();
}
}
public class DatabaseContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static void setDatabaseType(String string) {
contextHolder.set(string);
}
public static String getDatabaseType() {
return (String) contextHolder.get();
}
public static void clearDatabaseType() {
contextHolder.remove();
}
}
当我尝试在REST控制器中获取数据时,
When I try to get data in my REST controller I get data only from one database.
REST控制器中的某些代码
Some code in my REST controller
DatabaseContextHolder.setDatabaseType("db1");
//here I get data from db1 as expected
//I use JpaRepository
DatabaseContextHolder.clearDatabaseType();
DatabaseContextHolder.setDatabaseType("db2");
//here I should get data from db2 but get from db1
我尝试调试并且看起来Spring在http请求中仅获取一次数据源。
I tried to debug and it looks like Spring obtains data source only once in http request.
此方法仅调用一次。
@Override
public Connection getConnection() throws SQLException {
return determineTargetDataSource().getConnection();
}
有什么方法可以迫使Spring更改数据源。
Is there any way to force Spring to change data source.
推荐答案
您的问题可能与事务定界有关。
Your problem could be related with transaction delimitation.
当您定义 @Transactional
批注中,Spring会代表您创建开始和结束以及提交或回滚(如果需要)事务所需的所有东西。
When you define a @Transactional
annotation in your code, Spring will create on your behalf all the stuff necessary to begin and end, and commiting or rollback if required, a transaction.
如您在 DataSourceTransactionManager
的-jdbc / src / main / java / org / springframework / jdbc / datasource / DataSourceTransactionManager.java rel = nofollow noreferrer>源代码类-其他事务管理器也是如此-初始化事务时,Spring获得 Connection
-这就是为什么方法 getConnection
仅被调用一次-,并且w不适用于该事务中针对数据库的所有基础操作之间的连接重用(对于ACID保留来说很有意义)。
As you can see in the doBegin
method in the source code of the DataSourceTransactionManager
class - the same applies to other transaction managers - , Spring obtains a Connection
when the transaction is initialized - this is why the method getConnection
is invoked only once - , and it will reuse that connection across all the underlying operations against the database within that transaction (it makes sense for ACID preservation).
因此,如果您需要在同一请求中连接到多个数据源在处理过程中,您可以在服务代码中定义不同的方法,每个方法都带有 @Transactional
批注,并在调用它们之前根据需要更改基础数据源:
So, if you need to connect to several data sources within the same request processing, you can define different methods in you service code, every one annotated with a @Transactional
annotation, and change the underlying data source as you require before invoke them:
DatabaseContextHolder.setDatabaseType("db1");
// Invoke a service method annotated with @Transactional
// It can use the underlying JpaRepositories that you need
DatabaseContextHolder.clearDatabaseType();
DatabaseContextHolder.setDatabaseType("db2");
// Invoke again another (or the same, what you need) service method
// annotated with @Transactional, You should get data from db2 this time
这篇关于AbstractRoutingDataSource不会更改连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!