【问题标题】:Spring Batch - How to prevent batch from storing transactions in DBSpring Batch - 如何防止批处理将事务存储在数据库中
【发布时间】:2016-06-09 17:46:52
【问题描述】:

首先是问题陈述: 我在我的 DEV 环境中使用 Spring-Batch 很好。当我将代码移动到生产环境时,我遇到了问题。在我的 DEV 环境中,Spring-Batch 能够毫无问题地在我们的 DB2 数据库服务器中创建它的事务数据表。当我们转到 PROD 时,这不是一个选项,因为这是一项只读作业。

尝试的解决方案:

搜索堆栈溢出我发现了这个帖子: Spring-Batch without persisting metadata to database?

这听起来很完美,所以我添加了

@Bean
public ResourcelessTransactionManager transactionManager() {
    return new ResourcelessTransactionManager();
}

@Bean
public JobRepository jobRepository(ResourcelessTransactionManager transactionManager) throws Exception {
    MapJobRepositoryFactoryBean mapJobRepositoryFactoryBean = new MapJobRepositoryFactoryBean(transactionManager);
    mapJobRepositoryFactoryBean.setTransactionManager(transactionManager);

    return mapJobRepositoryFactoryBean.getObject();
}

我还通过调用 .reporitory(jobRepository) 将其添加到我的作业中。

但我明白了

Caused by: java.lang.NullPointerException: null
    at       org.springframework.batch.core.repository.dao.MapJobExecutionDao.synchronizeStatus(MapJobExecutionDao.java:158) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]

所以我不确定在这里做什么。我是 Spring 新手,所以我一边走一边自学。我对其他解决方案持开放态度,例如内存数据库,但我也无法让它们工作。我不需要在运行之间保存任何状态或会话信息,但我正在运行的数据库查询将返回大约一百万行左右,所以我需要分块获取。

任何建议或帮助将不胜感激。

【问题讨论】:

    标签: spring-batch spring-java-config


    【解决方案1】:

    将此 bean 添加到 AppClass

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new ResourcelessTransactionManager();
    }
    
    
    @Bean
    public JobExplorer jobExplorer() throws Exception {
        MapJobExplorerFactoryBean jobExplorerFactory = new MapJobExplorerFactoryBean(mapJobRepositoryFactoryBean());
        jobExplorerFactory.afterPropertiesSet();
        return jobExplorerFactory.getObject();
    }
    
    @Bean
    public MapJobRepositoryFactoryBean mapJobRepositoryFactoryBean() {
        MapJobRepositoryFactoryBean mapJobRepositoryFactoryBean = new MapJobRepositoryFactoryBean();
        mapJobRepositoryFactoryBean.setTransactionManager(transactionManager());
        return mapJobRepositoryFactoryBean;
    }
    
    @Bean
    public JobRepository jobRepository() throws Exception {
        return mapJobRepositoryFactoryBean().getObject();
    }
    
    @Bean
    public JobLauncher jobLauncher() throws Exception {
        SimpleJobLauncher simpleJobLauncher = new SimpleJobLauncher();
        simpleJobLauncher.setJobRepository(jobRepository());
        return simpleJobLauncher;
    }
    

    【讨论】:

      【解决方案2】:

      这并不能直接回答您的问题,但这不是一个好的解决方案;基于地图的存储库应该仅用于测试。它会在内存中无限增长。

      我建议你使用像 sqlite 这样的嵌入式数据库。将单独的数据库用于作业元数据的主要问题是,您应该协调您使用的两个数据库之间的事务(以便元数据的状态与数据的状态相匹配),但因为看起来您甚至没有编写在主数据库中,这对您来说可能不是问题。

      【讨论】:

      • 为什么它会在内存中无限增长?一旦工作完成,java程序退出,直到第二天才能重新启动。当 java 程序退出时,应该清除内存中的任何内容。第二天,当它运行时,应该会在我们的调度程序调用 java 程序时创建一个新的内存数据库。我不需要任何类型的状态来在运行之间持续存在。
      • @VydorScope 好吧,它会无限增长,直到 JVM 退出或上下文关闭。如果这就是您运行 Spring 批处理的方式,那么这不是问题。但是还有其他问题(可能适用于您也可能不适用于您),例如地图实现不参与事务。
      • JVM 将被关闭,一旦作业完成,上下文将被关闭。
      【解决方案3】:

      您可以很容易地使用内存数据库(例如 H2 或 HSQL)。例如,您可以在此处找到示例:http://www.mkyong.com/spring/spring-embedded-database-examples/

      至于 Map 支持的作业存储库,它确实提供了一个 method 来清除其内容:

      public void clear()
      
      Convenience method to clear all the map DAOs globally, removing all entities.
      

      请注意,基于 Map 的作业存储库不适合在分区步骤和其他多线程中使用。

      【讨论】:

      • 我在做多线程。我有一组并行运行的步骤。在这种情况下,您所说的“不适合使用”是什么意思?
      • 不久前我问过自己,here。该文档明确指出不应使用 Map 支持的作业存储库。
      【解决方案4】:

      以下内容似乎为我完成了这项工作:

      @Bean
      public DataSource dataSource() {        
      
          EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
          EmbeddedDatabase db = builder
              .setType(EmbeddedDatabaseType.HSQL) 
              .build();
          return db;
      }
      

      现在 Spring 不会在我们的生产数据库中创建表,并且当 JVM 退出状态时会丢失,因此似乎没有任何东西在徘徊。

      更新: 上面的代码给我们带来了并发错误。我们已经通过放弃 EmbeddedDatabaseBuilder 并以这种方式声明 HSQLDB 来解决这个问题:

      @Bean
          public BasicDataSource dataSource() {
              BasicDataSource dataSource = new BasicDataSource();
                dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
                dataSource.setUrl("jdbc:hsqldb:mem:testdb;sql.enforce_strict_size=true;hsqldb.tx=mvcc");
                dataSource.setUsername("sa");
                dataSource.setPassword("");
              return dataSource;
          }   
      

      主要区别在于我们能够在连接字符串中指定 mvcc(多版本并发控制)来解决问题。

      【讨论】:

      • 这不是线程安全的。如果两个线程同时尝试访问同一个作业,将会出现并发线程访问异常
      • 那么您如何将其更改为线程安全的,同时保持我工作的限制?
      • 我使用了基于地图的存储库
      • 正如 Heikki Doeleman 在下面的回答中提到的,不建议将 map 用于像我这样的多线程工作。
      • 我在使用这个嵌入式数据库时遇到了并发异常。我在地图上没有问题
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-04-22
      • 2017-01-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-02
      • 1970-01-01
      相关资源
      最近更新 更多