So lösen Sie die Ausnahme "Fehler beim Initialisieren einer Rollensammlung"

363

Ich habe dieses Problem:

org.hibernate.LazyInitializationException: Fehler beim Initialisieren einer Sammlung von Rollen: mvc3.model.Topic.comments, keine Sitzung oder Sitzung wurde geschlossen

Hier ist das Modell:

@Entity
@Table(name = "T_TOPIC")
public class Topic {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private int id;

    @ManyToOne
    @JoinColumn(name="USER_ID")
    private User author;

    @Enumerated(EnumType.STRING)    
    private Tag topicTag;

    private String name;
    private String text;

    @OneToMany(mappedBy = "topic", cascade = CascadeType.ALL)
    private Collection<Comment> comments = new LinkedHashSet<Comment>();

    ...

    public Collection<Comment> getComments() {
           return comments;
    }

}

Der Controller, der model aufruft, sieht folgendermaßen aus:

@Controller
@RequestMapping(value = "/topic")
public class TopicController {

    @Autowired
    private TopicService service;

    private static final Logger logger = LoggerFactory.getLogger(TopicController.class);


    @RequestMapping(value = "/details/{topicId}", method = RequestMethod.GET)
    public ModelAndView details(@PathVariable(value="topicId") int id)
    {

            Topic topicById = service.findTopicByID(id);
            Collection<Comment> commentList = topicById.getComments();

            Hashtable modelData = new Hashtable();
            modelData.put("topic", topicById);
            modelData.put("commentList", commentList);

            return new ModelAndView("/topic/details", modelData);

     }

}

Die JSP-Seite sieht folgendermaßen aus:

<%@page import="com.epam.mvc3.helpers.Utils"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
      <title>View Topic</title>
</head>
<body>

<ul>
<c:forEach items="${commentList}" var="item">
<jsp:useBean id="item" type="mvc3.model.Comment"/>
<li>${item.getText()}</li>

</c:forEach>
</ul>
</body>
</html>

Die Ausnahme wird erhöht, wenn jsp angezeigt wird. In der Zeile mit c: forEach- Schleife

Eugene
quelle

Antworten:

214

Wenn Sie wissen, dass Sie Commentjedes Mal alle s sehen möchten, wenn Sie eine abrufen, Topicändern Sie Ihre Feldzuordnung für comments:

@OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)
private Collection<Comment> comments = new LinkedHashSet<Comment>();

Sammlungen werden standardmäßig faul geladen. Sehen Sie sich dies an, wenn Sie mehr wissen möchten.

Darrengorman
quelle
35
Entschuldigung, aber ich möchte Lazy-Load verwenden. Also habe ich den Typ 'LinkedHashSet' in die 'PersistentList' geändert. Ausnahme tritt immer noch auf
Eugene
242
Dies könnte als Problemumgehung verwendet werden, ist jedoch keine tatsächliche Lösung des Problems. Was ist, wenn wir faul holen müssen?
Dkyc
14
Wenn wir jedoch faul sein möchten, funktioniert diese Lösung nicht und in den meisten Fällen möchten wir nur faul sein.
Prashant Thakre
103
Dies ist die Art der Antwort, die beim Stapelüberlauf überall angezeigt wird. Kurz gesagt, auf den Punkt gebracht, löst das Problem und MISLEADING. Tun Sie sich selbst einen Gefallen und lernen Sie, was genau faul und eifrig ist, und verstehen Sie die Konsequenzen.
Ced
13
@darrengorman Als ich JPA startete, stellte ich eine Frage in Anlehnung an die von OP. Ich habe die gleiche Antwort erhalten wie Sie. Ratet mal, was passiert ist, als ich einen Test mit Hunderttausenden von Reihen durchführte? Ich denke, es ist irreführend, weil es eine zu einfache Antwort auf ein Problem bietet, mit dem die meisten Anfänger konfrontiert werden, und bald genug wird ihre gesamte Datenbank im Speicher geladen, wenn sie nicht vorsichtig sind (und sie werden es nicht tun, weil sie es nicht tun) sei dir dessen bewusst) :).
Ced
182

Aus meiner Erfahrung habe ich die folgenden Methoden, um die berühmte LazyInitializationException zu lösen:

(1) Verwenden Sie Hibernate.initialize

Hibernate.initialize(topics.getComments());

(2) Verwenden Sie JOIN FETCH

Sie können die JOIN FETCH-Syntax in Ihrem JPQL verwenden, um die untergeordnete Sammlung explizit abzurufen. So ähnlich wie beim EAGER-Abrufen.

(3) Verwenden Sie OpenSessionInViewFilter

LazyInitializationException tritt häufig in der Ansichtsebene auf. Wenn Sie das Spring Framework verwenden, können Sie OpenSessionInViewFilter verwenden. Ich empfehle Ihnen jedoch nicht, dies zu tun. Bei unsachgemäßer Verwendung kann es zu Leistungsproblemen kommen.

Boris
quelle
5
(1) hat perfekt für mich funktioniert. Mein Fall: Hibernate.initialize (registry.getVehicle (). GetOwner (). GetPerson (). GetAddress ());
Leonel Sanches da Silva
6
Es scheint, dass Hibernate.initialize nicht mit EntityManager
Marionmaiden
8
Dies sollte die richtige Antwort sein. Zum Beispiel sollten wir in meinem Projekt bei der Arbeit ausdrücklich nicht das EAGER-Abrufen verwenden. Es verursacht Probleme in diesem speziellen System.
Steve Waters
Scheint attraktiv, aber es fehlt an Dokumentation, um sie in einem anderen Fall zu implementieren. Könnten Sie bitte weitere Links oder Erklärungen zur Implementierung dieser Lösung bereitstellen?
Pipo
58

Ich weiß, dass es eine alte Frage ist, aber ich möchte helfen. Sie können die Transaktionsanmerkung auf die Dienstmethode setzen, die Sie benötigen. In diesem Fall sollte findTopicByID (id) vorhanden sein

@Transactional(propagation=Propagation.REQUIRED, readOnly=true, noRollbackFor=Exception.class)

Weitere Informationen zu dieser Anmerkung finden Sie hier

Über die anderen Lösungen:

fetch = FetchType.EAGER 

ist keine gute Praxis, sollte NUR bei Bedarf verwendet werden.

Hibernate.initialize(topics.getComments());

Der Hibernate-Initialisierer bindet Ihre Klassen an die Hibernate-Technologie. Wenn Sie flexibel sein möchten, ist dies kein guter Weg.

Ich hoffe es hilft

sarbuLopex
quelle
3
Die Annotation @Transactional hat bei mir funktioniert, aber beachten Sie, dass Propagation.REQUIRED die Standardeinstellung ist, zumindest in Spring Boot 1.4.2 (Spring 4.3).
Ben3000
4
Ja, aber ich dachte, es könnte geschätzt werden, um klar zu machen, dass Sie den Ausbreitungsparameter tatsächlich ändern könnten
sarbuLopex
Ist das nicht nur @Transactionalein Frühling?
Campa
@Campa ja das ist es. Wenn Sie es manuell handhaben möchten, sollten Sie Ihre Geschäftslogik in eine Transaktion
einfügen, die
54

Der Ursprung Ihres Problems:

Standardmäßig lädt der Ruhezustand die Sammlungen (Beziehungen) träge. Wenn Sie also das collectionin Ihrem Code verwendete commentsFeld (hier in der TopicKlasse) verwenden, wird der Ruhezustand aus der Datenbank abgerufen. Das Problem besteht nun darin, dass Sie die Sammlung in Ihrem Controller abrufen (wo sich die JPA-Sitzung befindet ist geschlossen). Dies ist die Codezeile, die die Ausnahme verursacht (wo Sie die commentsSammlung laden ):

    Collection<Comment> commentList = topicById.getComments();

Sie erhalten die Sammlung "Kommentare" (topic.getComments ()) in Ihrem Controller (wo JPA sessionbeendet wurde) und dies verursacht die Ausnahme. Auch wenn Sie die commentsSammlung wie folgt in Ihrer JSP-Datei haben (anstatt sie in Ihrem Controller zu haben):

<c:forEach items="topic.comments" var="item">
//some code
</c:forEach>

Sie hätten aus demselben Grund immer noch dieselbe Ausnahme.

Lösung des Problems:

Da Sie mit der FetchType.Eager(eifrig abgerufenen Sammlung) in einer Entity-Klasse nur zwei Sammlungen haben können und das verzögerte Laden effizienter ist als das FetchTypeeifrige Laden, ist diese Art der Problemlösung meiner Meinung nach besser, als nur das zu ändern :

Wenn Sie möchten, dass die Sammlung faul initialisiert wird, und dies auch funktioniert, ist es besser, diesen Codeausschnitt zu Ihrem hinzuzufügen web.xml:

<filter>
    <filter-name>SpringOpenEntityManagerInViewFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>SpringOpenEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Dieser Code verlängert die Länge Ihres Codes JPA sessionoder wird, wie in der Dokumentation angegeben, verwendet, "to allow for lazy loading in web views despite the original transactions already being completed."sodass die JPA-Sitzung etwas länger geöffnet ist und Sie daher Sammlungen in Ihre JSP-Dateien und Controller-Klassen träge laden können .

gandalf
quelle
7
Warum ist die JPS-Sitzung geschlossen? Wie kann man dafür sorgen, dass es nicht geschlossen wird? Wie führe ich eine faule Sammlung durch?
Dims
1
Was definiert ein Limit von zwei FetchType.Eager-Sammlungen pro Entität?
Chrisinmtown
In Spring Boot können Sie 'spring.jpa.open-in-view = true' zu 'application.properties' hinzufügen
Askar
28

Der Grund dafür ist, dass die Sitzung geschlossen wird, wenn Sie Lazy Load verwenden.

Es gibt zwei Lösungen.

  1. Verwenden Sie keine träge Ladung.

    In lazy=falseXML oder @OneToMany(fetch = FetchType.EAGER)Annotation festlegen.

  2. Verwenden Sie Lazy Load.

    In lazy=trueXML oder @OneToMany(fetch = FetchType.LAZY)Annotation festlegen.

    und fügen Sie OpenSessionInViewFilter filterin Ihreweb.xml

Detail Siehe meinen POST .

saneryee
quelle
1
... und doch sind beide Lösungen nicht gut. Die Verwendung von EAGER kann zu großen Problemen führen. Die Verwendung von OpenSessionInViewFilter ist ein Anti-Pattern.
Rafael
27
@Controller
@RequestMapping(value = "/topic")
@Transactional

Ich löse dieses Problem durch Hinzufügen @Transactional, ich denke, dies kann die Sitzung öffnen

RuiZhi Wang
quelle
Warum erhielt dies eine Minus-Stimme? Das Hinzufügen einer Transaktion zur Operation verlängert die Sitzung
Tudor Grigoriu
1
Es ist eine schlechte Praxis, @Transactional auf dem Controller anzuzeigen.
Rafael
@ Rafael Warum ist es eine schlechte Praxis?
Amr Ellafy
@AmrEllafy -> Hier ist eine gute Erklärung: stackoverflow.com/a/18498834/1261162
Rafael
22

Das Problem wird durch den Zugriff auf ein Attribut bei geschlossener Sitzung im Ruhezustand verursacht. Sie haben keine Ruhezustandstransaktion im Controller.

Mögliche Lösungen:

  1. Führen Sie all diese Logik in der Service-Schicht (mit @Transactional) aus, nicht in der Steuerung. Es sollte den richtigen Ort dafür geben, es ist Teil der Logik der App, nicht im Controller (in diesem Fall eine Schnittstelle zum Laden des Modells). Alle Vorgänge in der Serviceschicht sollten transaktional sein. dh: Verschieben Sie diese Zeile in die TopicService.findTopicByID-Methode:

    Sammlung commentList = topicById.getComments ();

  2. Verwenden Sie "eifrig" anstelle von "faul" . Jetzt verwenden Sie nicht 'faul'. Es ist keine echte Lösung. Wenn Sie faul verwenden möchten, funktioniert dies wie eine vorübergehende (sehr vorübergehende) Problemumgehung.

  3. Verwenden Sie @Transactional im Controller . Es sollte hier nicht verwendet werden, Sie mischen Service-Schicht mit Präsentation, es ist kein gutes Design.
  4. Verwenden Sie OpenSessionInViewFilter , viele Nachteile gemeldet, mögliche Instabilität.

Im Allgemeinen ist die beste Lösung die 1.

abentan
quelle
2
Fetch-Typ von Eager ging davon aus, dass im Ruhezustand alle Daten in der ersten Abfrage
abgerufen
Sie sollten BEACHTEN, dass die BESTE LÖSUNG 1 ist ... in der Tat ist die EINZIGE GUTE Lösung, da alle anderen Anti-Muster sind!
Rafael
19

Um eine Sammlung verzögert zu laden, muss eine Sitzung aktiv sein. In einer Web-App gibt es zwei Möglichkeiten, dies zu tun. Sie können das Muster " Sitzung in Ansicht öffnen" verwenden , bei dem Sie die Sitzung am Anfang der Anforderung mit einem Interceptor öffnen und am Ende schließen. Das Risiko besteht darin, dass Sie eine solide Ausnahmebehandlung benötigen, oder Sie könnten alle Ihre Sitzungen binden und Ihre App könnte hängen bleiben.

Die andere Möglichkeit, dies zu handhaben, besteht darin, alle Daten zu sammeln, die Sie in Ihrem Controller benötigen, Ihre Sitzung zu schließen und die Daten dann in Ihr Modell einzufügen. Ich persönlich bevorzuge diesen Ansatz, da er dem Geist des MVC-Musters etwas näher zu kommen scheint. Auch wenn Sie auf diese Weise einen Fehler aus der Datenbank erhalten, können Sie damit viel besser umgehen als in Ihrem Ansichtsrenderer. Ihr Freund in diesem Szenario ist Hibernate.initialize (myTopic.getComments ()). Sie müssen das Objekt auch erneut an die Sitzung anhängen, da Sie bei jeder Anforderung eine neue Transaktion erstellen. Verwenden Sie dazu session.lock (myTopic, LockMode.NONE).

GMK
quelle
15

Wie ich in diesem Artikel erklärt habe , ist der beste Weg, dies zu handhaben LazyInitializationException, es bei der Abfragezeit wie folgt abzurufen:

select t
from Topic t
left join fetch t.comments

Sie sollten IMMER die folgenden Anti-Muster vermeiden:

Stellen Sie daher sicher, dass Ihre FetchType.LAZYZuordnungen zur Abfragezeit oder innerhalb des ursprünglichen @TransactionalBereichs Hibernate.initializefür sekundäre Sammlungen initialisiert werden .

Vlad Mihalcea
quelle
1
Vlad, haben Sie Vorschläge für die Arbeit mit einer verzögert initialisierten Sammlung in einer Entität, die mit der findById () -Methode eines von Spring generierten Repositorys abgerufen wird? Ich schreibe die Abfrage nicht und die Transaktion befindet sich außerhalb meines Codes.
Chrisinmtown
In diesem Artikel finden Sie weitere Informationen zum Initialisieren von Lazy Collections.
Vlad Mihalcea
Könnten Sie klarstellen, was Sie unter "innerhalb des ursprünglichen @ Transaktionsbereichs" verstehen? Dies ist mir unklar, da ich diesen Fehler in einer offenen Sitzung zu bekommen scheine (aber nicht in der richtigen?)
Michiel Haisma
Während innerhalb des Bereichs der obersten Transaktionsdienstmethode, auch als Transaktionsgateway bekannt. Schauen Sie sich das TrassctionInterceptorim Stack-Trace an und das ist derjenige.
Vlad Mihalcea
Eine der mit Abstand besten Antworten ... dies sollte als richtig markiert werden. Übrigens ... unter der Annahme, dass OSIV ein Anti-Pattern ist, wie ist es möglich, dass es in den neuesten Spring-Boot-Versionen standardmäßig aktiviert ist? ... ist das vielleicht nicht so schlimm?
Rafael
10

Wenn Sie versuchen, eine Beziehung zwischen einer Entität und einer Sammlung oder einer Liste von Java-Objekten (z. B. Long-Typ) herzustellen, möchten Sie Folgendes:

@ElementCollection(fetch = FetchType.EAGER)
    public List<Long> ids;
Javaboygo
quelle
1
In vielen Fällen wollen Sie das wirklich nicht. Sie verlieren alle Vorteile des faulen Ladens hier
kiedysktos
Die Verwendung von EAGER ist keine professionelle Lösung.
Rafael
9

Eine der besten Lösungen besteht darin, Folgendes in Ihre Datei application.properties aufzunehmen: spring.jpa.properties.hibernate.enable_lazy_load_no_trans = true

sreekmatta
quelle
1
Können Sie dem OP genau sagen, was es tut, welche Nebenwirkungen es hat und welche Auswirkungen es auf die Leistung hat?
PeS
3
Am Ende des verzögerten Ladens wird jedes Mal, wenn eine Zuordnung träge geladen wird, eine neue Sitzung gegabelt, wodurch mehr Verbindungen gegabelt werden und ein wenig Druck auf den Verbindungspool ausgeübt wird. Wenn die Anzahl der Verbindungen begrenzt ist, ist diese Eigenschaft möglicherweise nicht korrekt.
Sreekmatta
2
für einige gilt es als Anti-Muster vladmihalcea.com/…
Uri Loya
7

Ich fand heraus, dass das Deklarieren @PersistenceContextals EXTENDEDauch dieses Problem löst:

@PersistenceContext(type = PersistenceContextType.EXTENDED)
Elcin ABD
quelle
1
Hallo, sei vorsichtig mit solchen Änderungen. Die Erstellung von Persistenzkontexten mit TRANSACTION-Gültigkeitsbereich ist faul, was die OP-Absicht war. Die Frage ist also, ob Sie staatenlos sein wollen oder nicht. Diese Einstellung hängt vom Zweck des Systems ab und sollte nicht zu ... eifrig geändert werden. Wenn du weißt, was ich meine. Lesen Sie hier stackoverflow.com/questions/2547817/…
kiedysktos
Gefährlich. Dies ist nicht die richtige Antwort. Es gibt andere oben viel genauer und sicherer.
Rafael
5

Es war das Problem, mit dem ich kürzlich konfrontiert war und das ich mit der Verwendung gelöst habe

<f:attribute name="collectionType" value="java.util.ArrayList" />

Eine detailliertere Beschreibung hier und das hat mir den Tag gerettet.

Tolgayilmaz
quelle
5

Ihre Liste wird verzögert geladen, daher wurde die Liste nicht geladen. Anruf, um auf die Liste zu kommen, reicht nicht aus. Verwenden Sie in Hibernate.initialize, um die Liste zu starten. Wenn dies nicht funktioniert, führen Sie das Listenelement aus und rufen Sie für jedes Element Hibernate.initialize auf. Dies muss erfolgen, bevor Sie aus dem Transaktionsbereich zurückkehren. Schau dir diesen Beitrag an.
suchen nach -

Node n = // .. get the node
Hibernate.initialize(n); // initializes 'parent' similar to getParent.
Hibernate.initialize(n.getChildren()); // pass the lazy collection into the session 
Avihai Marchiano
quelle
4

Um das Problem in meinem Fall zu lösen, fehlte nur diese Zeile

<tx:annotation-driven transaction-manager="myTxManager" />

in der Anwendungskontextdatei.

Die @TransactionalAnmerkung zu einer Methode wurde nicht berücksichtigt.

Hoffe die Antwort wird jemandem helfen

Mario Biasi
quelle
4

@Transactional Annotation auf dem Controller fehlt

@Controller
@RequestMapping("/")
@Transactional
public class UserController {
}
Xcoder
quelle
17
Ich würde argumentieren, dass das Transaktionsmanagement zur Serviceschicht gehört, in der sich die Geschäftslogik befindet.
20.
Transaktionsanmerkungen fehlen nicht. Der Controller sollte keine solche Anmerkung haben. Diese Anmerkungen sollten sich auf Service-Ebene befinden.
Rafael
4

Wenn Sie mithilfe der @TransactionalAnnotation im Ruhezustand ein Objekt mit verzögert abgerufenen Attributen aus der Datenbank abrufen, können Sie diese einfach abrufen, indem Sie diese Attribute wie folgt abrufen:

@Transactional
public void checkTicketSalePresence(UUID ticketUuid, UUID saleUuid) {
        Optional<Ticket> savedTicketOpt = ticketRepository.findById(ticketUuid);
        savedTicketOpt.ifPresent(ticket -> {
            Optional<Sale> saleOpt = ticket.getSales().stream().filter(sale -> sale.getUuid() == saleUuid).findFirst();
            assertThat(saleOpt).isPresent();
        });
}

In einer von einem Hibernate-Proxy verwalteten Transaktion führt die Tatsache des Aufrufs ticket.getSales()eine weitere Abfrage durch, um Verkäufe abzurufen, da Sie dies ausdrücklich angefordert haben.

Alex
quelle
4

Zwei Dinge, die Sie haben sollten fetch = FetchType.LAZY.

@Transactional

und

Hibernate.initialize(topicById.getComments());
Karan Patel
quelle
2

Für diejenigen, die mit Kriterien arbeiten , habe ich das gefunden

criteria.setFetchMode("lazily_fetched_member", FetchMode.EAGER);

tat alles was ich brauchte getan hatte.

Der anfängliche Abrufmodus für Sammlungen ist auf FetchMode.LAZY eingestellt, um Leistung zu erzielen. Wenn ich jedoch die Daten benötige, füge ich einfach diese Zeile hinzu und genieße die vollständig ausgefüllten Objekte.

velis
quelle
2

In meinem Fall war der folgende Code ein Problem:

entityManager.detach(topicById);
topicById.getComments() // exception thrown

Weil es von der Datenbank getrennt wurde und im Ruhezustand keine Liste mehr aus dem Feld abgerufen wurde, als es benötigt wurde. Also initialisiere ich es vor dem Trennen:

Hibernate.initialize(topicById.getComments());
entityManager.detach(topicById);
topicById.getComments() // works like a charm
kiedysktos
quelle
1

Der Grund dafür ist, dass Sie versuchen, die Kommentarliste auf Ihrem Controller abzurufen, nachdem Sie die Sitzung innerhalb des Dienstes geschlossen haben.

topicById.getComments();

Oben wird die Kommentarliste nur geladen, wenn Ihre Ruhezustandssitzung aktiv ist. Ich denke, Sie haben sie in Ihrem Dienst geschlossen.

Sie müssen also die Kommentarliste abrufen, bevor Sie die Sitzung schließen.

Aditya Latte
quelle
2
Ja, dies ist die Problemstellung. Sie sollten auch eine Antwort in einemAnswer
Sarz
1

Die Sammlung commentsin Ihrer Modellklasse Topicwird träge geladen. Dies ist das Standardverhalten, wenn Sie sie nicht mit Anmerkungen versehenfetch = FetchType.EAGER speziell .

Es ist meistens wahrscheinlich, dass Ihr findTopicByIDDienst eine zustandslose Ruhezustandssitzung verwendet. Eine zustandslose Sitzung verfügt nicht über den Cache der ersten Ebene, dh keinen Persistenzkontext. Wenn Sie später versuchen, zu iterieren comments, löst Hibernate eine Ausnahme aus.

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: mvc3.model.Topic.comments, no session or session was closed

Die Lösung kann sein:

  1. Kommentieren commentsmitfetch = FetchType.EAGER

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)   
    private Collection<Comment> comments = new LinkedHashSet<Comment>();
  2. Wenn Sie weiterhin möchten, dass Kommentare träge geladen werden, verwenden Sie die Stateful-Sitzungen von Hibernate , damit Sie später bei Bedarf Kommentare abrufen können.

Yuci
quelle
1

In meinem Fall hatte ich das Mapping s / w Aund Bwie

A hat

@OneToMany(mappedBy = "a", cascade = CascadeType.ALL)
Set<B> bs;

In der DAOEbene muss die Methode mit Anmerkungen versehen werden, @Transactionalwenn Sie die Zuordnung nicht mit Fetch Type - Eager kommentiert haben

Shirgill Farhan
quelle
1

Nicht die beste Lösung, aber für diejenigen, die sich LazyInitializationExceptionbesonders Serializationdamit auseinandersetzen, wird dies helfen. Hier überprüfen Sie träge initialisierte Eigenschaften und stellen nulldiese ein. Erstellen Sie dazu die folgende Klasse

public class RepositoryUtil {
    public static final boolean isCollectionInitialized(Collection<?> collection) {
        if (collection instanceof PersistentCollection)
            return ((PersistentCollection) collection).wasInitialized();
        else 
            return true;
    }   
}

Fügen Sie in Ihrer Entity-Klasse, für die Sie träge initialisierte Eigenschaften haben, eine Methode wie unten gezeigt hinzu. Fügen Sie alle Ihre Eigenschaften zum langsamen Laden innerhalb dieser Methode hinzu.

public void checkLazyIntialzation() {
    if (!RepositoryUtil.isCollectionInitialized(yourlazyproperty)) {
        yourlazyproperty= null;
    }

Rufen Sie diese checkLazyIntialzation()Methode an allen Stellen auf, an denen Sie Daten laden.

 YourEntity obj= entityManager.find(YourEntity.class,1L);
  obj.checkLazyIntialzation();
Javad
quelle
0

Hallo, alle, die ziemlich spät posten, hoffen, dass es anderen hilft. Vielen Dank im Voraus an @GMK für diesen Beitrag. Hibernate.initialize (Objekt)

wenn Lazy = "wahr"

Set<myObject> set=null;
hibernateSession.open
set=hibernateSession.getMyObjects();
hibernateSession.close();

Wenn ich jetzt nach dem Schließen der Sitzung auf 'set' zugreife, wird eine Ausnahme ausgelöst.

Meine Lösung :

Set<myObject> set=new HashSet<myObject>();
hibernateSession.open
set.addAll(hibernateSession.getMyObjects());
hibernateSession.close();

Jetzt kann ich auch nach dem Schließen der Hibernate-Sitzung auf 'set' zugreifen.

vic
quelle
0

Eine weitere Möglichkeit, dies zu tun, ist die Verwendung von TransactionTemplate , um den faulen Abruf zu umgehen. Mögen

Collection<Comment> commentList = this.transactionTemplate.execute
(status -> topicById.getComments());
aristotll
quelle
0

Das Problem wird verursacht, weil der Code auf eine verzögerte JPA-Beziehung zugreift, wenn die "Verbindung" zur Datenbank geschlossen wird ( Persistenzkontext ist der korrekte Name in Bezug auf Ruhezustand / JPA).

Eine einfache Möglichkeit, dies in Spring Boot zu lösen, besteht darin, eine Serviceschicht zu definieren und die @TransactionalAnmerkung zu verwenden. Diese Annotation in einer Methode erstellt eine Transaktion, die in die Repository-Schicht weitergegeben wird und den Persistenzkontext bis zum Abschluss der Methode offen hält. Wenn Sie innerhalb der Transaktionsmethode auf die Sammlung zugreifen, ruft Hibernate / JPA die Daten aus der Datenbank ab.

In Ihrem Fall müssen Sie nur @Transactionaldie Methode findTopicByID(id)in Ihrer mit Anmerkungen versehen TopicServiceund den Abruf der Sammlung in dieser Methode erzwingen (z. B. indem Sie nach ihrer Größe fragen):

    @Transactional(readOnly = true)
    public Topic findTopicById(Long id) {
        Topic topic = TopicRepository.findById(id).orElse(null);
        topic.getComments().size();
        return topic;
    }
Domingo Gallardo
quelle
0

Um die Ausnahme der verzögerten Initialisierung zu beseitigen, sollten Sie keine verzögerte Erfassung aufrufen, wenn Sie mit einem getrennten Objekt arbeiten.

Meiner Meinung nach ist es am besten, DTO und nicht Entity zu verwenden. In diesem Fall können Sie explizit Felder festlegen, die Sie verwenden möchten. Wie immer ist es genug. Sie müssen sich keine Sorgen machen, dass etwas wie Jackson ObjectMapperoder hashCodevon Lombok generiert Ihre Methoden implizit aufruft .

In bestimmten Fällen können Sie @EntityGrpaphAnmerkungen verwenden, mit denen Sie eagerauch dann laden können, wenn Sie sie fetchType=lazyin Ihrer Entität haben.

degr
quelle
0

Für dieses Problem mit der verzögerten Initialisierung gibt es mehrere Lösungen:

1) Ändern Sie den Assoziationstyp "Abrufen" von "LAZY" in "EAGER". Dies ist jedoch keine gute Vorgehensweise, da dies die Leistung beeinträchtigt.

2) Verwenden Sie FetchType.LAZY für das zugeordnete Objekt und die Transaktionsanmerkung in Ihrer Service-Layer-Methode, damit die Sitzung geöffnet bleibt und beim Aufrufen von topicById.getComments () untergeordnete Objekte (Kommentare) geladen werden.

3) Versuchen Sie außerdem, ein DTO-Objekt anstelle einer Entität in der Controller-Schicht zu verwenden. In Ihrem Fall wird die Sitzung auf Controller-Ebene geschlossen. SO besser, Entität in DTO in Service-Schicht zu konvertieren.

vsharma
quelle
-11

Ich habe mit List anstelle von Set gelöst:

private List<Categories> children = new ArrayList<Categories>();
SaganTheBest
quelle