JPA @OneToOne выберите списки с N+1 запросами
Я на самом деле пытаюсь использовать JPA @OneToOne
аннотация, чтобы связать Child
сущность к своему Parent
,
Это работает хорошо, за исключением того факта, что при получении списка Child
s, движок 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.