Как я могу условно загрузить коллекцию, сопоставленную в объекте? - PullRequest
3 голосов
/ 23 февраля 2012

Учитывая следующее отображение

<class name="com.domain.Season" table="cm.pub.jsn_mstr">
    <id name="seasonCode" column="season_code" length="1"/>
    <property name="name" type="string" column="name" length="20"/>
    <set name="promotions" lazy="false">
        <key column="season_code"/>
        <one-to-many class="com.domain.Promotion" not-found="ignore"/>
    </set>
</class>

Как я могу включить или исключить нагрузку promotions? Я мог бы использовать lazy="true", хотя я использую Джексона для сериализации результата, который происходит после закрытия сессии.

public Collection<Season> getSeasons(boolean withPromotions) {
    final Session session = sessionFactory.getCurrentSession();
    try {
        session.beginTransaction();
        return (List<Season>) session.createQuery("from Season s").list();
    } finally {
        session.getTransaction().commit();
    }
}

ОБНОВЛЕНИЕ : проблема с отложенной загрузкой.

Метод getSeasons, описанный выше, используется в контроллере MVC, который будет извлекать сезоны, а затем с помощью Джексона сериализовать их в JSON (используя средство разрешения представления Spring / MVC), поэтому я на самом деле не получаю доступ к объектам сам, поэтому любая попытка ленивая загрузка коллекции приводит к исключению (поскольку Джексон вызовет итератор для всех свойств коллекции).

Вот пример, показывающий, что будет сгенерировано исключение:

public Collection<Season> getSeasons(boolean withPromotions) {
    final Session session = sessionFactory.getCurrentSession();
    final List<Season> r;
    try {
        session.beginTransaction();
        r = (List<Season>) session.createQuery(
                withPromotions
                ? "from Season s join fetch s.promotions"
                : "from Season s"
                ).list();
    } finally {
        session.getTransaction().commit();
    }
    try {
        for (Season s : r) {
            for (Promotion p : s.getPromotions()) {
                // Exception thrown here as we attempted to get an iterator.
                LOG.debug("Promotion: " + p.getName());
            }
        }
    } catch (Exception ex) {
        LOG.error("Couldn't get promotions", ex);
    }
    return r;
}

И, конечно, на этот раз отображение должно иметь lazy="true", иначе оно всегда будет стремиться прочитать коллекцию.

<class name="com.domain.Season" table="cm.pub.jsn_mstr">
    <id name="seasonCode" column="jsn_seas" length="1"/>
    <set name="promotions" lazy="true">
        <key column="jpr_seas"/>
        <one-to-many class="com.domain.Promotion" not-found="ignore"/>
    </set>
</class>

Тип данных для поля promotions: Collection<Promotion>.

Ответы [ 5 ]

3 голосов
/ 23 февраля 2012

Попробуйте это:

session.createQuery("from Season s join fetch s.promotions").list();

Из ссылки на спящий режим: соединение "извлечение" позволяет инициализировать ассоциации или коллекции значений вместе с их родительскими объектами, используя один выбор.

2 голосов
/ 23 февраля 2012

Вы можете использовать Hibernate.initialize() для инициализации коллекции из прокси. В вашем примере добавьте следующий код.

public Collection<Season> getSeasons(boolean withPromotions) {
    final Session session = sessionFactory.getCurrentSession();
    try {
        session.beginTransaction();
        List<Season> list = session.createQuery("from Season s").list();
        for(Season s : list){
            if(condition){
               Hibernate.initialize(s.getPromotions());
            }
        }
        return list;


    } finally {
        session.getTransaction().commit();
    }
}

Из документации API это будет

Статические методы Hibernate.initialize () и Hibernate.isInitialized () предоставляют приложению удобный способ работы с лениво инициализированными коллекциями или прокси. Hibernate.initialize (cat) будет принудительно инициализировать прокси, cat, пока его Сессия еще открыта. Hibernate.initialize (cat.getKittens ()) имеет аналогичный эффект для коллекции котят.

Прочитайте API здесь или пример здесь .

0 голосов
/ 04 декабря 2014

Извините, это очень поздний ответ, но я только нашел ваш вопрос, когда искал что-то еще.

Как уже указывалось в некоторых ответах, проблема в том, что Hibernate использует "заполнитель" PersistentCollection для отложенной загрузки коллекции. Чтобы остановить Hibernate, пытаясь заполнить коллекцию при обращении к ней, вы можете null вывести фактический список Promotions в каждом из Season объектов, возвращаемых запросом. В коде что-то вроде этого:

if (!withPromotions) {
    for (Season s : r) {
        s.setPromotions(null);
    }
}

Установка Promotions в ноль очистит ссылку на объект коллекции Hibernate. Вы должны быть в состоянии сделать это вне сеанса.

Что мы делаем, это создаем методы для наших сущностей для каждой из наших коллекций, аналогично следующему:

public boolean isPromotionsLoaded() {
    if (((promotions instanceof org.hibernate.collection.PersistentCollection)
            && !((org.hibernate.collection.PersistentCollection) promotions).wasInitialized())
            || (getPromotions() == null)) {
        return false;
    } else {
        return true;
    }
}

Затем мы можем использовать эти методы, чтобы выяснить, можем ли мы безопасно получить доступ к коллекции.

В любом случае, надеюсь, это кому-нибудь поможет.

0 голосов
/ 29 сентября 2012

По сути, ваша проблема в Джексоне - читать все в бобе.Не Hibernate.

Либо:

1) Правильно управляйте Джексоном, для передачи данных, которые вам действительно нужны - и избегайте того, что вы не хотите, чтобы они «тянули»on '.

2) Используйте Spring's OpenSessionInViewFilter или аналогичный, который связывает сессию с локальным потоком и оставляет его открытым до тех пор, пока представление не запустится - что позволит Джексону успокоитьсяпотяните за 'и загрузите коллекции на этапе рендеринга вида.

3) или, сопоставьте два разных класса (одна полная информация, один облегченный) в одну и ту же таблицу и получитетот, который вам действительно нужен.

SeasonLite может быть предком Season (полная информация), но учтите, что вы не отображаете наследование с Hibernate - вы отображаете два отдельных представления одной и той же таблицы.

PS: такие методы, как 'getSomeData (boolean retrieveThisData)', по-видимому, являются распространенным анти-паттерном.Я видел это много раз раньше.

0 голосов
/ 25 февраля 2012

Я нашел единственный способ сделать это - не отображать коллекцию, а вместо этого отправлять два запроса и добавлять в коллекцию сам.

Это дает мне больший контроль над ассоциацией, а также повышает производительность, поскольку я отправляю только два запроса вместо 1 + n (где n - строки, полученные из первого запроса).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...