Ylona
Ylona

Reputation: 13

Database session still open after spring batch processing

I'm currently developing an application that processes a large file file and stores it in a JPA database with spring batch. This happens on request. And after a batch is done you can use the same service to get information about the data loaded in the database on request.

Separately these requests work, but if I try combining them I get an error in my unit tests when persist and flushing entities in the database via the TestEntityManger.

The error is:

javax.persistence.TransactionRequiredException: no transaction is in progress

It seems like the database session is not properly closed after the batch processing is done. But this should be handled by spring batch, but it isn't. Is there something wrong with my configuration or do I need to manually close the session? And if so, how can I do that?

My batch configuration looks like:

@Configuration
@EnableBatchProcessing
public class BatchConfiguration {

    @Autowired
    public JobBuilderFactory jobBuilderFactory;

    @Autowired
    public StepBuilderFactory stepBuilderFactory;

    @Autowired
    public CustomerRepository customerRepository;

    @Bean
    public JsonItemReader<Klant> jsonItemReader() {
        return new JsonItemReaderBuilder<Klant>()
                .jsonObjectReader(new JacksonJsonObjectReader<>    (Klant.class))
                .resource(new ClassPathResource("sample-data.json"))
                .name("tradeJsonItemReader")
                .build();
    }

    @Bean
    public KlantItemProcessor processor() {
        return new KlantItemProcessor();
    }

   @Bean
    public RepositoryItemWriter<Customer> writer(){
        return new RepositoryItemWriterBuilder<Customer>()
                .methodName("save")
                .repository(customerRepository)
                .build();
    }

    @Bean
    public Job importUserJob(JobCompletionNotificationListener listener, Step step1) {
        return jobBuilderFactory.get("importUserJob")
                .incrementer(new RunIdIncrementer())
                .listener(listener)
                .flow(step1)
                .end()
                .build();
    }

    @Bean
    public Step step1(RepositoryItemWriter<Customer> writer) {
        return stepBuilderFactory.get("step1")
                .<Klant, Customer> chunk(1)
                .reader(jsonItemReader())
                .processor(processor())
                .writer(writer)
                .build();
    }

}

Can someone please explain to me what I can do to solve it?

Repository code:

public interface CustomerRepository extends 
PagingAndSortingRepository<Customer, String> {

    Optional<Customer> findCustomerByCustomerProspectNumber(int customerProspectNumber);

}

Unit test:

@RunWith(SpringRunner.class)
@ActiveProfiles("test")
@DataJpaTest
@Import({SecurityAutoConfiguration.class})
public abstract class BaseAccountRepositoryTest {

    private static final String[] DATA = {
            "/backend/model/exampleCustomer.json"
    };

    @Autowired
    protected TestEntityManager em;

    @Autowired
    protected CustomerRepository customerRepository;

    @Autowired
    protected TransactionRepository transactionRepository;

    @Autowired
    protected AccountRepository accountRepository;


    @Before
    public void init(){
        List<Customer> customersList = GsonUtil.fromJson(Customer.class, DATA);
        customersList.forEach(customer -> {

            em.persistAndFlush(customer);

            customer.getAccounts().forEach(account -> {
                account.setCustomer(customer);
                em.persistAndFlush(account);

                account.getTransactions().forEach(transaction -> {
                    transaction.setAccount(account);
                    em.persistAndFlush(transaction);
                });
            });
        });

        assertEquals(DATA.length, customerRepository.count());
        assertEquals(1, transactionRepository.count());
        assertEquals(1, accountRepository.count());


    }
}

Upvotes: 1

Views: 1171

Answers (1)

Mahmoud Ben Hassine
Mahmoud Ben Hassine

Reputation: 31600

Since you are using JPA, you need to use the JpaTransactionManager because by default, Spring Batch will use a DataSourceTransactionManager (more details in the javadoc of @EnableBatchProcessing) which knows nothing about your JPA configuration.

You can make your BatchConfiguration extend DefaultBatchConfigurer or add a bean of type BatchConfigurer in your context and specify the JpaTransactionManager to use by overriding the getTransactionManager method.

Here is an example (from the documentation):

@Bean
public BatchConfigurer batchConfigurer(EntityManagerFactory emf) {
    return new DefaultBatchConfigurer() {
        @Override
        public PlatformTransactionManager getTransactionManager() {
            return new JpaTransactionManager(emf);
        }
    };
}

Upvotes: 1

Related Questions