Справочная информация: Spring-boot Application (2.1.2.RELEASE), использующий JPA, реализованный Hibernate (5.3.7.Final) для сохранения.
Я за конкретную c попытку варианта использования для эффективной загрузки большой древовидной сущности, которая выглядит примерно так, где у родителя есть oneToMany для потомков, а у потомков есть несколько наборов данных oneToMany.
@Table(name = "parent")
public class Parent implements Serializable {
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "parent")
private Collection<Child> children = new ArrayList<>();
}
@Table(name = "child")
public class Child implements Serializable {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
private Parent parent;
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "child_id")
private Collection<Data1> data1= new ArrayList<>();
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "child_id")
private Collection<Data2> data2 = new ArrayList<>();
}
@Table(name = "data1")
public class Data1 implements Serializable {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "child_id")
private Child child;
...data fields...
}
Data2 similar to Data1.
Загрузка родителя (P) и затем выполнение этой конкретной операции будет проходить по большей части / всем данным в дереве, заставляя меня думать, что я должен просто загрузить данные, которые, по моему мнению, мне нужны, как можно быстрее. Наборы данных велики, но недостаточно велики, чтобы проблема памяти стала проблемой. Вариант использования доступен только для чтения.
Сначала я подумал об использовании LEFT JOIN FETCH, чтобы удостовериться, что получил базовые коллекции, но, учитывая, что некоторые из них, которые я получил, были MultipleBagFetchExceptions.
Тогда я планировал использовать кэш первого уровня, просто загрузив все нужные мне записи и позволив Hibernate отсортировать все остальное. В методе javax.transactional.Transactional, я делаю это:
Session s = entityManagerFactory.unwrap(SessionFactory.class).getCurrentSession();
Query d1Query = s.createQuery("SELECT d1 FROM Data1 d1"
+ " INNER JOIN d1.child c"
+ " WHERE c.parent.id = :pId");
d1Query.setParameter("pId", id);
sqlLogger.debug("Pre-fetched " + d1Query.getResultList().size() + " Data1");
... d2Query in the same way ...
Query pQuery = s.createQuery("SELECT p FROM Parent p"
+ " LEFT JOIN FETCH p.children"
+ " WHERE p.id = :id");
pQuery.setParameter("id", id);
Parent p = (Parent) pQuery .getSingleResult();
doStuff(p);
Теперь кажется, что запросы ничего не помещают в кеш, так как, когда я начинаю проходить данные, я получаю отдельные запросы для каждой записи Data1 / 2 (хотя JOIN FETCH делает трюк для списка Children в классе Parent). Поскольку эти числа исчисляются тысячами, это быстро делает поиск невозможным.
Я предполагаю, что это означает, что только загрузка (clazz, id) помещает объекты в кэш первого уровня? Что я могу сделать в моем случае? Что я делаю не так?