14

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?

Dimitri
  • 8,122
  • 19
  • 71
  • 128

2 Answers2

22

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/

Tobias Flohre
  • 300
  • 3
  • 9
  • Ok thx Tobias, I did not notice that part of your tutorials. Thx for your answers. – Dimitri Aug 02 '13 at 13:23
  • 1
    Using Spring 4, this solution does not work as is, batch will create it's schema in the Primary DataSource, which is annoying, and after your first run it will fail every time with duplicate primary key errors. In addition, the interface to BatchConfigurer has been extended. – AdrianVeidt Jul 21 '15 at 14:09
  • Thanks a ton Tobias Flohre. Your solution helped me solving my spring-batch issue which was - "Could not open JPA EntityManager for transaction; nested exception is java.lang.IllegalStateException". It may not be directly related to the same problem posted in question but solution what you gave worked. I was using 2 datasources ( both oracle) and running into same issue. I could resolve the issue by implementing BatchConfigurer interface by the class annotated with @EnableBatchProcessing and overriding the methods with Injected DataSource and PlatformTransactionManager beans created by me. – Ajay Singh Jul 26 '20 at 16:20
7

You need to explicitly reference your Transaction Manager in your step definition:

<job id="sampleJob" job-repository="jobRepository">
    <step id="step1">
        <tasklet transaction-manager="transactionManager">
            <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
        </tasklet>
    </step>
</job>

See: 5.1.1. Configuring a Step


Ah, seeing that you use JavaConfig, you need to assign the transaction manager to the TaskletStepBuilder using builder.transactionManager(transactionManager) (inherited from StepBuilderHelper)

Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • Hi and thx for your answer. Unfortunately, it stills does not work. I still have the same exception. Do you think I have the correct transaction manager definition? – Dimitri Jul 31 '13 at 09:16
  • 1
    And I have another question, do you think the Transaction used in spring batch will/must be the same as the one in Jpa? As you see in my code, the writer is using JpaWriter where I pass an entityManagerFactory. I think that's the problem. I am storing datas but there are no transactions bound to this jpa instance. What do you think? – Dimitri Jul 31 '13 at 09:37
  • 3
    Took me way too long to find this! Thank you! – Telmo Marques May 23 '18 at 15:18