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?
By default, if you provide a
DataSource
bean, Spring Batch will use aDataSourceTransactionManager
to manage transactions. This transaction manager knows nothing about your JPA/Hibernate context. So theHibernateItemWriter
which is using the HibernateSession
behind the scene is not "aware" of the ongoing transaction managed by theDataSourceTransactionManager
. Hence the error:no transaction is in progress
.The
HibernateTransactionManager
is what makes the HibernateSession
participate in spring managed transactions.The
JpaItemWriter
use JPA APIs (EntityManagerFactory
andEntityManager
)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
andSession
) 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 likeOpenJpaItemWriter
orEclipseLinkItemWriter
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