一对一映射触发N+1次查询

Ric*_*rdo 0 java mysql spring hibernate jpa

我正在尝试按照https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/进行 @OneToOne 映射,映射本身有效,但它触发N+1查询问题。

正在对父实体服务进行查询并触发 N+1 查询。 如何改进此代码以仅进行 1 个查询?在这种情况下,我们不需要访问 ParentDetails。

编辑:我尝试过使用 JPQL,但LEFT JOIN FETCH ParentDetails也不起作用。

EDIT2:只是尝试添加更多信息。我在 getParentDetails 上设置了一个断点,只是为了确保我没有在任何地方调用 getter,并且我没有调用并仔细检查,这似乎是存储库调用上的连接问题。

我们来看代码:

家长

@Entity
@DynamicUpdate
public class Parent {

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Long id;
   @OneToOne(fetch = FetchType.LAZY,
        cascade = CascadeType.ALL,
        mappedBy = "parent")
   private ParentDetails parentDetails;

 // Getters, setters, etc omitted for brevity
}
Run Code Online (Sandbox Code Playgroud)

家长详情

@Entity
public class ParentDetails {

    @Id
    private Long id;
    @OneToOne(fetch = FetchType.LAZY)
    @MapsId
    private Parent parent;

    // Getters, setters, etc omitted for brevity

Run Code Online (Sandbox Code Playgroud)

父详细信息存储库

@Repository
public interface ParentRepository extends JpaRepository<Parent, Long> {

    Page<Parent>findByNameOrderByName(@Param("name") final String name,final Pageable pageable);
}
Run Code Online (Sandbox Code Playgroud)

Tho*_*sen 5

Hibernate 执行额外的查询,因为父实体不映射外键列。Hibernate 不支持关联一端的延迟获取。当Hibernate实例化Parent对象时,它需要检查是否需要初始化与代理或空对象的关联。在某些时候,团队决定,如果他们无论如何都被迫执行查询,他们将获取关联的实体。我在这里更详细地解释了这一点:https://thorben-janssen.com/hibernate-tip-lazy-loading-one-to-one

如果您想避免额外的查询,则需要在 ParentDetails 和 Parent 之间建立单向关联模型。在您的示例中,这意味着您需要从 Parent 实体中删除 ParentDetails 属性。

@Entity
@DynamicUpdate
public class Parent {

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Long id;

 // Getters, setters, etc omitted for brevity
}
Run Code Online (Sandbox Code Playgroud)

由于您的 ParentDetails 实体使用与 Parent 实体相同的 id 值,因此您不需要双向关联。如果您想获取 Parent 实体的 ParentDetails 实体,可以通过调用 em.find(...) 方法来获取它

Parent p = // fetch the parent object ... 
ParentDetails pd = em.find(p.getId(), ParentDetails.class);
Run Code Online (Sandbox Code Playgroud)