483 Stimmen

Wie löst man die Hibernate-Ausnahme "failed to lazily initialize a collection of role"?

Ich habe dieses Problem:

org.hibernate.LazyInitializationException: Fehlgeschlagene Lazy-Initialisierung 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 wie folgt 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 wie folgt 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 wird eine Ausnahme ausgelöst. In der Zeile mit c:forEach Schleife

248voto

darrengorman Punkte 12672

Wenn Sie wissen, dass Sie alles sehen wollen Comment s jedes Mal, wenn Sie eine Topic dann ändern Sie Ihre Feldzuordnung für comments zu:

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

Sammlungen werden standardmäßig "lazy-loaded", siehe diese wenn Sie mehr wissen wollen.

221voto

Boris Punkte 2363

Aus meiner Erfahrung habe ich die folgenden Methoden zur Lösung der berühmten LazyInitializationException:

(1) Verwenden Sie Hibernate.initialize

Hibernate.initialize(topics.getComments());

(2) JOIN FETCH verwenden

Sie können die JOIN FETCH-Syntax in Ihrer JPQL verwenden, um die untergeordnete Sammlung explizit herauszuholen. Dies ist in gewisser Weise wie EAGER fetching.

(3) OpenSessionInViewFilter verwenden

LazyInitializationException treten oft in Ansicht Schicht. Wenn Sie Spring Framework verwenden, können Sie OpenSessionInViewFilter verwenden. Ich empfehle Ihnen jedoch nicht, dies zu tun. Es kann zu Leistungsproblemen führen, wenn es nicht korrekt verwendet wird.

129voto

sarbuLopex Punkte 1717

Ich weiß, es ist eine alte Frage, aber ich möchte helfen. Sie können die Transaktionsannotation auf die benötigte Dienstmethode anwenden, in diesem Fall sollte findTopicByID(id)

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

Weitere Informationen über diese Anmerkung finden Sie unter aquí

Über die anderen Lösungen:

fetch = FetchType.EAGER 

ist keine gute Praxis, sie sollte NUR verwendet werden, wenn es notwendig ist.

Hibernate.initialize(topics.getComments());

Der Hibernate-Initialisierer bindet Ihre Klassen an die Hibernate-Technologie. Wenn Sie darauf abzielen, flexibel zu sein, ist das kein guter Weg.

Ich hoffe, es hilft

75voto

Cid Punkte 2486

Der Ursprung Ihres Problems:

Standardmäßig lädt Hibernate die Sammlungen (Beziehungen) nach und nach, d.h. wenn Sie die collection in Ihrem Code(hier comments Bereich in Topic Klasse) Hibernate holt sich diese aus der Datenbank. Das Problem ist nun, dass Sie die Sammlung in Ihrem Controller erhalten (wo die Dies ist die Codezeile, die die Ausnahme verursacht (wo Sie die JPA-Sitzung laden). (wo Sie die Sammlung comments Sammlung):

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

Sie erhalten die Sammlung "comments" (topic.getComments()) in Ihrem Controller (wo JPA session beendet ist), und das verursacht die Ausnahme. Auch wenn Sie die die comments Auflistung in Ihrer JSP-Datei wie folgt (anstatt es in Ihrem Controller):

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

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

Die Lösung des Problems:

Denn man kann nur zwei Sammlungen mit dem FetchType.Eager (eagerly fetched collection) in einer Entitätsklasse, und weil das faule Laden mehr effizienter ist als eifriges Laden, denke ich, dass diese Art der Lösung Ihres Problems besser ist als die Änderung der FetchType zu begierig:

Wenn Sie Sammlung faul initialisiert haben wollen, und auch machen diese Arbeit, ist es besser, diesen Codeschnipsel in Ihre 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>

Mit diesem Code wird die Länge Ihrer JPA session oder, wie es in der Dokumentation heißt, wird es verwendet "to allow for lazy loading in web views despite the original transactions already being completed." so auf diese Weise bleibt die JPA-Sitzung etwas länger offen, und das führt dazu, dass können Sie Sammlungen in Ihren JSP-Dateien und Controller-Klassen nach und nach laden.

45voto

RuiZhi Wang Punkte 425
@Controller
@RequestMapping(value = "/topic")
@Transactional

Ich löse dieses Problem durch Hinzufügen von @Transactional Ich denke, das kann die Sitzung öffnen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X