org.hibernate.LazyInitializationException 수정하는 방법-프록시를 초기화 할 수 없습니다-세션 없음


188

다음과 같은 예외가 있습니다.

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)

main에서 다음 줄로 전화를 걸 때 :

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);

        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 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);

        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로 설정하는 것이었지만 세션을 제어하도록 제안 된 이유는 이것을 허용하지 않습니다.

답변:


93

여기서 잘못된 점은 트랜잭션을 커밋 할 때 세션 관리 구성이 세션을 닫도록 설정되어 있다는 것입니다. 다음과 같은 것이 있는지 확인하십시오.

<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;
    }
}

이 코드는 위의 몇 줄의 쿼리 문에서 id가 3 인 모델을 필터링합니다.

더 읽을 거리 :

세션 팩토리 구성

닫힌 세션 문제


1
감사합니다! 내가 제안한 링크 중 하나로서 getCurrentSession () 대신 openSession ()을 사용하여 문제를 해결했지만, 잘못 제안하면
두려워합니다.

2
아마 괜찮을 것입니다. 그러나 세션과 트랜잭션을 완전히 제어 할 수 있도록 몇 가지를 더 읽으십시오. Spring, Hibernate 등과 같은 모든 고급 기술은 동일한 개념으로 작동하기 때문에 기본 사항을 아는 것이 매우 중요합니다.
goroncy

179

Spring을 사용하여 클래스를 @Transactional 로 표시하면 Spring이 세션 관리를 처리합니다.

@Transactional
public class MyClass {
    ...
}

를 사용 @Transactional하면 트랜잭션 전파와 같은 많은 중요한 측면이 자동으로 처리됩니다. 이 경우 다른 트랜잭션 방법을 호출하면 "세션 없음"예외를 피하면서 진행중인 트랜잭션에 참여할 수 있습니다.

경고를 사용하는 경우 @Transactional결과 동작에 유의하십시오. 일반적인 함정에 대해서는 이 기사 를 참조하십시오 . 예를 들어, 명시 적으로 전화하지 않아도 엔터티에 대한 업데이트가 유지 됩니다.save


21
나는이 대답의 중요성을 과장 할 수 없다. 이 옵션을 먼저 사용해 보는 것이 좋습니다.
sparkyspider

6
또한 @EnableTransactionManagement트랜잭션을 사용하려면 구성에 추가 해야합니다. " 다른 트랜잭션 방법이 호출되면이 방법은 진행중인 트랜잭션에 참여할 수있는 옵션을 갖게됩니다. "이 동작은 트랜잭션이 구현되는 방식, 즉 인터페이스 프록시 대 클래스 프록시 또는 AspectJ 직조와는 다릅니다. 설명서를 참조하십시오 .
Erik Hofer

1
Spring Transactional주석은 트랜잭션 수정뿐만 아니라 트랜잭션에만 액세스하는 것이 좋습니다.
Stephane

8
테스트를 위해서만 클래스 상단에이 주석을 사용하는 것이 좋습니다. 실제 코드는 각 메소드를 클래스의 트랜잭션으로 별도로 표시해야합니다. 클래스의 모든 메소드가 데이터베이스에 대한 트랜잭션과의 열린 연결을 요구하지 않는 한.
m1ld

1
@Transactional 대신 @Transactional (readOnly = true)을 넣는 것이 안전하지 않습니까?
Hamedz 2016 년

105

당신은 설정하려고 할 수 있습니다

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

hibernate.cfg.xml 또는 persistence.xml에서

이 속성에서 명심해야 할 문제는 여기에 잘 설명되어 있습니다.


8
그 의미도 설명해 주시겠습니까?
Mohit Kanwar

2
나는 이것이 무엇을하는지 궁금합니다. 문제가 해결되었지만 그 이유를 알고 싶습니다.
Hassan

3
persistence.xml의 경우 :<property name="hibernate.enable_lazy_load_no_trans" value="true"/>
ACV

33
당신은 그것이 hibernate.enable_lazy_load_no_trans안티 패턴 이라는 것을 알고 있습니까?
Vlad Mihalcea

6
봄이 당신의 거래를 관리 할 경우이 부동산을 사용하지 마십시오.이 부동산은 거래의
폭파를 초래할 것이며, 간단히

54

를 처리하는 가장 좋은 방법LazyInitializationExceptionJOIN FETCH지시문 을 사용하는 것입니다 .

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

어쨌든, 일부 답변에서 제안한 다음 안티 패턴을 사용하지 마십시오.

때로는 DTO 돌기가 가져 오는 실체보다 더 나은 선택이 될 것입니다,이 방법은, 당신은 어떤을받지 않습니다 LazyInitializationException.


어떤 통화에 문제가 있는지 어떻게 알 수 있습니까? 통화를 식별하기가 어렵다는 것을 알게되었습니다. 어떤 방법이 있습니까? 테스트 목적으로 사용 FetchType=EAGER했지만 올바른 해결책이 아닙니다.
Shantaram Tupe

로깅 만 사용하십시오. 그리고 EAGER는 나쁘다.
Vlad Mihalcea

그런 다음 @Transactional서비스 를 종료하기 전에 DTO를 사용하거나 모든 연결을 초기화해야합니다 .
Vlad Mihalcea

2
최선의 기업 관행을 옹호해야하지만 빠른 해결책은 아닙니다.
etlds

21

아래 주석에 대한 일대 다 관계에 대해 동일한 오류가 발생했습니다.

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

fetch = FetchType.EAGER를 추가 한 후 아래와 같이 변경되었습니다.

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

26
예, 문제가 해결되었지만 이제 전체 데이터 트리를로드하고 있습니다. 이것은 대부분의 경우 성능에 부정적인 영향을 미칠 것입니다
astro8891


9

호출 할 때이 예외 session.getEntityById()는 세션이 닫힙니다. 따라서 엔터티를 세션에 다시 연결해야합니다. 또는 쉬운 솔루션은 구성이다 default-lazy="false" 당신에 entity.hbm.xml또는 주석을 사용하는 경우 만 추가 @Proxy(lazy=false)엔터티 클래스.


5

같은 문제가 발생했습니다. 이 문제를 해결하는 또 다른 방법은 다음과 같이 모델에서 요소를 가져 오기 위해 쿼리를 변경할 수 있다는 것입니다.

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

4

이것은 액세스하려는 오브젝트가로드되지 않았으므로 액세스하려는 오브젝트를 결합 페치 하는 조회를 작성하십시오 .

예 :

ObjectA에서 ObjectB를 가져 오려고하는 경우 ObjectB는 ObjectA의 외래 키입니다.

검색어 :

SELECT objA FROM ObjectA obj JOIN FETCH obj.objectB objB

3

이 오류를 광범위하게 처리하는 몇 가지 좋은 답변이 있습니다. Spring Security에서 특정 상황에 부딪 쳤습니다.

사용자 인증 중 (로그인하고 인증을 전달한 직후) SimpleUrlAuthenticationSuccessHandler를 확장하는 사용자 정의 클래스의 특정 권한에 대한 사용자 엔티티를 테스트하고있었습니다.

내 사용자 엔티티는 UserDetails를 구현하고 "org.hibernate.LazyInitializationException-프록시를 초기화 할 수 없음-세션 없음"예외를 발생시키는 지연로드 된 역할 세트를 가지고 있습니다. 해당 설정을 "fetch = FetchType.LAZY"에서 "fetch = FetchType.EAGER"로 변경하면이 문제가 해결되었습니다.



2

다른 사용 사례에서 동일한 예외에 직면했습니다.

여기에 이미지 설명을 입력하십시오

사용 사례 : DTO 프로젝션으로 DB에서 데이터를 읽으십시오 .

솔루션 : load 대신 get 메소드를 사용하십시오 .

일반 작업

public class HibernateTemplate {
public static Object loadObject(Class<?> cls, Serializable s) {
    Object o = null;
    Transaction tx = null;
    try {
        Session session = HibernateUtil.getSessionFactory().openSession();
        tx = session.beginTransaction();
        o = session.load(cls, s); /*change load to get*/
        tx.commit();
        session.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return o;
}

}

퍼시스턴스 클래스

public class Customer {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Id")
private int customerId;

@Column(name = "Name")
private String customerName;

@Column(name = "City")
private String city;

//constructors , setters and getters

}

CustomerDAO 인터페이스

public interface CustomerDAO 
     {
   public CustomerTO getCustomerById(int cid);
     }

엔터티 전송 객체 클래스

public class CustomerTO {

private int customerId;

private String customerName;

private String city;

//constructors , setters and getters

}

팩토리 클래스

public class DAOFactory {

static CustomerDAO customerDAO;
static {
    customerDAO = new HibernateCustomerDAO();
}

public static CustomerDAO getCustomerDAO() {
    return customerDAO;
}

}

엔터티 별 DAO

public class HibernateCustomerDAO implements CustomerDAO {

@Override
public CustomerTO getCustomerById(int cid) {
    Customer cust = (Customer) HibernateTemplate.loadObject(Customer.class, cid);
    CustomerTO cto = new CustomerTO(cust.getCustomerId(), cust.getCustomerName(), cust.getCity());
    return cto;
}

}

데이터 검색 : 테스트 클래스

CustomerDAO cdao = DAOFactory.getCustomerDAO();
CustomerTO c1 = cdao.getCustomerById(2);
System.out.println("CustomerName -> " + c1.getCustomerName() + " ,CustomerCity -> " + c1.getCity());

현재 데이터

여기에 이미지 설명을 입력하십시오

Hibernate System에 의해 생성 된 질의 및 출력

최대 절전 모드 : customer0_.Id를 Id1_0_0_로, customer0_.City를 City2_0_0_로, customer0_.Name을 Name3_0_0_으로 CustomerLab31 customer0_에서 선택하십시오. customer0_.Id =?

CustomerName-> Cody, CustomerCity-> LA


1

당신이 사용하는 경우 Grail's프레임 워크를, 그것의 간단 해결하는 게으른 초기화 예외를 사용하여 Lazy도메인 클래스의 특정 필드에 키워드를.

예를 들면 :

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

자세한 정보는 여기를 참조 하십시오


1

제 경우에는 잘못 배치 되어이 session.clear()문제가 발생했습니다.


1

이는 코드에서 JPA 또는 최대 절전 모드를 사용하고 비즈니스 로직 트랜잭션을 수행하지 않고 DB에서 수정 작업을 수행하고 있음을 의미합니다. 이에 대한 간단한 해결책은 코드를 @Transactional로 표시하는 것입니다.



-2

lazy = false를 * .hbm.xml 파일에 추가하여 문제를 해결하거나 db에서 객체를 가져올 때 Hibernate.init (Object)에서 객체를 초기화 할 수 있습니다


10
일반적으로 lazy = false를 추가하는 것은 좋은 생각이 아닙니다. 게으른 기본적으로 사실입니다 이유입니다
MoienGK

OP는 분명히 그렇게 할 수 없다고 말했다.
GingerHead

-2

servlet-context.xml 에서 다음 변경을 수행하십시오 .

    <beans:property name="hibernateProperties">
        <beans:props>

            <beans:prop key="hibernate.enable_lazy_load_no_trans">true</beans:prop>

        </beans:props>
    </beans:property>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.