В моем проекте у меня есть три сущности:
@Entity
public class A {
@Id
private int id;
}
@Entity
public class B {
@Id
private int id;
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, optional = false)
@OnDelete(action = OnDeleteAction.CASCADE)
private A a;
}
@Entity
public class C {
@Id
private int id;
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, optional = false)
@OnDelete(action = OnDeleteAction.CASCADE)
private B b;
}
Как вы можете видеть, C
имеет tansitive ManyToOne-Relationship с A
Теперь я хочу получить все C
с конкретным A
используя мой JpaRepository
:
public interface CRepository extends JpaRepository<C, Integer> {
List<C> findAllByBA(A a);
}
Это приводит к следующему запросу Hibernate sql (что хорошо):
Hibernate: выберите... из c c0_ cross join b b1_, где c0_.b_id = b1_.id и b1_.a_id =?
Но потом я получаю еще один запрос для каждого отдельного B
(который может быть несколько тысяч, так что это не нормально):
Hibernate: выберите... из b b0_ internal присоедините a1_ к b0_.a_id = a1_.id где b0_.id =?
Очевидно, что это можно решить с помощью единственного соединения, используя все три таблицы. Но как мне сказать Hibernate сделать это, не теряя удобства абстракции с JpaRepository
?
Дополнительные, нежелательные запросы вызваны тем, что ассоциация нетерпеливо. Чтобы избежать загрузки B, вы должны сделать объединение ленивым:
@ManyToOne(fetch: FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, optional = false)
Я вообще делаю каждую ассоциацию ленивой. Когда вас не интересует Bs, или не знаете заранее, если загрузка Bs необходима, чтобы ассоциация ленила, избегала их загрузки. И если вы знаете, что хотите, чтобы Bs загружался вместе с Cs, вы просто загружаете их явно в свой запрос:
@Query("select c from C c left join fetch c.b where c.b.a = ?")