С нетерпением загружает вложенную ассоциацию, используя HQL

#java #hibernate #jpa #one-to-many #eager-loading

#java #спящий режим #jpa #один ко многим #нетерпеливая загрузка

Вопрос:

У меня есть следующая модель:

 public class BaseModel {
  List<DataA> lazyCollectionA;
  List<DataB> lazyCollectionB;
}

public class DataA {
  OtherEntity otherEntity;
}

public class OtherEntity {
  List<DataC> lazyCollectionC;
}
  

Когда я посещаю определенную страницу, мне нужно использовать все эти данные. Это создает проблему выбора производительности n 1.

Я уже частично решил проблему, охотно извлекая коллекции с помощью:

 List<BaseModel> result = entityManager.createQuery(
    "select m from BaseModel m "  
    "left join fetch m.lazyCollectionA "  
    "where m.id in (:ids) ", BaseModel.class)
.setParameter("ids", ids)
.getResultList();

result = entityManager.createQuery(
    "select m from BaseModel m "  
    "left join fetch m.lazyCollectionB "  
    "where m.id in (:ids) ", BaseModel.class)
.setParameter("ids", ids)
.getResultList();
  

Обратите внимание, что мне пришлось выполнить 2 запроса вместо только 1, потому что в противном случае я бы получил MultipleBagFetchException .

Однако у меня возникают проблемы с загрузкой. lazyCollectionA.otherEntity.lazyCollectionC Я попробовал несколько вариантов запроса, чтобы попытаться быстро получить результаты, но при otherEntity.lazyCollectionC доступе проблема выбора n 1 продолжает всплывать.

Я думаю, что это должно сработать, но, к сожалению, это не так:

 entityManager.createQuery(
    "select a from BaseModel m "  
    "left join m.lazyCollectionA a "  
    "left join fetch a.otherEntity o "  
    "left join fetch o.lazyCollectionC "  
    "where m.id in (:ids) ", BaseModel.class)
.setParameter("ids", ids)
.getResultList();
  

У вас есть какие-либо идеи, почему это не работает?

Кроме того, я не совсем понимаю, как загружаются lazyCollectionA и работают мои первые 2 запроса lazyCollectionB . Я имею в виду, поскольку они загружаются в разное время, я бы ожидал, что только последний запрос будет иметь загруженные экземпляры. Это потому, что hibernate кэширует результаты, и поэтому ему не нужно снова запрашивать базу данных?

Спасибо за любую помощь, которую вы можете предоставить!

Ответ №1:

Я предполагаю, что все соединения между вашими моделями являются @OneToMany. В этом случае вы могли бы попробовать что-то вроде этого:

 @Autowired
private EntityManager em;

@Transactional
public List<BaseModel> getAllByThreeQueries() {
    List<Long> ids = Arrays.asList(1L);
    List<BaseModel> first = em.createQuery(
            "select distinct m from BaseModel m "  
                    "left join fetch m.lazyCollectionB "  
                    "where m.id in (:ids) ", BaseModel.class)
            .setParameter("ids", ids)
            .getResultList();
    List<BaseModel> second = em.createQuery(
            "select distinct m from BaseModel m "  
                    "left join fetch m.lazyCollectionA a "  
                    "left join fetch a.otherEntity o "  
                    "where m in (:models) ", BaseModel.class)
            .setParameter("models", first)
            .getResultList();
    em.createQuery("select distinct a from BaseModel m "  
            "left join m.lazyCollectionA a "  
            "left join fetch a.otherEntity o "  
            "left join fetch o.lazyCollectionC "  
            "where m in (:models) ", DataA.class)
            .setParameter("models", second)
            .getResultList();
    return second;
}
  

Полный код

У вас есть какие-либо идеи, почему это не работает?

 entityManager.createQuery(
    "select a from BaseModel m "  
    "left join m.lazyCollectionA a "  
    "left join fetch a.otherEntity o "  
    "left join fetch o.lazyCollectionC "  
    "where m.id in (:ids) ", BaseModel.class)
.setParameter("ids", ids)
.getResultList();
  

Потому что в этом случае вы получаете исключение MultipleBagFetchException. Вам нужно выполнить еще один запрос.

Комментарии:

1. Спасибо за ваш вклад! К сожалению, у меня это не сработало. Запросы select n 1 для выборки lazyCollectionC все еще выполняются. Я заметил, что эти запросы используют WHERE dataC.otherEntity_id = ? . Я попытался воспроизвести это предложение where, но n 1 запросов все равно появляются.

2. Также я попытался получить доступ к ссылке на полный код, которую вы опубликовали, но она выдает ошибку 404 :/

3. Извините, я забыл открыть репозиторий. Теперь я это сделал. Вы можете просмотреть ее.

4. Не могли бы вы предоставить больше информации о своей среде? Например, метод полного запроса, отношения между моделями, аннотации классов или методов или полей?