【问题标题】:Spring batch doesnt start transaction when using custom writer使用自定义编写器时 Spring Batch 不启动事务
【发布时间】:2020-02-14 18:18:33
【问题描述】:

我正在开发一个从 csv 文件读取并写入数据库的 spring 批处理。我正在使用 FlatFileItemReader 读取文件并实现了 ItemWriter,它使用 Jpa 将数据插入数据库。但是批处理失败,没有正在进行的事务。 这是我的工作配置

<bean id="datasource" class="oracle.jdbc.pool.OracleDataSource">
    <property name="user" value="xxx" />
    <property name="password" value="xx" />
    <property name="URL" value="xxx" />
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<bean id="entityManagerFactory" name="model"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="datasource"/>

    <property name="jpaVendorAdapter">
        <bean
                class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="generateDdl" value="false"/>
            <property name="showSql" value="true"/>
            <property name="database">
                <util:constant
                        static-field="org.springframework.orm.jpa.vendor.Database.ORACLE"/>
            </property>
            <property name="databasePlatform" value="org.hibernate.dialect.Oracle12cDialect"/>
        </bean>
    </property>
</bean>

<batch:job id="testJob">
    <batch:step id="step">
        <batch:tasklet>
            <batch:chunk reader="cvsFileItemReader" writer="databaseWriter"
                         commit-interval="10">
            </batch:chunk>
        </batch:tasklet>
    </batch:step>
</batch:job>

下面是我的作者

public class DatabaseWriter implements ItemWriter<Report> {


private EntityManagerFactory entityManagerFactory;

@Autowired
public DatabaseWriter(EntityManagerFactory entityManagerFactory) {
    this.entityManagerFactory = entityManagerFactory;
}

@Override
public void write(List<? extends Report> list) throws Exception {
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    for (Report report : list) {


        entityManager.persist(report );
    }
    entityManager.flush();

}

}

它只有在明确启动事务时才有效。如下所示

public class DatabaseWriter implements ItemWriter<Report> {


private EntityManagerFactory entityManagerFactory;

@Autowired
public DatabaseWriter(EntityManagerFactory entityManagerFactory) {
    this.entityManagerFactory = entityManagerFactory;
}

@Override
public void write(List<? extends Report> list) throws Exception {
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    entityManager.getTransaction().begin();
    for (Report report : list) {

        entityManager.persist(report );
    }
    entityManager.getTransaction().commit();

}

}

是否真的需要显式维护事务并且 Spring Batch 不对其进行开箱即用的控制?

编辑

我通过改变作家来修复它

public class DatabaseWriter implements ItemWriter<Report> {


@PersistenceContext
private EntityManager entityManager;

@Override

public void write(List<? extends Report> list)  {
    for (Report report : list) {
        entityManager.persist(report );
    }


}

使用 PersistenceContext 将 EntityMangerFactory 更改为 EntityManager 解决了该问题。但我很难理解这种行为的原因

【问题讨论】:

    标签: spring transactions spring-batch


    【解决方案1】:

    创建 DAO 并将您的 JPA 代码移到那里并在您的编写器中自动装配 DAO。

    还要确保您自动装配实体管理器,如下所示。

    @PersistenceContext
    private EntityManager em;
    

    【讨论】:

      【解决方案2】:

      但我很难理解这种行为的原因

      第一种方法不起作用,因为您在代码实际在 Spring Batch 驱动的事务范围内运行时手动创建事务。所以会有两个不同上下文的事务。

      您应该记住,小任务是在由 Spring Batch 驱动的事务中执行的(包括面向块的小任务的项目编写器)。因此,在该范围内运行的任何代码都应符合 tasklet 的事务定义。在您的情况下,写入器中注入的EntityManager 由您定义的JpaTransactionManager 驱动,Spring Batch 也将其用于 tasklet 的事务。

      附带说明,您可以使用 Spring Batch 提供的JpaItemWriter,而不是编写自定义编写器。代码几乎相同。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-09
        • 2020-03-21
        相关资源
        最近更新 更多