【问题标题】:Spring-Batch without persisting metadata to database?Spring-Batch 没有将元数据持久化到数据库?
【发布时间】:2023-04-06 12:46:02
【问题描述】:

我想创建一个spring-batch 作业,但我想在没有任何数据库持久性的情况下运行它。不幸的是,spring-batch 需要以某种方式将工作周期写入metadata ob 到数据库中,从而促使我至少提供某种带有事务管理器和实体管理器的数据库。

是否可以阻止元数据并独立于 txmanagers 和数据库运行?

更新:

ERROR org.springframework.batch.core.job.AbstractJob: Encountered fatal error executing job
java.lang.NullPointerException
    at org.springframework.batch.core.repository.dao.MapJobExecutionDao.synchronizeStatus(MapJobExecutionDao.java:158) ~[spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.batch.core.repository.support.SimpleJobRepository.update(SimpleJobRepository.java:161) ~[spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.7.0_51]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[?:1.7.0_51]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.7.0_51]
    at java.lang.reflect.Method.invoke(Method.java:606) ~[?:1.7.0_51]
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317) ~[spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98) ~[spring-tx-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262) ~[spring-tx-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) ~[spring-tx-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) ~[spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at com.sun.proxy.$Proxy134.update(Unknown Source) ~[?:?]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.7.0_51]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[?:1.7.0_51]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.7.0_51]
    at java.lang.reflect.Method.invoke(Method.java:606) ~[?:1.7.0_51]
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317) ~[spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) ~[spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at com.sun.proxy.$Proxy134.update(Unknown Source) ~[?:?]
    at org.springframework.batch.core.job.AbstractJob.updateStatus(AbstractJob.java:416) ~[spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:299) [spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:135) [spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) [spring-core-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:128) [spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.7.0_51]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[?:1.7.0_51]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.7.0_51]
    at java.lang.reflect.Method.invoke(Method.java:606) ~[?:1.7.0_51]
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317) [spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) [spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) [spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) [spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) [spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at com.sun.proxy.$Proxy50.run(Unknown Source) [?:?]

【问题讨论】:

  • 您能否分享您的工作和工作存储库配置(假设您使用的是 user3218114 提到的 MapJobRepository)?

标签: java spring spring-batch


【解决方案1】:

只需为批量配置创建一个没有数据源的配置:

@Configuration
@EnableAutoConfiguration
@EnableBatchProcessing
public class BatchConfiguration extends DefaultBatchConfigurer {

    @Override
    public void setDataSource(DataSource dataSource) {
        // override to do not set datasource even if a datasource exist.
        // initialize will use a Map based JobRepository (instead of database)
    }

}

它将使用基于内存映射的实现来初始化 JobRepository 和 JobExplorer。 https://github.com/spring-projects/spring-batch/blob/342d27bc1ed83312bdcd9c0cb30510f4c469e47d/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/DefaultBatchConfigurer.java#L84

即使使用 spring boot 自动配置,您也可以使用您的生产数据源。

【讨论】:

  • 我正在使用数据源来提取数据并使用数据源对其进行转换,而 Spring Batch 正在使用此数据源来存储他的表。现在我可以使用我的数据源并且有 Spring Batch Tables 但它们没有被使用。谢谢。
  • 您可以在 application.properties 中结合 spring.batch.initializer.enabled=false 以不重新创建 Spring Batch 表。
  • 很好的解决方案,谢谢,也感谢@AntuanSoft。
  • 对我来说最好的答案。
  • 但是这个配置正在等待数据源。我想在没有数据源的情况下工作
【解决方案2】:

我想在没有任何数据库持久性的情况下运行它

您可以使用MapJobRepositoryFactoryBeanResourcelessTransactionManager

示例配置:

<bean id="transactionManager"
    class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />

<bean id="jobRepository"
    class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
    <property name="transactionManager" ref="transactionManager" />
</bean>

<bean id="jobLauncher"
    class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
    <property name="jobRepository" ref="jobRepository" />
</bean>

对于 Spring 4.X,基于注解的配置如下:

@Bean
public PlatformTransactionManager getTransactionManager() {
    return new ResourcelessTransactionManager();
}

@Bean
public JobRepository getJobRepo() {
    return new MapJobRepositoryFactoryBean(getTransactionManager()).getObject();
}

【讨论】:

  • 不幸的是产生了上面更新的错误:NPE during MapJobExecutionDao.synchronizeStatus()...
  • 我应该把完整的配置文件分享给你吗?
  • 也许这会有所帮助,那将是非常友好的。我的配置看起来完全像这样:github.com/lhinze/spring-batch-retrytemplate-example/blob/… 主要问题似乎是在MapJobExecutionDao.classgetJobExecution() 方法返回null,因为executionsById 映射是空的。我是否可能忘记进行任何初始化?
  • 我回来了,并将您的示例配置迁移到基于 spring 4 注释的配置。上面编辑过,它按预期工作!
  • 试过这个,但它不能与 spring-boot + spring-batch 一起工作
【解决方案3】:

调整@Braj 的答案后,我的工作配置如下:

@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();
}

@Bean
public SimpleJobLauncher jobLauncher(JobRepository jobRepository) {
    SimpleJobLauncher simpleJobLauncher = new SimpleJobLauncher();
    simpleJobLauncher.setJobRepository(jobRepository);
    return simpleJobLauncher;
}

【讨论】:

  • 对我来说,不知何故基于 xml 的配置有效,但基于注释的配置无效。
  • 你确定你复制这些@Bean定义的类是用@Configuration注释的,并且它正在被Spring Boot拾取吗?查看启动日志以找出答案。
  • 只要我明确排除我在this question中回答的数据源bean,它就可以工作
  • 感谢@Sabir Khan @SpringBootApplication(exclude={DataSource.class,DataSourceAutoConfiguration.class}) 为我工作。
  • Cleankod 的配置导致org.springframework.transaction.TransactionSuspensionNotSupportedException 被抛出用于我使用 Spring Boot 1.5.2 进行的批处理。
【解决方案4】:

我回到自己的问题,因为解决方案不再起作用。 从 spring-batch-1.5.3 开始使用如下:

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
...
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new ResourcelessTransactionManager();
    }
}

【讨论】:

  • 谢谢。我不需要声明 transactionManager,它与 exclude 一起工作得很好。
  • 这是假设没有其他数据库用于应用程序吗?
  • 如果您的批处理使用 jpa 存储库将数据保存到数据库中,这将不起作用
【解决方案5】:

如果您不想将作业的元数据存储在数据库中,配置如下所示

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

@Bean
public MapJobRepositoryFactoryBean mapJobRepositoryFactory(ResourcelessTransactionManager transactionManager)
        throws Exception {
    MapJobRepositoryFactoryBean factory = new MapJobRepositoryFactoryBean(transactionManager);
    factory.afterPropertiesSet();
    return factory;
}

@Bean
public JobRepository jobRepository(MapJobRepositoryFactoryBean repositoryFactory) throws Exception {
    return repositoryFactory.getObject();
}

@Bean
public JobExplorer jobExplorer(MapJobRepositoryFactoryBean repositoryFactory) {
    return new SimpleJobExplorer(repositoryFactory.getJobInstanceDao(), repositoryFactory.getJobExecutionDao(),
            repositoryFactory.getStepExecutionDao(), repositoryFactory.getExecutionContextDao());
}

@Bean
public SimpleJobLauncher jobLauncher(JobRepository jobRepository) {
    SimpleJobLauncher launcher = new SimpleJobLauncher();
    launcher.setJobRepository(jobRepository);
    return launcher;
}
}

如果job需要读/写业务数据到数据库,数据源配置如下。

@Autowired
private DataSource dataSource;

spring batch 使用该数据源创建内部模式(BATCH_* 表和序列)。为防止这种情况发生,请在 application.properties 中设置标志 spring.batch.initializer.enabled=false

【讨论】:

    【解决方案6】:

    我尝试了上述所有解决方案,但它不适用于我的特定场景,因为我的 spring 批处理作业具有多线程等高级功能。它需要一个数据库。所以这是我为解决问题所做的:

    @Configuration
    public class FakeBatchConfig implements BatchConfigurer {
    
      PlatformTransactionManager transactionManager;
      JobRepository jobRepository;
      JobLauncher jobLauncher;
      JobExplorer jobExplorer;
    
      @Override
      public JobRepository getJobRepository() {
        return jobRepository;
      }
    
      @Override
      public PlatformTransactionManager getTransactionManager() {
        return transactionManager;
      }
    
      @Override
      public JobLauncher getJobLauncher() {
        return jobLauncher;
      }
    
      @Override
      public JobExplorer getJobExplorer() {
        return jobExplorer;
      }
    
      @PostConstruct
      void initialize() throws Exception {
    
        if (this.transactionManager == null) {
          this.transactionManager = new ResourcelessTransactionManager();
        }
    
        MapJobRepositoryFactoryBean jobRepositoryFactory = new MapJobRepositoryFactoryBean(this.transactionManager);
        jobRepositoryFactory.afterPropertiesSet();
        this.jobRepository = jobRepositoryFactory.getObject();
    
        MapJobExplorerFactoryBean jobExplorerFactory = new MapJobExplorerFactoryBean(jobRepositoryFactory);
        jobExplorerFactory.afterPropertiesSet();
        this.jobExplorer = jobExplorerFactory.getObject();
        this.jobLauncher = createJobLauncher();
      }
    
      private JobLauncher createJobLauncher() throws Exception {
        SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
        jobLauncher.setJobRepository(jobRepository);
        jobLauncher.afterPropertiesSet();
        return jobLauncher;
      }
    
    }
    

    【讨论】:

    • 感谢 Jeff 的编辑。我原来的帖子是用 Groovy 写的,而不是 Java。
    • @夏青,谢谢。你能指导我们如何在这里传递 JobParameters 吗?
    • 当我使用相同的解决方案时,我在使用 Spring Data Repository.save() 时遇到问题 .. 它不会持久化对象!
    • @Ghassen 我面临保存无法正常工作的问题。任何指针你是如何解决的?
    • @Ghassen 我也面临同样的问题。批处理无法将数据保存到数据库。
    【解决方案7】:

    除了@Braj-s 响应之外,我还必须排除批量自动配置: @SpringBootApplication(exclude = {BatchAutoConfiguration.class})

    否则无法正常工作。 这包括 Spring Boot 应用程序

    【讨论】:

      【解决方案8】:

      阅读所有提供的答案后,我得到了它的工作。我混合使用了 membersound 和 Aure77 解决方案。我发现没有必要重写 setDataSource 方法。 DefaultBatchConfigurer 自动处理 PlatformTransactionManager 并且不需要 DataSource。请注意我使用的是 Spring boot 2.0.3.RELEASE。 以下是我使用的方法

      @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
      @EnableBatchProcessing
      public class DemoApplication extends DefaultBatchConfigurer {
      
          @Autowired
          private JobBuilderFactory jobs;
      
          @Autowired
          private StepBuilderFactory steps;
      
          @Bean
          protected Tasklet tasklet() {
      
              return new Tasklet() {
                  @Override
                  public RepeatStatus execute(StepContribution contribution, ChunkContext context) {
                      return RepeatStatus.FINISHED;
                  }
              };
          }
      
          @Bean
          public Job job() throws Exception {
              return this.jobs.get("job").start(step1()).build();
          }
      
          @Bean
          protected Step step1() throws Exception {
              return this.steps.get("step1").tasklet(tasklet()).build();
          }
      
          public static void main(String[] args) throws Exception {
              //System.exit(SpringApplication.exit(SpringApplication.run(DemoApplication.class, args)));
              SpringApplication.run(DemoApplication.class, args);
          }
      }
      

      【讨论】:

        【解决方案9】:

        最简单的方法是将嵌入式数据库添加到类路径。 自然不建议在生产中使用,但如果您使用它来学习,请节省时间。

        将嵌入式 H2 数据库依赖项添加到您的 pom.xml:

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        

        就是这样。

        【讨论】:

        • 是的,这绝对是最简单的!谢谢。
        【解决方案10】:

        可以进行以下更改以使用 Spring Batch,而无需创建与 Spring Batch 相关的元数据数据库结构。

        这对我有用:

        1. 添加了ResourcelessTransactionManager 类型的bean 并配置了MapJobRepositoryFactoryBean
        2. 使用配置的 bean MapJobRepositoryFactoryBean 创建了一个 SimpleJoblauncher bean。
        3. @SpringBootApplication 下方添加注释@EnableBatchProcessing
        4. 要覆盖默认值,我必须在application.properties 中添加spring.main.allow-bean-definition-overriding=true

        【讨论】:

        • 我只是想使用 Spring Batch 提供的功能,如 Scheduler 和 ItemReader 和 ItemWriter,但不想在数据库表中维护执行状态,因为我的需求是一个简单而微不足道的需求。如果您有复杂的企业级需求,使用所有 Spring 批处理表会很有帮助,我鼓励您这样做。
        猜你喜欢
        • 1970-01-01
        • 2020-02-23
        • 2019-10-23
        • 2016-08-19
        • 2021-07-09
        • 1970-01-01
        • 2016-11-09
        • 1970-01-01
        • 2015-08-19
        相关资源
        最近更新 更多