Reputation: 13
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
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