问题
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 Child
s, 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
?
回答1:
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
回答2:
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
回答3:
As I explained in this article, 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. For more details, check out my High-Performance Java Persistence book.
来源:https://stackoverflow.com/questions/47754497/jpa-onetoone-select-lists-with-n1-queries