it-roy-ru.com

org.hibernate.LazyInitializationException - не удалось инициализировать прокси - нет сеанса

Я получаю следующее исключение:

Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.Java:167)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.Java:215)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.Java:190)
    at sei.persistence.wf.entities.Element_$$_jvstc68_47.getNote(Element_$$_jvstc68_47.Java)
    at JSON_to_XML.createBpmnRepresantation(JSON_to_XML.Java:139)
    at JSON_to_XML.main(JSON_to_XML.Java:84)

когда я пытаюсь позвонить из основного в следующие строки:

Model subProcessModel = getModelByModelGroup(1112);
System.out.println(subProcessModel.getElement().getNote());

Сначала я реализовал метод getModelByModelGroup(int modelgroupid) так:

    public static Model getModelByModelGroup(int modelGroupId, boolean openTransaction) {

        Session session = SessionFactoryHelper.getSessionFactory().getCurrentSession();     
        Transaction tx = null;

        if (openTransaction)
            tx = session.getTransaction();

        String responseMessage = "";

        try {
            if (openTransaction)            
                tx.begin();
            Query query = session.createQuery("from Model where modelGroup.id = :modelGroupId");
            query.setParameter("modelGroupId", modelGroupId);
            @SuppressWarnings("unchecked")
            List<Model> modelList = (List<Model>)query.list(); 
            Model model = null;
            // Cerco il primo Model che è in esercizio: idwf_model_type = 3
            for (Model m : modelList)
                if (m.getModelType().getId() == 3) {
                    model = m;
                    break;
                }

            if (model == null) {
                Object[] arrModels = modelList.toArray();
                if (arrModels.length == 0) 
                    throw new Exception("Non esiste ");

                model = (Model)arrModels[0];
            }

            if (openTransaction)
                tx.commit();
            return model;

        } catch(Exception ex) {
            if (openTransaction)
                tx.rollback();
            ex.printStackTrace();
            if (responseMessage.compareTo("") == 0)
                responseMessage = "Error" + ex.getMessage();
            return null;        
        }

и получил исключение. Тогда друг предложил мне всегда тестировать сеанс и получать текущий сеанс, чтобы избежать этой ошибки. Итак, я сделал это:

public static Model getModelByModelGroup(int modelGroupId) {

        Session session = null;
        boolean openSession = session == null;
        Transaction tx = null;
        if (openSession){
          session = SessionFactoryHelper.getSessionFactory().getCurrentSession();   
            tx = session.getTransaction();
        }
        String responseMessage = "";

        try {
            if (openSession)            
                tx.begin();
            Query query = session.createQuery("from Model where modelGroup.id = :modelGroupId");
            query.setParameter("modelGroupId", modelGroupId);
            @SuppressWarnings("unchecked")
            List<Model> modelList = (List<Model>)query.list(); 
            Model model = null;
            for (Model m : modelList)
                if (m.getModelType().getId() == 3) {
                    model = m;
                    break;
                }

            if (model == null) {
                Object[] arrModels = modelList.toArray();
                if (arrModels.length == 0) 
                    throw new RuntimeException("Non esiste");

                model = (Model)arrModels[0];

            if (openSession)
                tx.commit();
            return model;

        } catch(RuntimeException ex) {
            if (openSession)
                tx.rollback();
            ex.printStackTrace();
            if (responseMessage.compareTo("") == 0)
                responseMessage = "Error" + ex.getMessage();
            return null;        
        }

    }

но все равно получаю ту же ошибку. Я много читал об этой ошибке и нашел несколько возможных решений. Одним из них было установить для lazyLoad значение false, но мне не разрешено это делать, поэтому мне предложили контролировать сеанс

132
Blerta Dhimitri

Здесь неправильно то, что ваша конфигурация управления сеансом настроена на закрытие сеанса при фиксации транзакции. Проверьте, есть ли у вас что-то вроде:

<property name="current_session_context_class">thread</property> 

в вашей конфигурации.

Чтобы преодолеть эту проблему, вы можете изменить конфигурацию фабрики сеансов или открыть другой сеанс и только потом запрашивать эти лениво загруженные объекты. Но я бы предложил здесь инициализировать эту ленивую коллекцию в самой getModelByModelGroup и вызвать:

Hibernate.initialize(subProcessModel.getElement());

когда вы все еще находитесь в активном сеансе.

И последнее. Дружеский совет. У вас есть что-то вроде этого в вашем методе:

            for (Model m : modelList)
            if (m.getModelType().getId() == 3) {
                model = m;
                break;
            }

Пожалуйста, вставьте этот код, просто отфильтруйте те модели с идентификатором типа, равным 3, в запросе, всего несколькими строками выше. 

Еще немного чтения:

заводская конфигурация сеанса

проблема с закрытой сессией

68
goroncy

Вы можете попробовать установить 

<property name="hibernate.enable_lazy_load_no_trans">true</property>

в hibernate.cfg.xml или persistence.xml

Проблема, которую нужно учитывать с этим свойством, хорошо объяснена здесь

96
Wilianto Indrawan

Если вы используете Spring, пометьте класс как @Transactional , то Spring будет обрабатывать управление сессиями.

@Transactional
public class My Class {
    ...
}

Используя @Transactional, многие важные аспекты, такие как распространение транзакций, обрабатываются автоматически. В этом случае, если вызван другой транзакционный метод, у метода будет возможность присоединиться к текущей транзакции, избегая исключения «без сеанса».

80
user2601995

Лучший способ обработать LazyInitializationException это использовать директиву JOIN FETCH:

Query query = session.createQuery(
    "from Model m " +
    "join fetch m.modelType " +
    "where modelGroup.id = :modelGroupId"
);

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

Иногда DTO-проекция является лучшим выбором, чем выборка объектов, и таким образом вы не получите никакой LazyInitializationException.

37
Vlad Mihalcea

Я получал ту же ошибку для отношений один ко многим для аннотации ниже. 

@OneToMany(mappedBy="department", cascade = CascadeType.ALL)

После добавления fetch = FetchType.EAGER изменилось, как показано ниже, у меня это сработало.

@OneToMany(mappedBy="department", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
9
Smruti R Tripathy

Это исключение из-за того, что при вызове session.getEntityById() сеанс будет закрыт. Таким образом, вам нужно повторно присоединить объект к сеансу. Или простое решение - просто настройте default-lazy="false" для своего entity.hbm.xml или, если вы используете аннотации, просто добавьте @Proxy(lazy=false) к вашему классу сущностей.

7
Reddeiah Pidugu

если вы используете Spring Data JPA, Spring Boot вы можете добавить эту строку в application.properties

spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
5
Shaaban Ebrahim

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

Query query = session.createQuery("from Model m join fetch m.element where modelGroup.id = :modelGroupId")
4
Tony Vu

Здесь есть несколько хороших ответов, которые обрабатывают эту ошибку в широком смысле. С Spring Security я столкнулся с особой ситуацией, в которой было быстрое, хотя, возможно, и не оптимальное решение.

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

Моя пользовательская сущность реализует UserDetails и имеет набор отложенных ролей, которые выдавали исключение «org.hibernate.LazyInitializationException - не удалось инициализировать исключение прокси-сервера - без сеанса». Изменение этого набора с "fetch = FetchType.LAZY" на "fetch = FetchType.EAGER" исправило это для меня.

2
Night Owl

Если вы используете JPQL, использовать JOIN FETCH - самый простой способ: http://www.objectdb.com/Java/jpa/query/jpql/from#LEFT_OUTER_INNER_JOIN_FETCH_

1
xihui

Это означает, что объект, к которому вы пытаетесь получить доступ, не загружен, поэтому напишите запрос, который делает join fetch объекта, к которому вы пытаетесь получить доступ. 

Например:

Если вы пытаетесь получить ObjectB из ObjectA, где ObjectB является внешним ключом в ObjectA.

Запрос:

SELECT objA FROM ObjectA obj JOIN FETCH obj.objectB objB
0
rex roy

Если вы используете Grail's Framework, просто разрешить исключение lazy инициализации с помощью ключевого слова Lazy в определенном поле в классе домена.

Например: 

class Book {
    static belongsTo = [author: Author]
    static mapping = {
        author lazy: false
    }
}

Найти дополнительную информацию здесь

0
Zeb

В моем случае эта проблема возникла из-за неправильной session.clear()

0
Ab Sin

Это означает, что вы используете JPA или hibernate в своем коде и выполняете операцию изменения в БД, не совершая транзакцию бизнес-логики ........ Таким простым решением для этого является пометить ваш кусок кода @Transactional

Благодарю.

0
Aman Goel