AbstractRoutingDataSource不会更改连接

mar*_*108 5 java spring datasource

我用来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();
    }
}
Run Code Online (Sandbox Code Playgroud)

当我尝试在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
Run Code Online (Sandbox Code Playgroud)

我尝试调试,看起来Spring在http请求中仅获得一次数据源。

此方法仅被调用一次。

@Override
public Connection getConnection() throws SQLException {
    return determineTargetDataSource().getConnection();
}
Run Code Online (Sandbox Code Playgroud)

有什么办法可以迫使Spring改变数据源。

Kav*_*lai 0

  • 我怀疑你有一个用@Transactional注释注释的方法。在调用该事务方法之前,您首先指定一个数据源键,然后调用事务方法。在事务方法内,您首先调用存储库,它会按照您设置的数据源查找键按预期工作。但是,然后您在事务方法内设置不同的密钥,并调用另一个存储库,它仍然使用您第一次设置的密钥。

  • DataSource将在启动时由框架选择transaction,因此如果您使用@Transactional注释,那么您在方法内所做的任何切换都是无用的。因为数据源是由为注释创建的代理选择的@Transactional。最好的选择是在非事务性服务中使用分支逻辑或TransactionTemplate使用@Transactional

  • 例如,确保this 中YourRestController没有类级别@Transactional以及没有注释,您将保留它们为您服务。@TransactionalyourRestControllerMethod

     @RestController
     public class YourRestController {

      @Autowired
      TransactionalService transactional

      public void yourRestControllerMethod(){
        //set the datasource look up key to A1 and then
        transactional.methodA1();
        //change datasource look up key to A2 and then
        transactional.methodA2();
      }

     }
Run Code Online (Sandbox Code Playgroud)
    @Service
    public class TransactionalService {

       @Transactional
       public void methodA1(){

       }
       
       @Transactional
       public void methodA2() {

       }

    }
Run Code Online (Sandbox Code Playgroud)

  • 我们有完全相同的设置。我调试了这个问题,它归结为这样一个事实:当第一个事务开始时 --&gt; 数据源密钥已解析 -&gt; hikari 池尝试创建一个新连接,在其中使用预定义的(在配置中)数据源映射的映射和获取正确的数据源。但是,当新事务开始时 --&gt; 数据源密钥再次被解析(如预期) --&gt; 但这一次,hikari 池不会尝试获取新连接,而是使用旧连接,因此它永远不会使用这次的数据源地图。 (2认同)