我使用AbstractRoutingDataSource
动态更改数据源,并使用ThreadLocal
设置currentLookupKey。当每个http请求只使用一个数据源时,它工作得很好。我用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控制器中获取数据时,我只从一个数据库获取数据。
我的REST控制器中的一些代码
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请求中只获得一次数据源。
此方法仅调用一次。
@Override
public Connection getConnection() throws SQLException {
return determineTargetDataSource().getConnection();
}
有没有办法强制Spring更改数据源。
5条答案
按热度按时间2w3kk1z51#
您的问题可能与事务定界有关。
当您在代码中定义
@Transactional
注解时,Spring将代表您创建开始和结束事务所需的所有内容,并在需要时提交或回滚事务。正如你在
DataSourceTransactionManager
类的source code中的doBegin
方法中看到的--这同样适用于其他事务管理器--Spring在初始化事务时获得一个Connection
--这就是为什么方法getConnection
只被调用一次--并且它将在该事务中对数据库的所有底层操作中重用该连接(这对于ACID保存是有意义的)。因此,如果你需要在同一个请求处理中连接到多个数据源,你可以在服务代码中定义不同的方法,每个方法都用
@Transactional
注解,并在调用它们之前根据需要更改底层数据源:lb3vh1jj2#
@Transactional
注解注解的方法。在调用该事务性方法之前,首先指定一个数据源键,然后调用事务性方法。在transactional方法中,您首先调用repository,它会按照您设置的datasource查找键的预期工作。但是,您在事务方法中设置了不同的键,并调用了另一个存储库,它仍然使用您第一次设置的键。transaction
启动时,DataSource
将被框架选择,所以**如果你使用的是@Transactional
注解,无论你在方法内部做什么切换都是无用的。**因为数据源将由为@Transactional
注解创建的代理选择。最好的选择是在非事务性服务中使用分支逻辑,或者使用TransactionTemplate
而不是@Transactional
YourRestController
没有类级别@Transactional
,并且在这个yourRestControllerMethod
中没有@Transactional
注解,您将把它们保留到您的服务中。hts6caw33#
我遇到了同样的问题,上面的解决方案都不能解决。. but making my Service methodfinal(在我的REST控制器中)
ca1c2owp4#
将
spring.jpa.open-in-view
设置为false
。pqwbnv8z5#
在我的例子中,我遵循了jccampanero的suggestion,它工作了:将服务类中的两个方法分开,每个方法都有一个与不同数据源的数据库连接,通过
AbstractRoutingDataSource
切换数据源。我认为这里的关键点是数据库配置,我们将
AbstractRoutingDataSource
指定为EntityManagerFactory
和TransactionManager
,而不是正常的DataSource
。和DatabaseConfiguration:
通过上面的配置,JPA Repositories将使用
entityManagerFactory
bean来获取DB连接(在每个服务方法中,Spring将调用RoutingDataSource
中的方法determineCurrentLookupKey
来获取我们之前指定的数据源)。