So beheben Sie org.hibernate.LazyInitializationException - Proxy konnte nicht initialisiert werden - keine Sitzung

186

Ich bekomme folgende Ausnahme:

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)

wenn ich versuche, von main die folgenden Zeilen anzurufen:

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

Ich habe die getModelByModelGroup(int modelgroupid)Methode zunächst folgendermaßen implementiert :

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

und bekam die Ausnahme. Dann schlug mir ein Freund vor, die Sitzung immer zu testen und die aktuelle Sitzung abzurufen, um diesen Fehler zu vermeiden. Also habe ich das gemacht:

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

aber trotzdem den gleichen Fehler bekommen. Ich habe viel für diesen Fehler gelesen und einige mögliche Lösungen gefunden. Eine davon war, lazyLoad auf false zu setzen, aber ich darf das nicht. Deshalb wurde mir vorgeschlagen, die Sitzung zu steuern

Blerta Dhimitri
quelle

Antworten:

93

Was hier falsch ist, ist, dass Ihre Sitzungsverwaltungskonfiguration so eingestellt ist, dass die Sitzung geschlossen wird, wenn Sie eine Transaktion festschreiben. Überprüfen Sie, ob Sie etwas haben wie:

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

in Ihrer Konfiguration.

Um dieses Problem zu beheben, können Sie die Konfiguration der Sitzungsfactory ändern oder eine andere Sitzung öffnen und nur dann nach diesen verzögert geladenen Objekten fragen. Was ich hier vorschlagen würde, ist, diese faule Sammlung in getModelByModelGroup selbst zu initialisieren und aufzurufen:

Hibernate.initialize(subProcessModel.getElement());

wenn Sie sich noch in einer aktiven Sitzung befinden.

Und noch eine letzte Sache. Ein freundlicher Rat. Sie haben so etwas in Ihrer Methode:

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

Bitte filtern Sie von diesem Code nur die Modelle mit der Typ-ID gleich 3 in der Abfrageanweisung, nur ein paar Zeilen oben.

Noch etwas lesen:

Session Factory-Konfiguration

Problem mit geschlossener Sitzung

Goroncy
quelle
1
Danke dir! Ich habe mein Problem mit openSession () anstelle von getCurrentSession () gelöst, da einer der Links, die Sie mir gegeben haben, dies vorgeschlagen hat, aber jetzt habe ich Angst, wenn es falsch ist
Blerta Dhimitri
2
Nein, es ist wahrscheinlich in Ordnung. Lesen Sie jedoch noch etwas mehr, um Ihre Sitzungen und Transaktionen vollständig steuern zu können. Es ist wirklich wichtig, die Grundlagen zu kennen, da alle übergeordneten Technologien wie Spring, Hibernate und andere nach demselben Konzept arbeiten.
Goroncy
177

Wenn Sie Spring verwenden, markieren Sie die Klasse als @Transactional , dann übernimmt Spring die Sitzungsverwaltung.

@Transactional
public class MyClass {
    ...
}

Durch die Verwendung @Transactionalwerden viele wichtige Aspekte wie die Transaktionsweitergabe automatisch behandelt. In diesem Fall hat die Methode die Möglichkeit, sich der laufenden Transaktion anzuschließen, wenn eine andere Transaktionsmethode aufgerufen wird, wobei die Ausnahme "Keine Sitzung" vermieden wird.

WARNUNG Wenn Sie verwenden @Transactional, beachten Sie bitte das resultierende Verhalten. In diesem Artikel finden Sie häufige Fallstricke. Beispielsweise bleiben Aktualisierungen von Entitäten erhalten, auch wenn Sie nicht explizit aufrufensave

user2601995
quelle
20
Ich kann die Bedeutung dieser Antwort nicht übertreiben. Ich würde ernsthaft empfehlen, diese Option zuerst zu versuchen.
Sparkyspider
6
Beachten Sie auch, dass Sie @EnableTransactionManagementIhrer Konfiguration etwas hinzufügen müssen, um Transaktionen zu aktivieren. " Wenn eine andere Transaktionsmethode aufgerufen wird, hat die Methode die Möglichkeit, sich der laufenden Transaktion anzuschließen. " Dieses Verhalten unterscheidet sich für die verschiedenen Arten, wie Transaktionen implementiert werden, dh Schnittstellen-Proxy oder Klassen-Proxy oder AspectJ-Weben. Siehe die Dokumentation .
Erik Hofer
1
Sollten wir verstehen, dass die Spring- TransactionalAnnotation daher nicht nur zum Ändern von Transaktionen empfohlen wird, sondern auch für den Zugriff auf nur solche?
Stephane
8
Ich würde ernsthaft empfehlen, diese Anmerkung nur zum Testen über der Klasse zu verwenden. Realer Code sollte jede Methode separat als Transaktion in der Klasse markieren. Es sei denn, alle Methoden in der Klasse erfordern eine geöffnete Verbindung mit der Transaktion zur Datenbank.
1.
1
Ist es nicht sicher, @Transactional (readOnly = true) anstelle von nur @Transactional zu setzen?
Hamedz
105

Sie können versuchen, einzustellen

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

in hibernate.cfg.xml oder persistence.xml

Das Problem, das bei dieser Eigenschaft zu beachten ist, wird hier ausführlich erläutert

Wilianto Indrawan
quelle
8
Können Sie auch seine Bedeutung erklären?
Mohit Kanwar
2
Ich bin auch neugierig, was das macht. Es hat das Problem behoben, das ich hatte, aber ich würde gerne verstehen, warum.
Hassan
3
für persistence.xml:<property name="hibernate.enable_lazy_load_no_trans" value="true"/>
ACV
33
Sie erkennen, dass dies hibernate.enable_lazy_load_no_transein Anti-Patterns ist , oder?
Vlad Mihalcea
6
DO NOT USE Diese Eigenschaft, IF SPRING verwaltet Ihre GESCHÄFTE Diese Eigenschaft wird auf Transaktionen EXPLOSION FÜHREN, SIMPLY SHUTDOWN Feder DIE ANWENDUNG
Youans
54

Der beste Weg, um damit umzugehen,LazyInitializationException ist die Verwendung der JOIN FETCHDirektive:

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

Verwenden Sie auf keinen Fall die folgenden Anti-Patterns, wie in einigen Antworten vorgeschlagen:

Manchmal ist eine DTO-Projektion die bessere Wahl als das Abrufen von Entitäten, und auf diese Weise erhalten Sie keine LazyInitializationException.

Vlad Mihalcea
quelle
Wie kann ich feststellen, bei welchem ​​Anruf ein Problem auftritt? Ich finde es genauso schwierig, den Anruf zu identifizieren. Gibt es irgendeinen Weg ? Zu Testzwecken habe ich verwendet FetchType=EAGER, aber das ist keine richtige Lösung, oder?
Shantaram Tupe
Verwenden Sie einfach die Protokollierung. Und EAGER ist schlecht, ja.
Vlad Mihalcea
Anschließend sollten Sie entweder DTOs verwenden oder alle Zuordnungen initialisieren, bevor Sie den @TransactionalDienst verlassen.
Vlad Mihalcea
2
Wir sollten die beste Unternehmenspraxis befürworten, aber nicht die schnelle Lösung.
etlds
20

Ich habe den gleichen Fehler für eine bis viele Beziehungen für die folgenden Anmerkungen erhalten.

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

Nach dem Hinzufügen von fetch = FetchType.EAGER wie unten geändert, hat es bei mir funktioniert.

@OneToMany(mappedBy="department", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
Smruti R Tripathy
quelle
24
Ja, es kann das Problem beheben, aber jetzt laden Sie den gesamten Datenbaum. Dies wird in den meisten Fällen negative Auswirkungen auf die Leistung haben
astro8891
13

Wenn Sie spring data jpa, spring boot verwenden, können Sie diese Zeile in application.properties hinzufügen

spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
Shaaban Ebrahim
quelle
7
Dies wird als Anti-Muster angesehen. vladmihalcea.com/…
Sudip Bhandari
9

Diese Ausnahme wird aufgrund des Anrufs session.getEntityById()geschlossen. Sie müssen die Entität also erneut an die Sitzung anhängen. Oder Einfache Lösung ist einfach zu konfigurieren , default-lazy="false" um Ihre entity.hbm.xmloder wenn Sie Anmerkungen verwenden fügen Sie einfach @Proxy(lazy=false)auf Ihre Entity - Klasse.

Reddeiah Pidugu
quelle
5

Ich bin auf das gleiche Problem gestoßen. Ich denke, eine andere Möglichkeit, dies zu beheben, besteht darin, dass Sie die Abfrage so ändern können, dass das Abrufen Ihres Elements aus dem Modell wie folgt erfolgt:

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

Dies bedeutet, dass das Objekt, auf das Sie zugreifen möchten, nicht geladen ist. Schreiben Sie daher eine Abfrage, mit der ein Join des Objekts abgerufen wird, auf das Sie zugreifen möchten.

Z.B:

Wenn Sie versuchen, ObjectB von ObjectA abzurufen, wobei ObjectB ein Fremdschlüssel in ObjectA ist.

Abfrage:

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

Hier gibt es mehrere gute Antworten, die diesen Fehler in einem weiten Bereich behandeln. Ich bin mit Spring Security auf eine bestimmte Situation gestoßen, die eine schnelle, wenn auch wahrscheinlich nicht optimale Lösung hatte.

Während der Benutzerautorisierung (unmittelbar nach dem Anmelden und dem Übergeben der Authentifizierung) habe ich eine Benutzerentität für eine bestimmte Berechtigung in einer benutzerdefinierten Klasse getestet, die SimpleUrlAuthenticationSuccessHandler erweitert.

Meine Benutzerentität implementiert UserDetails und verfügt über eine Reihe von verzögert geladenen Rollen, die die Ausnahme "org.hibernate.LazyInitializationException - Proxy konnte nicht initialisiert werden - keine Sitzung" ausgelöst haben. Das Ändern dieses Sets von "fetch = FetchType.LAZY" in "fetch = FetchType.EAGER" hat dies für mich behoben.

Nachteule
quelle
2

Konfrontierte die gleiche Ausnahme in verschiedenen Anwendungsfällen.

Geben Sie hier die Bildbeschreibung ein

Anwendungsfall: Versuchen Sie, Daten aus der Datenbank mit DTO-Projektion zu lesen .

Lösung: Verwenden Sie die Methode get anstelle von load .

Generischer Betrieb

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

}}

Persistenzklasse

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-Schnittstelle

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

Objektklasse für Entitätsübertragung

public class CustomerTO {

private int customerId;

private String customerName;

private String city;

//constructors , setters and getters

}}

Fabrikklasse

public class DAOFactory {

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

public static CustomerDAO getCustomerDAO() {
    return customerDAO;
}

}}

Entitätsspezifisches 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;
}

}}

Daten abrufen: Testklasse

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

Aktuelle Daten

Geben Sie hier die Bildbeschreibung ein

Abfrage und Ausgabe von Hibernate System generiert

Ruhezustand: Wählen Sie customer0_.Id als Id1_0_0_, customer0_.City als City2_0_0_, customer0_.Name als Name3_0_0_ aus CustomerLab31 customer0_ aus, wobei customer0_.Id =?

Kundenname -> Cody, CustomerCity -> LA

Kms
quelle
1

Wenn Sie Grail'sFramework verwenden, ist es einfach, eine verzögerte Initialisierungsausnahme mithilfe von zu behebenLazy Schlüsselworts für ein bestimmtes Feld in der Domänenklasse .

Zum Beispiel:

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

Weitere Informationen finden Sie hier

Zeb
quelle
1

In meinem Fall verursachte ein Verlegter session.clear()dieses Problem.

Absin
quelle
1

Dies bedeutet, dass Sie JPA oder Ruhezustand in Ihrem Code verwenden und Änderungsvorgänge für die Datenbank ausführen, ohne die Geschäftslogiktransaktion durchzuführen. Eine so einfache Lösung hierfür ist, Ihren Code @Transactional zu markieren

Aman Goel
quelle
-1

verwendet session.get (*. class, id); aber keine Funktion laden

Artavazd Manukyan
quelle
3
Kannst du das bitte erklären?
Neujahr
-2

Sie können es auch lösen, indem Sie lazy = false in Ihre * .hbm.xml-Datei einfügen, oder Sie können Ihr Objekt in Hibernate.init (Object) initiieren, wenn Sie ein Objekt von db erhalten

Sandeep Roniyaar
quelle
10
Im Allgemeinen ist es keine gute Idee, faul = falsch hinzuzufügen. Deshalb ist Faulheit standardmäßig wahr
MoienGK
Das OP hat zuvor klar gesagt, dass er das nicht darf.
GingerHead
-2

Nehmen Sie die folgenden Änderungen in servlet-context.xml vor

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

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

        </beans:props>
    </beans:property>
ArunDhwaj IIITH
quelle