Transaction management with Spring Batch

java, jpa, spring, spring-batch, spring-transactions

Solution

The problem is that you are creating a second transaction manager (transactionManager2), but Spring Batch is using another transaction manager for starting 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. Take a look at this example: https://github.com/codecentric/spring-batch-javaconfig/blob/master/src/main/java/de/codecentric/batch/configuration/WebsphereInfrastructureConfiguration.java. Here I am switching the transaction manager to a WebspherUowTransactionManager, and in the same way you can switch the transaction manager to some other transaction manager. Here's the link to the blog post explaining it: http://blog.codecentric.de/en/2013/06/spring-batch-2-2-javaconfig-part-3-profiles-and-environments/

Problem

I am discovering actually Spring and I am able to setup some jobs. Now, I would like to save my imported datas in a database using Hibernate/JPA and I keep getting this error : ``` 14:46:43.500 [main] ERROR o.s.b.core.step.AbstractStep - Encountered an error executing the step javax.persistence.TransactionRequiredException: no transaction is in progress ``` I see that the problem is with the transaction. Here is my spring java config for the `entityManager` and the `transactionManager` : ``` @Configuration public class PersistenceSpringConfig implements EnvironmentAware { @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws Exception { // Initializes the entity manager LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); factoryBean.setPersistenceUnitName(PERSISTENCE_UNIT_NAME); factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); factoryBean.setDataSource(dataSource()); // Scans the database model factoryBean.setPackagesToScan(EntiteJuridiqueJPA.class.getPackage().getName()); // Defines the Hibernate properties Properties jpaProperties = new Properties(); jpaProperties.setProperty("hibernate.show_sql", "false"); jpaProperties.setProperty("hibernate.format_sql", "false"); String connectionURL = "jdbc:h2:file:" + getDatabaseLocation(); jpaProperties.setProperty("hibernate.connection.url", connectionURL); jpaProperties.setProperty("hibernate.connection.username", "sa"); jpaProperties.setProperty("hibernate.connection.driver_class", "org.h2.Driver"); jpaProperties.setProperty("hibernate.dialect", H2Dialect.class.getName()); jpaProperties.setProperty("hibernate.hbm2ddl.auto", "create"); jpaProperties.setProperty("hibernate.hbm2ddl.import_files_sql_extractor", "org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor"); jpaProperties.setProperty("hibernate.hbm2ddl.import_files", "org/springframework/batch/core/schema-drop-h2.sql,org/springframework/batch/core/schema-h2.sql"); factoryBean.setJpaProperties(jpaProperties); return factoryBean; } @Bean public PlatformTransactionManager transactionManager2() throws Exception { EntityManagerFactory object = entityManagerFactory().getObject(); JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(object); return jpaTransactionManager; } ``` I am using the JpaItemWriter to store the datas in the database : ``` @Bean public ItemWriter<EntiteJuridiqueJPA> writer() { JpaItemWriter<EntiteJuridiqueJPA> writer = new JpaItemWriter<EntiteJuridiqueJPA>(); writer.setEntityManagerFactory(entityManagerFactory.getObject()); return writer; } ``` This is the code that causes the exception : `javax.persistence.TransactionRequiredException: no transaction is in progress` Any idea to how to solve this problem? [Edit] I am putting also the Job definition and the step definition. All my Spring configuration is written in Java. ``` @Configuration @EnableBatchProcessing @Import(PersistenceSpringConfig.class) public class BatchSpringConfig { @Autowired private JobBuilderFactory jobBuilders; @Autowired private StepBuilderFactory stepBuilders; @Autowired private DataSource dataSource; @Autowired private LocalContainerEntityManagerFactoryBean entityManagerFactory; @Bean public Step step() { return stepBuilders.get("step").<EntiteJuridique, EntiteJuridiqueJPA> chunk(5).reader(cvsReader(null)) .processor(processor()).writer(writer()).listener(processListener()).build(); } @Bean @StepScope public FlatFileItemReader<EntiteJuridique> cvsReader(@Value("#{jobParameters[input]}") String input) { FlatFileItemReader<EntiteJuridique> flatFileReader = new FlatFileItemReader<EntiteJuridique>(); flatFileReader.setLineMapper(lineMapper()); flatFileReader.setResource(new ClassPathResource(input)); return flatFileReader; } @Bean public LineMapper<EntiteJuridique> lineMapper() { DefaultLineMapper<EntiteJuridique> lineMapper = new DefaultLineMapper<EntiteJuridique>(); DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer(); lineTokenizer.setDelimiter(";"); lineTokenizer.setNames(new String[] { "MEGA_ENTITE", "PORTEFEUILLE", "MEGA_ENTITE", "Libellé" }); BeanWrapperFieldSetMapper<EntiteJuridique> fieldSetMapper = new BeanWrapperFieldSetMapper<EntiteJuridique>(); fieldSetMapper.setTargetType(EntiteJuridique.class); lineMapper.setLineTokenizer(lineTokenizer); lineMapper.setFieldSetMapper(fieldSetMapper); return lineMapper; } @Bean public Job dataInitializer() { return jobBuilders.get("dataInitializer").listener(protocolListener()).start(step()).build(); } @Bean public ItemProcessor<EntiteJuridique, EntiteJuridiqueJPA> processor() { return new EntiteJuridiqueProcessor(); } @Bean public ItemWriter<EntiteJuridiqueJPA> writer() { JpaItemWriter<EntiteJuridiqueJPA> writer = new JpaItemWriter<EntiteJuridiqueJPA>(); writer.setEntityManagerFactory(entityManagerFactory.getObject()); return writer; // return new EntiteJuridiqueWriter(); } @Bean public ProtocolListener protocolListener() { return new ProtocolListener(); } @Bean public CSVProcessListener processListener() { return new CSVProcessListener(); } @Bean public PlatformTransactionManager transactionManager2() throws Exception { EntityManagerFactory object = entityManagerFactory.getObject(); JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(object); return jpaTransactionManager; } ``` [EDIT] I am still stuck with this problem. I have followed the suggestions of @Sean Patrick Floyd and @bellabax by setting a transaction manager for the stepBuilders, but I still get the same exception. I have tested my entityManager independtly of spring-batch and I am able to store any data in the database. But, when using the same entity manager with spring batch, I have this exception. Anyone can give more insights how transactions are managed within spring batch? Thx for your help?

Original source