【问题标题】:Use of multiple DataSources in Spring BatchSpring Batch 中多个数据源的使用
【发布时间】:2014-10-21 19:49:28
【问题描述】:

我正在尝试在 Spring Batch 中配置几个数据源。在启动时,Spring Batch 抛出以下异常:

To use the default BatchConfigurer the context must contain no more thanone DataSource, found 2

批处理配置中的片段

@Configuration
@EnableBatchProcessing 
public class BatchJobConfiguration {

    @Primary
    @Bean(name = "baseDatasource")
    public DataSource dataSource() {
         // first datasource definition here
    }
    @Bean(name = "secondaryDataSource")
    public DataSource dataSource2() {
         // second datasource definition here
    }
    ...
}

不知道为什么我会看到这个异常,因为我已经看到了一些基于 xml 的 Spring 批处理配置,它声明了多个数据源。我正在使用 Spring Batch 核心版本 3.0.1.RELEASE 和 Spring Boot 版本 1.1.5.RELEASE。任何帮助将不胜感激。

【问题讨论】:

  • 使用 xml 你必须明确 Spring Batch 使用哪个数据源。如果您没有使用基于 Java 的配置显式声明它,它将尝试检测数据源是否工作,这仅在检测到单个数据源的情况下才有效。您可以尝试使用@Primary 注释用于批处理的那个。否则,您可以构造一个DefaultBatchConfigurer,它需要一个数据源作为构造参数并将其传递给使用。
  • 我已经尝试使用@Primary,但它不起作用,我将尝试使用 DefaultBatchConfigurer。
  • 在自动装配注释的情况下,bean 是“按类型”注入的。如果同一类型的对象中存在任何冲突,则“按名称”连接 bean。

标签: java spring datasource spring-batch


【解决方案1】:

您可以在下面定义 bean 并确保您的 application.properties 文件具有所需的条目

@Configuration
@PropertySource("classpath:application.properties")
public class DataSourceConfig {

    @Primary
    @Bean(name = "abcDataSource")
    @ConfigurationProperties(prefix = "abc.datasource")
    public DataSource dataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }


    @Bean(name = "xyzDataSource")
    @ConfigurationProperties(prefix = "xyz.datasource")
    public DataSource xyzDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }
}

application.properties

abc.datasource.jdbc-url=XXXXX
abc.datasource.username=XXXXX
abc.datasource.password=xxxxx
abc.datasource.driver-class-name=org.postgresql.Driver

...........
...........
...........
...........

这里可以参考:Spring Boot Configure and Use Two DataSources

【讨论】:

    【解决方案2】:

    最简单的解决方案是扩展 DefaultBatchConfigurer 并通过限定符自动连接您的数据源:

    @Component
    public class MyBatchConfigurer extends DefaultBatchConfigurer {
    
        /**
         * Initialize the BatchConfigurer to use the datasource of your choosing
         * @param firstDataSource
         */
        @Autowired
        public MyBatchConfigurer(@Qualifier("firstDataSource") DataSource firstDataSource) {
            super(firstDataSource);
        }
    }
    

    旁注(因为这也涉及使用多个数据源):如果您使用自动配置来运行数据初始化脚本,您可能会注意到它没有在您期望的数据源上进行初始化。对于这个问题,看看这个:https://github.com/spring-projects/spring-boot/issues/9528

    【讨论】:

    • 运行批处理时出现此异常! org.hsqldb.HsqlException:用户缺少权限或找不到对象:BATCH_JOB_INSTANCE
    • Spring batch 4.1.1 - 这不起作用,因为后处理器不知道填充的属性,并且仍然尝试设置自动连接的数据源,但会找到候选列表。
    【解决方案3】:

    我想在这里提供一个解决方案,这与@vanarchi 回答的解决方案非常相似,但我设法将所有必要的配置放入一个类中。

    为了完整起见,这里的解决方案假设主数据源是hsql。

    @Configuration
    @EnableBatchProcessing
    public class BatchConfiguration extends DefaultBatchConfigurer {
    
    @Bean
    @Primary
    public DataSource batchDataSource() {
    
        // no need shutdown, EmbeddedDatabaseFactoryBean will take care of this
        EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
        EmbeddedDatabase embeddedDatabase = builder
                .addScript("classpath:org/springframework/batch/core/schema-drop-hsqldb.sql")
                .addScript("classpath:org/springframework/batch/core/schema-hsqldb.sql")
                .setType(EmbeddedDatabaseType.HSQL) //.H2 or .DERBY
                .build();
        return embeddedDatabase;
    }
    
    @Override
    protected JobRepository createJobRepository() throws Exception {
        JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
        factory.setDataSource(batchDataSource());
        factory.setTransactionManager(transactionManager());
        factory.afterPropertiesSet();
    
        return (JobRepository) factory.getObject();
    }
    
    private ResourcelessTransactionManager transactionManager() {
        return new ResourcelessTransactionManager();
    }
    
    //NOTE: the code below is just to provide developer an easy way to access the in-momery hsql datasource, as we configured it to the primary datasource to store batch job related data. Default username : sa, password : ''
    @PostConstruct
    public void getDbManager(){
        DatabaseManagerSwing.main(
                new String[] { "--url", "jdbc:hsqldb:mem:testdb", "--user", "sa", "--password", ""});
    }
    

    }

    这个解决方案的三个关键点:

    1. 该类使用@EnableBatchProcessing@Configuration 进行注释,并从DefaultBatchConfigurer 扩展而来。通过这样做,我们指示 spring-batch 在 AbstractBatchConfiguration 尝试查找 BatchConfigurer 时使用我们自定义的批处理配置器;
    2. 将batchDataSource bean注解为@Primary,指示spring-batch使用该数据源作为其存储9个作业相关表的数据源。
    3. 重写 protected JobRepository createJobRepository() throws Exception 方法,这使得 jobRepository bean 使用主数据源,并使用与其他数据源不同的 transactionManager 实例。

    【讨论】:

      【解决方案4】:

      如果我可以补充上述问题,那么每个 DS 都有 2 个事务上下文的含义。如何将 XA 事务与批处理步骤集成,因为我们需要确保步骤级别的 TXN 管理?要求就像在批处理步骤中我们需要以下内容。

      1. 从 DS 1 读取 -- jpaItemReader
      2. 写入 DS2 - JPAItemwriter
      3. 从 DS2 读取 - JPAItemreader
      4. 写入 Ds1 - JPAItemwriter
      5. 提交所有 txns 步骤完成。

      【讨论】:

      • 没那么容易。使用此作业配置器(您提到的那个)创建的批处理步骤将有一个 DS 。和一个 TxnManager。但 Req 是在一个步骤中应该有 2 个不同的 DS 将被读取和写入。它们每个都有各自不同的 TxnManager,它们由一个链式 TXN 管理器链接,或者有一个 XA TXN 管理器将确保所有实体最终提交。
      • @imarchuang - 你说得很对。但是在我的要求中还提到了一个关键点,即使用单独的事务管理器进行单独的 DS 保持单个批处理步骤的封闭上下文。为此,我使用了 ChainedTransactionManager,它又为每个 DS 保存两个单独的 platformtransactionmanager ,并用链式 TXN 管理器封装批处理步骤。
      • 并且任何一个 DS 都可以用来在其中嵌入 dB 以进行批处理。但无论使用哪个都必须成为主要的,并将与默认的 Spring 批处理配置器/自定义批处理配置器连接。
      • 嗯...我认为基于当前 Spring-batch 的事务管理是不可行的...如果您的要求只是从 DS1 读取并写入 DS2,则 Spring-batch 可以毫无问题地管理它。我正在考虑的方式是拥有一个 XA txn 管理器,并使用 Spring 的声明式拦截它的方式将其连接到作业和步骤中
      • 提出问题,兄弟! (this seems related)
      【解决方案5】:

      AbstractBatchConfiguration 首先尝试在容器中查找 BatchConfigurer,如果找不到,则尝试自行创建它 - 这是在容器中有多个 DataSource bean 时抛出 IllegalStateException 的地方。

      解决问题的方法是防止在AbstractBatchConfiguration 中创建DefaultBatchConfigurer bean。 为此,我们提示通过 Spring 容器使用 @Component 注释创建 DefaultBatchConfigurer

      @EnableBatchProcessing 所在的配置类我们可以用@ComponentScan 进行注解,以扫描包含从DefaultBatchConfigurer 派生的空类的包:

      package batch_config;
      ...
      @EnableBatchProcessing
      @ComponentScan(basePackageClasses = MyBatchConfigurer.class)
      public class MyBatchConfig {
          ...
      }
      

      空派生类的完整代码在这里:

      package batch_config.components;
      import org.springframework.batch.core.configuration.annotation.DefaultBatchConfigurer;
      import org.springframework.stereotype.Component;
      @Component
      public class MyBatchConfigurer extends DefaultBatchConfigurer {
      }
      

      在此配置中,@Primary 注释适用于 DataSource bean,如下例所示:

      @Configuration
      public class BatchTestDatabaseConfig {
          @Bean
          @Primary
          public DataSource dataSource()
          {
              return .........;
          }
      }
      

      这适用于 Spring Batch 版本 3.0.3.RELEASE

      使DataSource 上的@Primary 注释起作用的最简单解决方案可能只是添加@ComponentScan(basePackageClasses = DefaultBatchConfigurer.class)@EnableBatchProcessing 注释:

      @Configuration
      @EnableBatchProcessing
      @ComponentScan(basePackageClasses = DefaultBatchConfigurer.class)
      public class MyBatchConfig {
      

      【讨论】:

      • 第二种方法效果很好。在这浪费了4个小时。非常感谢。
      • 在给定的数据源上使用主要是一个坏主意。每当 Spring 注入数据源时,它都会选择主要的数据源。我刚刚遇到了 SpringBatch 和 Spring JPA./Hibernate 的问题。 Hibernate 正在使用 DataSource2,而 SpringJpa 正在主数据源 (Datasource1) 上执行最终提交。
      【解决方案6】:

      您必须提供自己的 BatchConfigurer。 Spring 不想为你做那个决定

      @Configuration
      @EnableBatchProcessing
      public class BatchConfig {
      
           @Bean
            BatchConfigurer configurer(@Qualifier("batchDataSource") DataSource dataSource){
              return new DefaultBatchConfigurer(dataSource);
            }
      
      ...
      

      【讨论】:

      • 简单有效。谢谢!
      • 我们现在需要定义 JobRepository 和 JobLauncher 吗?如果我这样做,我会收到很多错误
      • 4.1.1 - 据我所知,为了使用带有多个数据源的 spring 批处理配置注释,您需要重写整个配置堆栈,以便您可以告诉它使用哪个数据源。
      【解决方案7】:

      首先,创建一个自定义的 BatchConfigurer

      @Configuration
      @Component
      public class TwoDataSourcesBatchConfigurer implements BatchConfigurer {
      
          @Autowired
          @Qualifier("dataSource1")
          DataSource dataSource;
      
          @Override
          public JobExplorer getJobExplorer() throws Exception {
              ...
          }
      
          @Override
          public JobLauncher getJobLauncher() throws Exception {
              ...
          }
      
          @Override
          public JobRepository getJobRepository() throws Exception {
              JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
              // use the autowired data source
              factory.setDataSource(dataSource);
              factory.setTransactionManager(getTransactionManager());
              factory.afterPropertiesSet();
              return factory.getObject();
          }
      
          @Override
          public PlatformTransactionManager getTransactionManager() throws Exception                      {
              ...
          }
      
      }
      

      那么,

      @Configuration
      @EnableBatchProcessing
      @ComponentScan("package")
      public class JobConfig {
          // define job, step, ...
      }
      

      【讨论】:

        猜你喜欢
        • 2020-11-03
        • 1970-01-01
        • 2021-02-25
        • 2022-07-22
        • 1970-01-01
        • 2013-12-18
        • 2021-08-22
        • 1970-01-01
        • 2015-03-06
        相关资源
        最近更新 更多