JPA @OneToOne select lists with N+1 queries

6.6k Views Asked by At

I'm actually trying to use JPA @OneToOne annotation to link a Child entity to its Parent.

It's working well, except the fact that when getting a list of Childs, the JPA engine (Hibernate in this case) make 1+n queries.

Here is the log of the Hibernate queries :

select child0_.id as id1_0_, child0_.parent as parent3_0_, child0_.value as value2_0_ from child child0_
select parent0_.id as id1_1_0_, parent0_.something as somethin2_1_0_ from parent parent0_ where parent0_.id=?
select parent0_.id as id1_1_0_, parent0_.something as somethin2_1_0_ from parent parent0_ where parent0_.id=?
select parent0_.id as id1_1_0_, parent0_.something as somethin2_1_0_ from parent parent0_ where parent0_.id=?

Using exactly the same entities definition, when I get a child in particular, JPA executes the query with expected JOIN :

select child0_.id as id1_0_0_, child0_.parent as parent3_0_0_, child0_.value as value2_0_0_, parent1_.id as id1_1_1_, parent1_.something as somethin2_1_1_ from child child0_ left outer join parent parent1_ on child0_.parent=parent1_.id where child0_.id=?

Here is the Child entity definition :

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
@Table(name = "child")
public class Child {

    @Id
    private Long   id;
    @Column
    private String value;
    @OneToOne(optional = false)
    @JoinColumn(name = "parent")
    private Parent parent;

}

And the Parent entity :

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
@Table(name = "parent")
public class Parent {

    @Id
    private Long   id;
    @Column
    private String something;

}

You can find a complete example of running code here : https://github.com/Alexandre-Carbenay/demo-jpa-onetoone

Is there a way to avoid the 1+n queries when getting the list of Child entities with Parent ?

3

There are 3 best solutions below

0
Alexandre Carbenay On BEST ANSWER

I finally found a better solution than JOIN FETCH that also works with QueryDsl, using @EntityGraph annotation on repository methods.

Here is the updated Child definition :

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
@NamedEntityGraph(name = "Child.withParent", attributeNodes = @NamedAttributeNode("parent"))
@Table(name = "child")
public class Child {
    @Id
    private Long   id;
    @Column
    private String value;
    @OneToOne(optional = false)
    @JoinColumn(name = "parent")
    private Parent parent;
}

And the ChildJpaRepository definition :

public interface ChildJpaRepository extends JpaRepository<Child, Long>, QueryDslPredicateExecutor<Child> {

    @Override
    @EntityGraph("Child.withParent")
    List<Child> findAll();

    @Override
    @EntityGraph("Child.withParent")
    List<Child> findAll(Predicate predicate);

}

Thanks to Simon Martinelli and Vlad Mihalcea for your help

1
Simon Martinelli On

I can reproduce your observations but I see no reason why Hibernate is doing this.

A solution to avoid the queries is to use JOIN FETCH like

select c from Child c join fetch c.parent
5
Vlad Mihalcea On

By default, the @OneToOne and @ManyToOne associations use FetchType.EAGER, and that's why you see the N+1 query issue.

So, the solution is fairly simple, just set the fetch strategy to LAZY:

@OneToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "parent")
private Parent parent;

If you have a bidirectional @OneToOne association, the parent-side cannot be made lazy unless you use bytecode enhancement.