Spring Batch JpaItemWriter vs HibernateItemWriter 以及为啥在使用 HibernateItemWriter 时需要 HibernateTransacti

Posted

技术标签:

【中文标题】Spring Batch JpaItemWriter vs HibernateItemWriter 以及为啥在使用 HibernateItemWriter 时需要 HibernateTransactionManager【英文标题】:Spring Batch JpaItemWriter vs HibernateItemWriter and why HibernateTransactionManager is needed when HibernateItemWriter is usedSpring Batch JpaItemWriter vs HibernateItemWriter 以及为什么在使用 HibernateItemWriter 时需要 HibernateTransactionManager 【发布时间】:2019-02-20 13:42:24 【问题描述】:

我正在使用 Spring Boot 项目开发 Spring Batch。 我的问题是,当我如下使用 HibernateItemWriter 时,为什么需要来自 LocalSessionFactoryBean 的 HibernateTransactionManager 和 SessionFactory?

Application.java

import java.util.Properties;
import javax.sql.DataSource;

import org.hibernate.SessionFactory;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBuilder;
import org.springframework.transaction.PlatformTransactionManager;

@SpringBootApplication
@EnableBatchProcessing
public class Application 

public static void main(String[] args) throws Exception 
    SpringApplication.run(Application.class, args);


@Bean
public PlatformTransactionManager transactionManager() 
    HibernateTransactionManager transactionManager = new HibernateTransactionManager();
    transactionManager.setSessionFactory(sessionFactory(null));
    return transactionManager;


@Bean
public SessionFactory sessionFactory(DataSource datasource) 
    Properties properties = new Properties();
    properties.setProperty("hibernate.show_sql", "true");
    properties.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");

    return new LocalSessionFactoryBuilder(datasource).scanPackages("hello")
             .addProperties(properties)
            .buildSessionFactory();


BatchConfiguration.java

import org.hibernate.SessionFactory;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.database.HibernateItemWriter;
import org.springframework.batch.item.file.FlatFileItemWriter;
import org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor;
import 
org.springframework.batch.item.file.transform.DelimitedLineAggregator;
import org.springframework.batch.item.file.transform.FieldExtractor;
import org.springframework.batch.item.file.transform.LineAggregator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.web.client.RestTemplate;

@Configuration
public class BatchConfiguration 

@Autowired
public JobBuilderFactory jobBuilderFactory;

@Autowired
public StepBuilderFactory stepBuilderFactory;

@Autowired
public RestTemplate restTemplate;


@Autowired
public PlatformTransactionManager tManager;

@Value("$file.name")
public String fileName;


@Bean
@StepScope
public RestItemReader reader2() 
    return new RestItemReader(restTemplate);


@Bean
public PersonItemProcessor processor() 
    return new PersonItemProcessor();


@Bean
public HibernateItemWriter<Person> hibernateWriter(SessionFactory emf) 
    HibernateItemWriter<Person> writer = new HibernateItemWriter<>();
    writer.setSessionFactory(emf);
    return writer;



@Bean
public Step step1() 
    return stepBuilderFactory.get("step1")
            .transactionManager(tManager)
        .<KarvyFundInfoModel, Person> chunk(2)
        .reader(reader2())
        .processor(new PersonItemProcessor())
        .writer(hibernateWriter(null))
        .build();


这是因为如果我没有包含它,并且使用以下代码从 EntityManagerFactory 获取 SessionFactory

EntityManagerFactory.unwarp(SessionFactory.class);

我会收到“没有正在进行的交易”错误。但是,当我使用 JpaItemWriter 时,情况并非如此。

但是根据我对Spring Batch的理解,在chunk处理中,已经提供了一个默认的事务管理器。

为了使用 HibernateItemWriter,是否必须从 LocalSessionFactoryBean(from hibernate) 提供 HibernateTransactionManager 和 SessionFactory?

那么,JpaItemWriter 和 HibernateItemWriter 之间的主要区别是什么? 我已经对这两者进行了研究,Jpa 是关于如何使用注释方式指定实体等的规范,而 hibernate 是 Jpa 的实现之一。不过,我对此还不是很清楚。与默认 jpa 相比,hibernate 是否具有更多功能?比如 SearchCriteria 等?

【问题讨论】:

【参考方案1】:

为什么当我使用 HibernateItemWriter 时需要来自 LocalSessionFactoryBean 的 HibernateTransactionManager 和 SessionFactory

默认情况下,如果您提供DataSource bean,Spring Batch 将使用DataSourceTransactionManager 来管理事务。这个事务管理器对您的 JPA/Hibernate 上下文一无所知。因此,在幕后使用 Hibernate SessionHibernateItemWriter 并不“知道”由 DataSourceTransactionManager 管理的正在进行的事务。因此出现错误:no transaction is in progress

HibernateTransactionManager 是 Hibernate Session 参与 Spring 托管事务的原因。

JpaItemWriter 和 HibernateItemWriter 的主要区别是什么?

JpaItemWriter 使用 JPA API(EntityManagerFactoryEntityManager)来编写项目。它不使用任何 JPA 提供者特定的 API。这样就可以在不更改编写器的情况下切换 JPA 提供程序。

另一边的HibernateItemWriter 使用Hibernate 特定的API(SessionFactorySession)并且仅特定于Hibernate。这个组件对于直接使用休眠而不使用 JPA 的应用程序很有用。您可以拥有相同的编写器,但要使用其他 JPA 提供程序,例如 OpenJpaItemWriterEclipseLinkItemWriter,它们使用这些提供程序的特定 API。


注意:有一个类似的问题,我在这里添加以供参考:Transaction management with Spring Batch

【讨论】:

很好的解释!非常感谢!

以上是关于Spring Batch JpaItemWriter vs HibernateItemWriter 以及为啥在使用 HibernateItemWriter 时需要 HibernateTransacti的主要内容,如果未能解决你的问题,请参考以下文章

Spring-batch:如何在 Spring Batch 中使用 skip 方法捕获异常消息?

spring batch ftp 集成超时错误 - 使用 spring-boot/spring-batch 进行 ETL

陪你解读Spring Batch带你入手Spring Batch

Spring boot spring.batch.job.enabled=false 无法识别

Spring boot spring.batch.job.enabled=false 无法识别

spring batch读取数据库怎么用