spring-projects / spring-projects/spring-batch
NullPointerException when closing an unopened JpaPagingItemReader
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 3k
- Forks
- 2.5k
- Avg merge
- 6d 53m
- Merged PRs (30d)
- 3
Description
Bug description
Closing a JpaPagingItemReader that has never been opened throws an ItemStreamException caused by a NullPointerException. Its EntityManager is assigned only in doOpen(), but doClose() calls entityManager.close() unconditionally.
This can occur at application context shutdown when an instantiated singleton reader bean has an inferred close destroy method and its step has not run.
Environment
Spring Batch main at a3028e3c8f78c0e385cb61fb800afb32b5f7846a. No database is needed to reproduce this cleanup failure.
Steps to reproduce / Minimal Complete Reproducible example
The following JUnit 5 test uses Mockito for the factory:
import jakarta.persistence.EntityManagerFactory;
import org.junit.jupiter.api.Test;
import org.springframework.batch.infrastructure.item.database.JpaPagingItemReader;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.mockito.Mockito.mock;
class JpaPagingItemReaderCloseTests {
@Test
void closeWithoutOpen() throws Exception {
EntityManagerFactory factory = mock(EntityManagerFactory.class);
JpaPagingItemReader<Object> reader = new JpaPagingItemReader<>(factory);
reader.setQueryString("select o from Object o");
reader.afterPropertiesSet();
assertDoesNotThrow(reader::close);
}
}
The close call throws ItemStreamException: Error while closing item reader, caused by NullPointerException: Cannot invoke "jakarta.persistence.EntityManager.close()" because "this.entityManager" is null in JpaPagingItemReader.doClose().
Expected behavior
Close should skip EntityManager cleanup when none has been created, while still performing superclass cleanup.
The related shutdown case for JpaCursorItemReader was addressed in #5486 / #5489. The paging reader's unguarded close predates the JSpecify migration; this report does not attribute it to that migration.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with JpaPagingItemReader.doClose() and the supplied JpaPagingItemReaderCloseTests reproduction. Run the JUnit test without a database, then verify that closing a reader before open no longer throws while superclass cleanup still occurs. Done means the regression test passes and the existing cleanup behavior remains covered.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 86/100