Hibernate зависает или выбрасывает ленивую инициализацию ни один сеанс или сеанс не был закрыт - PullRequest
12 голосов
/ 30 мая 2011

Я улучшаю старое приложение Spring / Hibernate, и я застрял. У меня есть метод, который читает файл длиной более 3000 строк, в каждой строке есть запись, которую нужно сравнить с чем-то в базе данных, а затем добавить в базу данных регистр (таблица «многие ко многим»).

Таблицы и отношения

Филиал имеет много Продукт , Продукт находится в много филиалов .

Продукты имеют много продуктов , а Категория имеет много продуктов

И есть еще таблицы, которые были там и работают нормально.

Созданные мной новые таблицы / объекты: Branch, Product, BranchToProduct .

Продукты имеют набор объектов BranchToProduct , которые имеют 3 поля

Мне нужно добавить объекты BranchToProduct в набор Product с 3 полями, заполненными информацией, которую я получаю из каждой строки файла.

Я добавляю простую строку и приложение выдает:

product = productDAO.findByModel (stringModel);

не удалось лениво инициализировать Коллекция ролей: com.bamboo.catW3.domain.Product.products, ни один сеанс или сеанс не был закрыт

Если я перехожу к отображению гибернации (файл hbm) и устанавливаю отношение product_to_products lazy = false, строка работает нормально, но если я пытаюсь поместить ее в файловый цикл, приложение всегда зависает на 18-й строке, обрабатываемой , не имеет значения, какой файл я использую или порядок содержимого, консоль перестает работать, приходится закрывать Java, убивая процесс.

В любом случае, при отладке я получаю много HQL для простого поиска, 13 строк HQL, пока не получу ошибку, когда lazy = true, и МНОГО строк, когда я использую lazy = false и положу его на цикл.

Думаю, мне следует попытаться исправить проблему с помощью lazy = true.

Эта ситуация заставляет меня задуматься:

1.- Когда ленивый = правда. Почему я не могу запустить одну строку этой команды этого метода, но он отлично работает на других методах класса?

кстати, это класс с именем CatalogFacade, который реализует методы других классов: (CategoryFacade, ContainerFacade, ProductFacade, ProductOptionFacade, ProductStatusFacade, UserFacade, EmailFacade, FileFacade, BranchOfficeFacade)

Это код для
productDao.find ():

public Product find(Integer id) throws DataAccessException {

        Product product= (Product) super.find(Product.class, id);


        if(product!=null){
            product.setProductAttributes(new TreeSet<ProductAttribute>(product.getProductAttributes()));

            for (Product ptp : product.getProducts()){
               ptp.setProductAttributes(new TreeSet<ProductAttribute>(ptp.getProductAttributes()));

             }

         } 

Исключение было выброшено прямо в этой строке, в конце концов для:

pptp.setProductAttributes(new TreeSet<ProductAttribute>(ptp.getProductAttributes()))  

в отладчике Intelij я вижу объект, неправильно сформированный из запроса:

product.getProducts () = {org.hibernate.collection.PersistentSet@4312 Eventunable для оценки метода выражения, вызвавшего исключение org.hibernate.LazyInitializationException.

Как бы то ни было, другие атрибуты в порядке. У этого продукта даже нет других продуктов в базе данных.

UPDATE

Глубоко копаясь в ситуации, внутри

product.find (INT)

В строке перед тем, как я получу исключение, мы можем увидеть в отладке, что в массиве product.products есть ошибка, вместо значения, которое вы видите в lazyInitialitationException. Как бы то ни было , если я вызову его из другого метода, массив будет находить. Так что он не может быть внутри него ДАЖЕ, хотя метод получает только целое число.

Кроме того, мы обнаружили, что это происходило на протяжении всего жизненного цикла приложения, иногда сотрудники повторяли метод, подобный ему, но изменяя его, устанавливая значение NULL для этих поврежденных массивов. Поэтому я на 100% уверен, что это приложение потребляет больше ресурсов, чем должно.

У него есть представления во Flex и более поздние представления в JSTL, где они были созданы, и в зависимости от того, кто вызывает методы, исключения вызываются по-разному для одних и тех же методов.

Добавление дополнительной информации. Вот как produt.find реализован в AbstractDAOImpl:

public final Object find(Class clazz, Integer id) throws DataAccessException{

        return getHibernateTemplate().get(clazz,id);
}

и это моя конфигурация диспетчера транзакций, метод аннотации, описанный в первом ответе по fillip, не работал:

<bean id="catalogFacade" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager">
        <ref local="transactionManager"/>
    </property>
    <property name="target">
        <ref local="catalogFacadeTarget"/>
    </property>
    <property name="transactionAttributes">
        <props>
            <prop key="add*">PROPAGATION_REQUIRED</prop>
            <prop key="save*">PROPAGATION_REQUIRED</prop>
            <prop key="update*">PROPAGATION_REQUIRED</prop>
            <prop key="delete*">PROPAGATION_REQUIRED</prop>
            <prop key="remove*">PROPAGATION_REQUIRED</prop>
            <prop key="get*">PROPAGATION_SUPPORTS,readOnly</prop>
            <prop key="find*">PROPAGATION_SUPPORTS,readOnly</prop>
            <prop key="contains*">PROPAGATION_SUPPORTS,readOnly</prop>
            <prop key="login*">PROPAGATION_SUPPORTS,readOnly</prop>
        </props>
    </property>
</bean>

Ответы [ 3 ]

24 голосов
/ 30 мая 2011

Вы получаете ленивое исключение при инициализации, потому что ваш сеанс закрывается, прежде чем вы получите доступ к переменным-членам Product. Когда выполняется следующая строка:

Product product= (Product) super.find(Product.class, id)

Hibernate открывает сеансы, получает то, что вы ищете, затем закрывает затем сеанс. Любые поля, которые имеют lazy = true, не извлекаются в это время; вместо этого эти поля заполняются прокси. Когда вы пытаетесь получить фактическое значение объекта прокси, он попытается вернуться в базу данных, используя активный сеанс для получения данных. Если сеанс не может быть найден, вы получите исключение, которое видите. Установка lazy = true имеет преимущества, поскольку предотвращает немедленную загрузку всего графа объектов; вложенные объекты оставляются в покое, пока вы специально не попросите их

Существует два распространенных метода решения вашей проблемы. Первое, что вы уже определили, это установка lazy = false. Это хорошо, если продукт всегда имеет атрибуты продукта, и вы обычно используете продукт и его атрибуты вместе. Если вам часто нужен только объект Product без его атрибутов, вы создаете ненужную загрузку базы данных.

Второй способ - пометить метод как транзакционный с использованием аннотаций Spring.

@Transactional
public Product find(Integer id) throws DataAccessException {

}

Несколько заметок:

  1. Вам нужны дополнительные конфигурация для транзакций в работа (аннотация @Transactional не достаточно). Смотрите здесь для больше информации.
  2. В соответствии с передовым опытом вы должны аннотировать методы на уровне обслуживания, а не на уровне доступа к данным.
0 голосов
/ 26 марта 2015

Замените метод загрузки на метод get ..

Я выяснил, проведя дополнительные исследования, что метод load в действительности не загружает объект из базы данных. Вместо этого он автоматически возвращает прокси-объект. Загрузка предполагает, что объект уже «получен с get» из базы данных и находится в кеше.

Просто используйте get вместо load, если вы хотите убедиться, что вы попали в базу данных, и убедитесь, что вы знаете разницу между этими двумя методами.

Источник: Комментарий этого весеннего форума

Я лично проверил это, и это правильно, метод загрузки не получает все нужные данные из БД. Использование исправить мою проблему.

0 голосов
/ 30 мая 2011

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

getHibernateTemplate().loadAll(class)

для получения чего-либо и

getHibernateTemplate().get(class, id) 

чтобы найти одну вещь. Оба из тех я использую без проблем. Я обнаружил, что .find () дает мне, что сессия была закрыта ошибка.

Я действительно не выяснил, почему это так.

Единственный другой вариант, который я могу придумать, кроме использования другого метода, - это открывать и закрывать сессии самостоятельно, но я полагаю, что вы предпочитаете не делать этого.

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