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
Aus meiner Erfahrung habe ich die folgenden Methoden, um die berühmte LazyInitializationException zu lösen:
(1) Verwenden Sie Hibernate.initialize
(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.
quelle
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
Weitere Informationen zu dieser Anmerkung finden Sie hier
Über die anderen Lösungen:
ist keine gute Praxis, sollte NUR bei Bedarf verwendet werden.
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
quelle
@Transactional
ein Frühling?Der Ursprung Ihres Problems:
Standardmäßig lädt der Ruhezustand die Sammlungen (Beziehungen) träge. Wenn Sie also das
collection
in Ihrem Code verwendetecomments
Feld (hier in derTopic
Klasse) 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 diecomments
Sammlung laden ):Sie erhalten die Sammlung "Kommentare" (topic.getComments ()) in Ihrem Controller (wo
JPA session
beendet wurde) und dies verursacht die Ausnahme. Auch wenn Sie diecomments
Sammlung wie folgt in Ihrer JSP-Datei haben (anstatt sie in Ihrem Controller zu haben):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 dasFetchType
eifrige 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
:Dieser Code verlängert die Länge Ihres Codes
JPA session
oder 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 .quelle
Der Grund dafür ist, dass die Sitzung geschlossen wird, wenn Sie Lazy Load verwenden.
Es gibt zwei Lösungen.
Verwenden Sie keine träge Ladung.
In
lazy=false
XML oder@OneToMany(fetch = FetchType.EAGER)
Annotation festlegen.Verwenden Sie Lazy Load.
In
lazy=true
XML oder@OneToMany(fetch = FetchType.LAZY)
Annotation festlegen.und fügen Sie
OpenSessionInViewFilter filter
in Ihreweb.xml
Detail Siehe meinen POST .
quelle
Ich löse dieses Problem durch Hinzufügen
@Transactional
, ich denke, dies kann die Sitzung öffnenquelle
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:
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 ();
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.
Im Allgemeinen ist die beste Lösung die 1.
quelle
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).
quelle
Wie ich in diesem Artikel erklärt habe , ist der beste Weg, dies zu handhaben
LazyInitializationException
, es bei der Abfragezeit wie folgt abzurufen:Sie sollten IMMER die folgenden Anti-Muster vermeiden:
hibernate.enable_lazy_load_no_trans
Konfigurationseigenschaft für den RuhezustandStellen Sie daher sicher, dass Ihre
FetchType.LAZY
Zuordnungen zur Abfragezeit oder innerhalb des ursprünglichen@Transactional
BereichsHibernate.initialize
für sekundäre Sammlungen initialisiert werden .quelle
TrassctionInterceptor
im Stack-Trace an und das ist derjenige.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:
quelle
Eine der besten Lösungen besteht darin, Folgendes in Ihre Datei application.properties aufzunehmen: spring.jpa.properties.hibernate.enable_lazy_load_no_trans = true
quelle
Ich fand heraus, dass das Deklarieren
@PersistenceContext
alsEXTENDED
auch dieses Problem löst:quelle
Es war das Problem, mit dem ich kürzlich konfrontiert war und das ich mit der Verwendung gelöst habe
Eine detailliertere Beschreibung hier und das hat mir den Tag gerettet.
quelle
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 -
quelle
Um das Problem in meinem Fall zu lösen, fehlte nur diese Zeile
in der Anwendungskontextdatei.
Die
@Transactional
Anmerkung zu einer Methode wurde nicht berücksichtigt.Hoffe die Antwort wird jemandem helfen
quelle
@Transactional Annotation auf dem Controller fehlt
quelle
Wenn Sie mithilfe der
@Transactional
Annotation 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: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.quelle
Zwei Dinge, die Sie haben sollten
fetch = FetchType.LAZY
.und
quelle
Für diejenigen, die mit Kriterien arbeiten , habe ich das gefunden
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.
quelle
In meinem Fall war der folgende Code ein Problem:
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:
quelle
Der Grund dafür ist, dass Sie versuchen, die Kommentarliste auf Ihrem Controller abzurufen, nachdem Sie die Sitzung innerhalb des Dienstes geschlossen haben.
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.
quelle
Answer
Die Sammlung
comments
in Ihrer ModellklasseTopic
wird träge geladen. Dies ist das Standardverhalten, wenn Sie sie nicht mit Anmerkungen versehenfetch = FetchType.EAGER
speziell .Es ist meistens wahrscheinlich, dass Ihr
findTopicByID
Dienst 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 iterierencomments
, löst Hibernate eine Ausnahme aus.Die Lösung kann sein:
Kommentieren
comments
mitfetch = FetchType.EAGER
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.
quelle
In meinem Fall hatte ich das Mapping s / w
A
undB
wieA
hatIn der
DAO
Ebene muss die Methode mit Anmerkungen versehen werden,@Transactional
wenn Sie die Zuordnung nicht mit Fetch Type - Eager kommentiert habenquelle
Nicht die beste Lösung, aber für diejenigen, die sich
LazyInitializationException
besondersSerialization
damit auseinandersetzen, wird dies helfen. Hier überprüfen Sie träge initialisierte Eigenschaften und stellennull
diese ein. Erstellen Sie dazu die folgende KlasseFü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.
Rufen Sie diese
checkLazyIntialzation()
Methode an allen Stellen auf, an denen Sie Daten laden.quelle
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"
Wenn ich jetzt nach dem Schließen der Sitzung auf 'set' zugreife, wird eine Ausnahme ausgelöst.
Meine Lösung :
Jetzt kann ich auch nach dem Schließen der Hibernate-Sitzung auf 'set' zugreifen.
quelle
Eine weitere Möglichkeit, dies zu tun, ist die Verwendung von TransactionTemplate , um den faulen Abruf zu umgehen. Mögen
quelle
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
@Transactional
Anmerkung 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
@Transactional
die MethodefindTopicByID(id)
in Ihrer mit Anmerkungen versehenTopicService
und den Abruf der Sammlung in dieser Methode erzwingen (z. B. indem Sie nach ihrer Größe fragen):quelle
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
ObjectMapper
oderhashCode
von Lombok generiert Ihre Methoden implizit aufruft .In bestimmten Fällen können Sie
@EntityGrpaph
Anmerkungen verwenden, mit denen Sieeager
auch dann laden können, wenn Sie siefetchType=lazy
in Ihrer Entität haben.quelle
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.
quelle
Ich habe mit List anstelle von Set gelöst:
quelle