Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Batch JpaItemWriter vs HibernateItemWriter and why HibernateTransactionManager is needed when HibernateItemWriter is used

I'm working on Spring Batch with Spring Boot project. My question is, why HibernateTransactionManager and SessionFactory from LocalSessionFactoryBean are needed when I use HibernateItemWriter as below?

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

This is because if I didn't include it, and by getting SessionFactory from EntityManagerFactory using code as below

EntityManagerFactory.unwarp(SessionFactory.class);

I will get "no transaction is in progress" error. However, this is not the case when I use JpaItemWriter.

However based on my understanding on Spring Batch, in chunk processing, a default transaction manager is already provided.

Is it a must to provide HibernateTransactionManager and SessionFactory from LocalSessionFactoryBean(from hibernate) in order to use HibernateItemWriter?

And, what is the main difference between JpaItemWriter and HibernateItemWriter? I already did research regarding these two, Jpa is a specification on how to specify entity etc. by using annotation way and hibernate is one of the implementations of Jpa. Still, I'm not very clear on this. Is it hibernate has more features over default jpa? Such as SearchCriteria etc?

like image 500
AlexZai Avatar asked Sep 16 '18 12:09

AlexZai


People also ask

How do I change the transaction manager Spring Batch uses for transactions?

If you use @EnableBatchProcessing, Spring Batch automatically registers a transaction manager to use for its transactions, and your JpaTransactionManager never gets used. If you want to change the transaction manager that Spring Batch uses for transactions, you have to implement the interface BatchConfigurer.

What is the use of itemreader in Spring Batch framework?

One of the important goals of a batch processing framework is to read large amounts of data, perform some business processing/transformation and write out the result. Spring Batch Framework supports this bulk reading, processing and writing using three key interfaces: ItemReader, ItemProcessor and ItemWriter. 1.

What is the use of itemwriter in JPA?

ItemWriter that is using a JPA EntityManagerFactory to merge any Entities that aren't part of the persistence context. It is required that write (List) is called inside a transaction. The reader must be configured with an EntityManagerFactory that is capable of participating in Spring managed transactions.

How do I commit one item at a time in Spring Batch?

Note the property commitInterval which is set to 1. This tells Spring Batch that the commit should happen after 1 element .i.e. writer will write 1 item at a time. Spring Batch comes with a simple utility class called CommandLineJobRunner which has a main () method which accepts two arguments.


1 Answers

why HibernateTransactionManager and SessionFactory from LocalSessionFactoryBean are needed when I use HibernateItemWriter

By default, if you provide a DataSource bean, Spring Batch will use a DataSourceTransactionManager to manage transactions. This transaction manager knows nothing about your JPA/Hibernate context. So the HibernateItemWriter which is using the Hibernate Session behind the scene is not "aware" of the ongoing transaction managed by the DataSourceTransactionManager. Hence the error: no transaction is in progress.

The HibernateTransactionManager is what makes the Hibernate Session participate in spring managed transactions.

what is the main difference between JpaItemWriter and HibernateItemWriter?

The JpaItemWriter use JPA APIs (EntityManagerFactory and EntityManager)to write items. It does not use any JPA provider specific APIs. This makes it possible to switch the JPA provider without changing your writer.

The HibernateItemWriter on the other side uses Hibernate specific APIs (SessionFactory and Session) and is specific to Hibernate only. This component can be useful for apps using hibernate directly without using JPA. You could have the same writer but for another JPA provider like OpenJpaItemWriter or EclipseLinkItemWriter which use specific APIs from these providers.


NB: There is a similar question to this one, I'm adding it here for reference: Transaction management with Spring Batch

like image 143
Mahmoud Ben Hassine Avatar answered Nov 15 '22 12:11

Mahmoud Ben Hassine