During the migration from Spring-Boot:2.7.4 to Spring-Boot:3.0.9, I notice that EntityGraphJpaSpecificationExecutor.findOne(Specification<T> spec, EntityGraph entityGraph)
does not completely resolve OneToMany collection.
With spring-boot:2.7.4 this worked very well. I can't find any information about this problem in the migration instructions.
This can be seen in the small demo. When calling findOne(with(authorId), entityGraph)
not all books are resolved for the author - test will fail.
I expect all lazy fetched OneToMany
details to be resolved.
You can find small demo here: https://github.com/da-von/spring-boot-jpa-findOne
When logging the generated SQL queries, it is visible that left join
is combined with fetch first ? rows only
limitation.
This seems to make no sense to me, as EntityGraphs can be dynamic and the results must also be completely resolved for dependent details.
select a1_0.id,b1_0.author_id,b1_0.id,b1_0.genre,b1_0.isbn,b1_0.title,a1_0.name
from author a1_0
left join book b1_0 on a1_0.id=b1_0.author_id
where a1_0.id=?
fetch first ? rows only
java:17
locally existing VM temurin-17.jdk
spring-boot:3.0.9
com.cosium.spring.data:spring-data-jpa-entity-graph:3.0.1
Postgres:13-alpine
Solved! Override repository interface with:
@NoRepositoryBean
public interface OverrideEntityGraphSimpleJpaRepository<T, ID> extends JpaRepository<T, ID>, EntityGraphPagingAndSortingRepository<T, ID>, EntityGraphJpaSpecificationExecutor<T> {
@Override
default Optional<T> findOne(Specification<T> spec) {
return findOne(spec, (EntityGraph) null);
}
@Override
default Optional<T> findOne(Specification<T> spec, EntityGraph entityGraph) {
val items = findAll(spec, entityGraph);
if (items.size() > 1) {
throw new IncorrectResultSizeDataAccessException(1);
}
if (items.size() == 1) {
return Optional.of(items.get(0));
}
return Optional.empty();
}
}
AuthorRepository and BookRepository extend this interface, executing the overridden findOne(...)
variants:
public interface AuthorRepository extends OverrideEntityGraphSimpleJpaRepository<Author, Long>,
EntityGraphJpaSpecificationExecutor<Author> {
default Optional<Author> findOne(long id, EntityGraph entityGraph) {
return findOne(
(a, cb, cq) -> cq.equal(a.get(Author_.id), id),
entityGraph
);
}
// other detail ommitted
}
Solution is committed to the sample repository https://github.com/Cosium/spring-data-jpa-entity-graph