it-swarm.com.de

Beheben der Ausnahmebedingung "Fehler beim verzögerten Initialisieren einer Rollensammlung" im Ruhezustand

Ich habe dieses Problem:

org.hibernate.LazyInitializationException: Fehler beim verzögerten Initialisieren einer Rollensammlung: 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>

Bei der Anzeige von jsp tritt eine Ausnahme auf. In der Zeile mit c: forEach loop

310
Eugene

Wenn Sie wissen, dass Sie jedes Mal, wenn Sie ein Comment abrufen, alle Topic anzeigen möchten, ändern Sie die Feldzuordnung für comments in:

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

Sammlungen sind standardmäßig faul geladen. Schauen Sie sich this an, wenn Sie mehr wissen möchten.

179
darrengorman

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) Benutze JOIN FETCH

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

(3) Verwenden Sie OpenSessionInViewFilter

LazyInitializationException tritt häufig in der Ansichtsebene auf. Wenn Sie Spring Framework verwenden, können Sie OpenSessionInViewFilter verwenden. Ich empfehle Ihnen jedoch nicht, dies zu tun. Bei falscher Verwendung kann dies zu Leistungsproblemen führen.

166
Boris

Der Ursprung Ihres Problems:

Standardmäßig lädt der Ruhezustand die Auflistungen (Beziehungen) träge. Dies bedeutet, dass Sie die Auflistung in Ihrem Controller immer dann abrufen, wenn Sie das Feld collection in Ihrem Code verwenden (hier das Feld comments in der Klasse Topic) Die JPA-Sitzung ist geschlossen.) Dies ist die Codezeile, die die Ausnahme verursacht (wenn Sie die comments-Auflistung laden):

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

Sie erhalten die Auflistung "comments" (topic.getComments ()) in Ihrem Controller (wobei _JPA session_ beendet wurde) und dies verursacht die Ausnahme. Auch wenn Sie die Sammlung comments in Ihrer JSP-Datei haben (anstatt sie in Ihrem Controller abzurufen):

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

Sie würden immer noch die gleiche Ausnahme aus dem gleichen Grund haben.

Lösung des Problems:

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

Wenn Sie möchten, dass die Sammlung verzögert initialisiert wird, und dies auch funktioniert, ist es besser, diesen Codeausschnitt zu Ihrem _web.xml_ hinzuzufügen:

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

Was dieser Code bewirkt, ist, dass er die Länge Ihres _JPA session_ verlängert oder, wie in der Dokumentation angegeben, verwendet wird _"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 dies können Laden Sie Sammlungen in Ihre JSP-Dateien und Controller-Klassen.

49
gandalf

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

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

weitere Informationen zu dieser Annotation finden Sie hier

Über die anderen Lösungen:

fetch = FetchType.EAGER 

dies ist keine gute Praxis. Sie sollte NUR verwendet werden, wenn dies erforderlich ist.

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

43
sarbuLopex

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

Es gibt zwei Lösungen.

  1. Verwenden Sie keine faule Ladung.

    Setze lazy=false in XML oder Setze @OneToMany(fetch = FetchType.EAGER) in Annotation.

  2. Verwenden Sie Lazy Load.

    Setze lazy=true in XML oder Setze @OneToMany(fetch = FetchType.LAZY) in Annotation.

    und füge OpenSessionInViewFilter filter in dein web.xml ein

Detail Siehe mein POST .

29
saneryee

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

Mögliche Lösungen:

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

    Auflistung 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. benutze @Transactional im Controller. Es sollte hier nicht verwendet werden, Sie mischen Serviceebene mit Präsentation, es ist kein gutes Design.
  4. benutze OpenSessionInViewFilter, viele Nachteile gemeldet, mögliche Instabilität.

Im Allgemeinen ist die beste Lösung die 1.

20
abentan
@Controller
@RequestMapping(value = "/topic")
@Transactional

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

19
RuiZhi Wang

Um eine Sammlung faul 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, wobei Sie einen Interceptor verwenden, um die Sitzung am Anfang der Anforderung zu öffnen und am Ende zu schließen. Das Risiko besteht darin, dass Sie eine solide Ausnahmebehandlung benötigen oder alle Ihre Sitzungen beenden und Ihre App hängen bleibt.

Die andere Möglichkeit, dies zu handhaben, besteht darin, alle Daten zu erfassen, die Sie in Ihrem Controller benötigen, die 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 ein wenig näher zu kommen scheint. Auch wenn Sie auf diese Weise einen Fehler aus der Datenbank erhalten, können Sie viel besser damit umgehen, als wenn dies in Ihrem Ansichtsrenderer der Fall ist. Ihr Freund in diesem Szenario ist Hibernate.initialize (myTopic.getComments ()). Sie müssen das Objekt auch erneut der Sitzung zuordnen, da Sie bei jeder Anforderung eine neue Transaktion erstellen. Verwenden Sie dazu session.lock (myTopic, LockMode.NONE).

18
GMK

Wenn Sie versuchen, eine Beziehung zwischen einer Entität und einer Auflistung oder einer Liste von Java Objekten herzustellen (z. B. Langer Typ), würde dies ungefähr so ​​aussehen:

@ElementCollection(fetch = FetchType.EAGER)
    public List<Long> ids;
11
javaboygo

Ich habe herausgefunden, dass das Deklarieren von @PersistenceContext als EXTENDED auch dieses Problem löst:

@PersistenceContext(type = PersistenceContextType.EXTENDED)
9
Elcin ABD

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

7
sreekmatta

Wie ich in diesem Artikel erklärt habe, besteht die beste Möglichkeit, mit LazyInitializationException umzugehen, darin, es zum Zeitpunkt der Abfrage abzurufen:

select t
from Topic t
left join fetch t.comments

Sie sollten IMMER die folgenden Anti-Patterns vermeiden:

Stellen Sie daher sicher, dass Ihre FetchType.LAZY Zuordnungen zur Abfragezeit oder innerhalb des ursprünglichen @Transactional Bereichs mit Hibernate.initialize für sekundäre Auflistungen initialisiert werden.

7
Vlad Mihalcea

@ Transaktionsanmerkung auf dem Controller fehlt

@Controller
@RequestMapping("/")
@Transactional
public class UserController {
}
6
Xcoder

ihre Liste wird nur faul geladen, daher wurde sie nicht geladen. Anruf auf die Liste zu bekommen ist nicht genug. Verwenden Sie in Hibernate.initialize, um die Liste zu starten. Wenn dies nicht der Fall ist, führen Sie das Listenelement aus und rufen Sie Hibernate.initialize für jedes Element auf. Dies muss geschehen, bevor Sie aus dem Transaktionsbereich zurückkehren. schau dir this post 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 
5

es war das Problem, mit dem ich vor kurzem konfrontiert war und mit dem ich es gelöst habe

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

detailliertere Beschreibung hier und das hat mir den Tag gerettet.

5
tolgayilmaz

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

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

in der Anwendungskontextdatei.

Die @Transactional -Anmerkung über eine Methode wurde nicht berücksichtigt.

Hoffe, die Antwort wird jemandem helfen

4
Mario Biasi

Wenn Sie mit der Annotation hibernate @Transactional 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 der Aufruf von ticket.getSales() eine weitere Abfrage aus, um Verkäufe abzurufen, da Sie diese explizit angefordert haben.

4
Alex

In meinem Fall war folgender Code ein Problem:

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

Weil es von der Datenbank getrennt wurde und die Liste im Ruhezustand nicht mehr aus dem Feld abgerufen wurde, als es benötigt wurde. Also initialisiere ich es, bevor ich es entferne:

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

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

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

tat alles, was ich brauchte, hatte getan.

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

2
velis

In meinem Fall hatte ich das Mapping s/w A und B mögen

A hat

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

in der Ebene DAO muss Methode mit @Transactional kommentiert werden, wenn Sie das Mapping nicht mit Fetch Type - Eager kommentiert haben

1
Shirgill Farhan

Die Sammlung comments in Ihrer Modellklasse Topic wird verzögert geladen. Dies ist das Standardverhalten, wenn Sie sie nicht speziell mit fetch = FetchType.EAGER kommentieren.

Es ist sehr wahrscheinlich, dass Ihr findTopicByID -Dienst eine zustandslose Ruhezustandssitzung verwendet. Eine zustandslose Sitzung hat keinen Cache der ersten Ebene, d. H. Keinen Persistenzkontext. Wenn Sie später versuchen, comments zu iterieren, 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. Kommentiere comments mit fetch = 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 verzögert geladen werden, verwenden Sie Hibernate's stateful sessions , damit Sie Kommentare später bei Bedarf abrufen können.

1
Yuci

Hallo alle Postings ziemlich spät, hoffe es hilft anderen, Danke im Voraus an @GMK für diesen Beitrag Hibernate.initialize (object)

wenn Lazy = "true"

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 Session auf "Set" zugreifen.

0
vic

Eine weitere Möglichkeit, dies zu tun, ist TransactionTemplate , um den Lazy Fetch zu umgehen. Mögen

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

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, was Sie vermutlich in Ihrem Dienst geschlossen haben.

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

0
aditya lath