8

我实际上是在尝试使用 JPA@OneToOne注释将Child实体链接到它的Parent.

它运行良好,除了在获取Childs 列表时,JPA 引擎(在本例中为 Hibernate)进行 1+n 次查询。

这是 Hibernate 查询的日志:

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=?

使用完全相同的实体定义,特别是当我得到一个孩子时,JPA 使用预期的 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=?

这是Child实体定义:

@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;

}

Parent实体:

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

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

}

您可以在此处找到运行代码的完整示例: https ://github.com/Alexandre-Carbenay/demo-jpa-onetoone

Child获取实体列表时有没有办法避免 1+n 查询Parent

4

3 回答 3

5

我终于找到了一个更好的解决方案,JOIN FETCH它也适用于 QueryDsl,@EntityGraph在存储库方法上使用注释。

这是更新的Child定义:

@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;
}

ChildJpaRepository定义:

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);

}

感谢 Simon Martinelli 和 Vlad Mihalcea 的帮助

于 2017-12-13T10:46:55.233 回答
1

默认情况下,@OneToOne@ManyToOne关联使用FetchType.EAGER,这就是您看到N+1 查询问题的原因。

因此,解决方案相当简单,只需将获取策略设置为 LAZY:

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

如果你有一个双向@OneToOne关联,除非你使用字节码增强,否则父端不能懒惰。

于 2017-12-12T08:27:49.817 回答
0

我可以重现您的观察结果,但我看不出 Hibernate 这样做的原因。

避免查询的解决方案是使用 JOIN FETCH

select c from Child c join fetch c.parent
于 2017-12-12T08:17:41.697 回答