Spring在数据源之间动态选择(替代ThreadLocal)

时间:2014-07-18 14:30:50

标签: java multithreading spring spring-data spring-batch

我已经阅读了AbstractRoutingDataSource以及this article动态绑定数据源的标准方法:

public class CustomerRoutingDataSource extends AbstractRoutingDataSource {

   @Override
   protected Object determineCurrentLookupKey() {
      return CustomerContextHolder.getCustomerType();
   }
} 

它使用ThreadLocal上下文持有者来“设置”DataSource:

public class CustomerContextHolder {

   private static final ThreadLocal<CustomerType> contextHolder = 
            new ThreadLocal<CustomerType>();

   public static void setCustomerType(CustomerType customerType) {
      Assert.notNull(customerType, "customerType cannot be null");
      contextHolder.set(customerType);
   }

   public static CustomerType getCustomerType() {
      return (CustomerType) contextHolder.get();
   } 

   // ...
}

我有一个非常复杂的系统,其中线程不一定在我的控制之下,比如说:

  • 计划EJB从数据库中读取作业列表
  • 对于每个Job,它将触发Spring(或Java EE)批处理作业。
  • 每个作业都有其原始数据库和目标数据库(从中央数据库中读取)。
  • 多个作业将并行运行
  • 乔布斯可能是多线程的。
  • ItemReader将使用为该特定作业设置的原始数据源(原始数据源必须绑定到某些存储库)
  • ItemWriter将使用为该特定作业设置的目标数据源(目标数据源也必须绑定到某些存储库)。

所以我对ThreadLocal感到有点焦虑,特别是,我不确定是否会使用相同的线程来处理多个作业。如果发生这种情况,原始数据库和目标数据库可能会混合。

在处理多个线程时,如何以安全的方式动态“存储”和绑定数据源?

1 个答案:

答案 0 :(得分:0)

我找不到一种方法来设置Spring与我的设置一起玩得很好并注入所需的DataSource,所以我决定手动处理它。

详细解决方案:

  1. 我将我的存储库更改为原型,以便每次连接时都会构建一个新实例:

    @Repository
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    
  2. 我在顶级接口/实现中引入了新的setDataSourcesetSchema方法,这些方法应该适用于多个实例/模式。

  3. 由于我使用spring-data-jdbc-repository我的setDataSource方法,因此简单地用DataSource包裹新JdbcTemplate并传播更改。

    setJdbcOperations(new JdbcTemplate(dataSource));
    
  4. 我的实现是直接从应用服务器获取DataSources

    final Context context = new InitialContext();
    final DataSource dataSource = (DataSource) context.lookup("jdbc/" + dsName);
    
  5. 最后,对于同一数据库实例下的多个模式,我使用特殊用户(具有正确的权限)登录并使用Oracle命令切换到所需的模式:

    getJdbcOperations().execute("ALTER SESSION SET CURRENT_SCHEMA = " + schema);
    
  6. 虽然这违反了依赖性反转原则,但它能够很好地处理我的并发性要求。