【问题标题】:Using Spring Batch Without Transaction在没有事务的情况下使用 Spring Batch
【发布时间】:2021-03-17 13:21:13
【问题描述】:

我有一个 Spring Batch 作业,它在第一步/tasklet 中调用另一个作业。当然,这不适用于作业默认处理事务的方式。

在我的应用程序中,我真的不需要那么多事务,而且这些步骤也没有数据库连接,所以我宁愿完全删除事务。为此,对于这两项工作的每一步,我都做了以下工作:

  public Step stepX() {
    return this.stepBuilderFactory
        .get("stepX")
        .tasklet(new StepXTasklet())
        .transactionAttribute(transactionAttribute())
        .build();
  }

  private TransactionAttribute transactionAttribute() {
    DefaultTransactionAttribute attribute = new DefaultTransactionAttribute();
    attribute.setPropagationBehavior(Propagation.SUPPORTS.value());
    return attribute;
  }

应用程序仍然会抛出此异常:

org.springframework.dao.OptimisticLockingFailureException: Attempt to update step execution id=1 with wrong version (2), where current version is 3
    at org.springframework.batch.core.repository.dao.MapStepExecutionDao.updateStepExecution(MapStepExecutionDao.java:106)
    at org.springframework.batch.core.repository.support.SimpleJobRepository.update(SimpleJobRepository.java:196)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:353)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:99)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
    at com.sun.proxy.$Proxy150.update(Unknown Source)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)

出于某种原因,Spring Batch 是如何呈现其内部 TransactionSuspensionNotSupportedException 的。

所以有些东西仍在使用事务,我只是不确定是什么以及如何删除它们。也许JobExecutionListener?也许是块?

从第二个块之后(按读取的行数计算)到第三个块之前我得到输出:

Commit failed while step execution data was already updated. Reverting to old version.

如果有所不同,父作业是“流”作业,子作业是“读取器-变压器-写入器”。

Spring Batch 作业框架的哪些部分使用事务,我如何确保这些部分停止使用它们?

【问题讨论】:

  • Spring Batch 将有关作业和步骤执行的数据写入数据库。这就是它需要交易的原因。
  • 如果您不希望这种行为结帐stackoverflow.com/questions/25077549/…
  • @SimonMartinelli 但是这些交易不应该在“工作中的工作”世界中重叠,不是吗?它们只是在步骤之间,所以不应该有任何TransactionSuspensionNotSupportedException,因为没有任何打开的事务必须暂停才能让另一个运行。
  • 为什么你认为这个OptimisticLockingFailureException是TransactionSuspensionNotSupportedException?
  • @SimonMartinelli 因为我调试了它。父作业启动子作业,并在创建步骤时尝试打开事务,这在标准 TransactionManager 中是不可能的。这种行为通常很奇怪,因为之后它会重新启动父作业,这也不应该发生。可能不是获得交易的步骤,而是块?因为对于这一步我禁用了交易。

标签: java spring spring-batch


【解决方案1】:

问题在于默认作业存储库。似乎它的事务处理is buggy。要解决此问题,请将其替换为具有内存数据库的 JDBC 作业存储库。只需将此类添加到 Spring 上下文中:

@Configuration
@EnableBatchProcessing
public class InMemoryBatchContextConfigurer extends DefaultBatchConfigurer {

  @Override
  protected JobRepository createJobRepository() throws Exception {
    JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
    factory.setDatabaseType(DatabaseType.H2.getProductName());
    factory.setDataSource(dataSource());
    factory.setTransactionManager(getTransactionManager());
    return factory.getObject();
  }

  public DataSource dataSource() {
    EmbeddedDatabaseBuilder embeddedDatabaseBuilder = new EmbeddedDatabaseBuilder();
    return embeddedDatabaseBuilder
        .addScript("classpath:org/springframework/batch/core/schema-drop-h2.sql")
        .addScript("classpath:org/springframework/batch/core/schema-h2.sql")
        .setType(EmbeddedDatabaseType.H2).build();
  }
}

【讨论】:

    猜你喜欢
    • 2014-08-21
    • 1970-01-01
    • 2019-08-19
    • 2016-04-25
    • 2013-09-16
    • 1970-01-01
    • 2020-04-24
    • 2023-03-10
    • 1970-01-01
    相关资源
    最近更新 更多