JPA @OneToOne выберите списки с N+1 запросами

Я на самом деле пытаюсь использовать 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

Есть ли способ избежать 1+n запросов при получении списка Child лица с Parent?

3 ответа

Решение

Я наконец нашел лучшее решение, чем 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);

}

Спасибо Саймону Мартинелли и Владу Михалчеа за помощь

Я могу воспроизвести ваши наблюдения, но не вижу причин, почему Hibernate делает это.

Решением, позволяющим избежать запросов, является использование JOIN FETCH как

select c from Child c join fetch c.parent

Как я объяснил в этой статье, по умолчанию @OneToOne а также @ManyToOne использование ассоциаций FetchType.EAGERи вот почему вы видите проблему запроса N+1.

Итак, решение довольно простое, просто установите стратегию выборки в LAZY:

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

Если у вас есть двунаправленный @OneToOne ассоциация, родительская сторона не может быть сделана ленивой, если вы не используете расширение байт-кода. Для получения более подробной информации, ознакомьтесь с моей книгой о высокопроизводительном Java Persistence.

Другие вопросы по тегам