Why does JPA getResultStream() throw "ResultSet is closed" in a multi-threaded Spring service while JdbcTemplate works fine?

18 hours ago 1
ARTICLE AD BOX

I am developing a Spring Boot 3 application (Java 25) that periodically checks for updates from external APIs (GitHub/StackOverflow). I have two repository implementations: one using JdbcTemplate (SQL) and another using EntityManager (JPA/ORM).

The application uses a fixed thread pool to process links in parallel.

The Problem: When I use the SQL implementation, everything works perfectly. However, when I switch to the JPA implementation, I consistently get the following error during parallel execution:

A problem occurred in the SQL executor : Error advancing (next) ResultSet position [This ResultSet is closed.]

My JPA Repository method:

@Override public OffsetDateTime getUpdatedAtByUrl(String url) { String query = "select l.lastUpdatedAt from Link l where l.url = :url"; return entityManager .createQuery(query, OffsetDateTime.class) .setParameter("url", url) .getResultStream() .findFirst() .orElse(null); }

My Service logic:

ExecutorService executor = Executors.newFixedThreadPool(threadCount); for (LinkResponse link : links) { executor.submit(() -> processLink(link)); // Calls the repository inside }

If I replace .getResultStream().findFirst() with .getResultList().stream().findFirst(), the error disappears.

My questions are:

Why does getResultStream() keep the ResultSet open in a way that causes conflicts in a multi-threaded environment, even if the threads are supposedly independent?

Is it a known limitation of Hibernate's Stream support when combined with Spring's transaction management and ExecutorService?

What is the recommended way to fetch a single optional value in JPA to avoid this "ResultSet is closed" race condition?

Read Entire Article