0

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
  • Local Machine: MacBook Pro, macOS Ventura:13.4.1
  • 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 in Docker image Postgres:13-alpine
danielv
  • 1
  • 1

1 Answers1

0

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

danielv
  • 1
  • 1