Comment résoudre le" échoué à initialiser paresseusement une collection de rôle " Hibernate exception

j'ai ce problème:

org.hiberner.LazyInitializationException: n'a pas réussi à initialiser paresseusement une collection de rôle: mvc3.modèle.Sujet.commentaires, pas de session ou la session a été fermée

voici le modèle:

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

}

le contrôleur, qui appelle le modèle ressemble à ce qui suit:

@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);

     }

}

La jsp-présentation de la page de li la suivante:

<%@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>

Exception est monté, lors de la visualisation des jsp. Dans la ligne c: forEach loop

244
demandé sur Vlad Mihalcea 2012-07-31 22:18:11

26 réponses

si vous savez que vous voulez voir tous les Comment s chaque fois que vous récupérez un Topic puis changer votre cartographie de champ pour comments à:

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

Collections sont paresseux-chargé par défaut, jetez un oeil à ce si vous voulez en savoir plus.

155
répondu darrengorman 2017-05-23 12:34:57

D'après mon expérience, j'ai les méthodes suivantes pour résoudre le célèbre LazyInitializationException:

(1) Utiliser Hibernation.initialiser

Hibernate.initialize(topics.getComments());

(2) "

vous pouvez utiliser la syntaxe JOIN FETCH dans votre JPQL pour récupérer explicitement la collection enfant. C'est une façon comme le chargement agressif.

(3) Utilisation OpenSessionInViewFilter

LazyInitializationException se produisent souvent dans la couche vue. Si vous utilisez Spring framework, vous pouvez utiliser OpenSessionInViewFilter. Toutefois, Je ne vous suggère pas de le faire. Elle peut entraîner des problèmes de performance si elle n'est pas utilisée correctement.

136
répondu Boris 2017-05-15 11:08:58

L'origine de votre problème:

par défaut hibernate charge les collections (relations) ce qui signifie que lorsque vous utilisez le collection dans votre code (ici comments champ dans la classe Topic ) le problème est que vous obtenez la collection dans votre controller (où le JPA la fermeture de la session).C'est la ligne de code qui provoque l'exception (où vous chargez la collection comments ):

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

Vous "commentaires" de la collection (le sujet.getComments ()) dans votre controller (où JPA session s'est terminé) et qui cause l'exception. Aussi si vous aviez obtenu la collection comments dans votre fichier jsp comme ceci (au lieu de l'obtenir dans votre contrôleur):

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

Vous aurait toujours la même exception pour la même raison.

Résoudre le problème:

Parce que vous ne pouvez avoir que deux collections avec la FetchType.Eager (collection avec impatience) dans une classe Entity et parce que le chargement paresseux est plus efficace que Chargement avide, je pense que cette façon de résoudre votre problème est mieux que de simplement changer le FetchType à impatient:

si vous voulez avoir la collecte initialisé paresseux, et aussi faire ce travail, il est préférable d'ajouter cet extrait de code à votre 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>

quoi ce code fait est qu'il va augmenter la longueur de votre JPA session ou comme la documentation dit, il est utilisé "to allow for lazy loading in web views despite the original transactions already being completed." ainsi de cette façon, la session de L'App sera ouverte un peu plus longtemps et à cause de cela vous pouvez charger paresseusement des collections dans vos fichiers jsp et classes de contrôleur.

45
répondu gandalf 2015-07-18 10:21:22

je sais que c'est une vieille question mais je veux aider. Vous pouvez mettre l'annotation transactionnelle sur la méthode de service dont vous avez besoin, dans ce cas findTopicByID (id) devrait avoir

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

plus d'informations sur cette annotation peut être trouvé ici

concernant les autres solutions:

fetch = FetchType.EAGER 

n'est pas une bonne pratique, elle ne doit être utilisée que si nécessaire.

Hibernate.initialize(topics.getComments());

le hibernate initializer lie vos classes à la technologie hibernate. Si vous cherchez à être flexible n'est pas une bonne façon d'aller.

j'Espère que ça aide

34
répondu sarbuLopex 2017-05-23 12:10:54

la raison est que lorsque vous utilisez la charge paresseuse, la session est fermée.

il y a deux solutions.

  1. de Ne pas utiliser le lazy load.

    Set lazy=false en XML ou Définir des @OneToMany(fetch = FetchType.EAGER) Dans l'annotation.

  2. Utiliser le lazy load.

    Set lazy=true en XML ou Définir des @OneToMany(fetch = FetchType.LAZY) Dans l'annotation.

    et ajouter OpenSessionInViewFilter filter dans votre web.xml

détail Voir mon POST .

28
répondu saneryee 2017-10-24 11:09:37

pour charger paresseusement une collection il doit y avoir une session active. Dans une application web, il y a deux façons de le faire. Vous pouvez utiliser la session ouverte en vue pattern, où vous utilisez un interceptor pour ouvrir la session au début de la demande et la fermer à la fin. Le risque est que vous ayez besoin d'avoir une bonne gestion des exceptions ou vous pourriez lier toutes vos sessions et votre application pourrait pendre.

le une autre façon de gérer cela est de collecter toutes les données dont vous avez besoin dans votre controller, fermer votre session, et ensuite bourrer les données dans votre model. Personnellement, je préfère cette approche, car il semble un peu plus proche de l'esprit du modèle MVC. Aussi, si vous obtenez une erreur de la base de données de cette façon, vous pouvez gérer beaucoup mieux que si cela arrive à votre avis rendu. Votre ami dans ce scénario est hiberné.initialiser (myTopic.getComments()). Vous aurez également à rattachez l'objet à la session, puisque vous créez une nouvelle transaction avec chaque requête. Session d'utilisation.lock (myTopic, LockMode.AUCUN).

19
répondu GMK 2012-08-02 17:47:20

le problème est causé par l'accès à un attribut avec la session d'hibernation fermée. Vous n'avez pas de transaction d'hibernation dans le contrôleur.

solutions possibles:

  1. faire toute cette logique, dans la couche de service , (avec le @Transactional), pas dans le contrôleur. Il devrait être le bon endroit pour ce faire, il fait partie de la logique de l'application, et non dans le contrôleur (dans ce cas, un interface pour charger le modèle). Toutes les opérations de la couche de service devraient être transactionnelles. i.e.: déplacez cette ligne vers le TopicService.findTopicByID method:

    Collection commentList = topicById.getComments ();

  2. utilisez" eager "au lieu de "lazy " . Maintenant, vous n'utilisez pas "paresseux".. ce n'est pas une vraie solution, si vous souhaitez utiliser paresseux, fonctionne comme une temporaire (très temporaire) solution de contournement.

  3. utiliser @Transactionnelle dans le Contrôleur . Il ne doit pas être utilisé ici, vous mélangez couche de service avec présentation, il n'est pas une bonne conception.
  4. utiliser OpenSessionInViewFilter , nombreux inconvénients signalés, instabilité possible.

en général, la meilleure solution est le 1.

14
répondu abentan 2017-02-13 14:17:16
@Controller
@RequestMapping(value = "/topic")
@Transactional

Je résous ce problème en ajoutant @Transactional , je pense que cela peut rendre la session ouverte

13
répondu RuiZhi Wang 2018-04-10 16:36:50

si vous essayez d'avoir une relation entre une entité et une Collection ou une liste d'objets java (par exemple un type Long), elle aimerait quelque chose comme ceci:

@ElementCollection(fetch = FetchType.EAGER)
    public List<Long> ids;
10
répondu javaboygo 2015-08-24 14:50:11

j'ai découvert que déclarer @PersistenceContext comme EXTENDED résout aussi ce problème:

@PersistenceContext(type = PersistenceContextType.EXTENDED)
9
répondu Elcin ABD 2015-09-16 13:22:12

@l'annotation transactionnelle sur le contrôleur est manquante

@Controller
@RequestMapping("/")
@Transactional
public class UserController {
}
6
répondu Xcoder 2016-03-26 10:30:47

c'est le problème que j'ai récemment rencontré et que j'ai résolu en utilisant

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

description plus détaillée ici et cela m'a sauvé la vie.

5
répondu tolgayilmaz 2013-11-07 15:22:57

votre liste est chargée paresseusement, donc la liste n'a pas été chargée. appeler pour obtenir sur la liste n'est pas suffisant. utiliser en hibernation.initialiser afin d'initialisation de la liste. Si dosnt work fonctionne, exécutez l'élément list et appelez Hibernate.initialiser pour chaque . cela doit être fait avant que vous ne reveniez du champ d'application de la transaction. regardez ce" post 151930920".

chercher pour -

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
répondu Avihai Marchiano 2017-10-24 11:24:30

pour résoudre le problème dans mon cas, il manquait juste cette ligne

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

dans le dossier de la demande-contexte.

l'annotation @Transactional sur une méthode n'a pas été prise en compte.

Espérons que la réponse sera aider quelqu'un

4
répondu Mario Biasi 2016-02-05 16:38:38

pour ceux qui travaillent avec critère , j'ai trouvé que

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

a fait tout ce dont j'avais besoin.

le mode fetch initial pour les collections est défini à FetchMode.Paresseux de fournir des performances, mais quand j'ai besoin des données, je viens d'ajouter cette ligne et profiter des objets entièrement peuplés.

2
répondu velis 2015-12-01 05:22:43

dans mon cas le code suivant était un problème:

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

parce qu'il s'est détaché de la base de données et Hibernate n'a plus récupéré la liste sur le terrain quand elle était nécessaire. Donc je l'initialise avant de le détacher:

Hibernate.initialize(topicById.getComments());
entityManager.detach(topicById);
topicById.getComments() // works like a charm
2
répondu kiedysktos 2017-02-03 09:36:53

comme je l'ai expliqué dans cet article , la meilleure façon de gérer le LazyInitializationException est de le récupérer au moment de la requête, comme ceci:

select t
from Topic t
left join fetch t.comments

vous devez toujours éviter les anti-modèles suivants:

par conséquent, assurez-vous que vos associations FetchType.LAZY sont initialisées au moment de la requête ou dans la portée @Transactional originale en utilisant Hibernate.initialize pour les collections secondaires.

2
répondu Vlad Mihalcea 2018-06-27 06:21:50

en utilisant l'annotation @Transactional d'hibernation, si vous obtenez un objet dans la base de données avec des attributs paresseusement récupérés, vous pouvez simplement obtenir ces attributs en récupérant ces attributs comme ceci:

@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();
        });
}

ici, dans une transaction gérée par proxy Hibernate, le fait d'appeler ticket.getSales() fait une autre requête pour récupérer des ventes parce que vous l'avez explicitement demandé.

2
répondu Alex 2018-07-25 11:59:54

la raison est que vous essayez d'obtenir la liste de commentaires sur votre contrôleur après avoir fermé la session dans le service.

topicById.getComments();

ci-dessus chargera la liste des commentaires seulement si votre session d'hibernation est active, ce que je suppose que vous avez fermé dans votre service.

ainsi, vous devez obtenir la liste des commentaires avant de fermer la session.

0
répondu aditya lath 2018-03-16 10:11:10

dans mon cae, j'avais le mapping b / w A et B comme

a a

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

dans la couche DAO, la" méthode 151970920 " doit être annotée avec @Transactional si vous n'avez pas annoté la cartographie avec Fetch Type-Eager

0
répondu Shirgill Farhan Ansari 2018-05-23 10:12:58

La collection comments dans votre modèle de classe Topic est paresseusement chargé, ce qui est le comportement par défaut si vous n'avez pas l'annoter avec fetch = FetchType.EAGER .

il est fort probable que votre service findTopicByID utilise une session d'hibernation sans état. Une session sans état ne pas avoir le premier niveau de cache, c'est à dire, pas de contexte de persistance. Plus tard, lorsque vous essayez d'itérer comments , Hibernate lancera un exception.

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

la solution peut être:

  1. Annoter comments avec fetch = FetchType.EAGER

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)   
    private Collection<Comment> comments = new LinkedHashSet<Comment>();
    
  2. si vous souhaitez toujours que vos commentaires soient chargés paresseusement, utilisez Hibernate's stateful sessions , de sorte que vous puissiez obtenir des commentaires plus tard sur demande.

0
répondu Yuci 2018-07-20 10:32:18

Salut tout le monde postant assez tard espoir qu'il aide les autres, Merci d'avance à @GMK pour ce post hibernation.initialiser (objet)

quand Lazy= "true"

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

maintenant si j'accède à ' set ' après la session de clôture, Cela fait exception.

ma solution:

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

maintenant je peux accéder à ' set ' même après la fermeture de la session D'hibernation.

0
répondu vic 2018-07-25 06:41:48

une des meilleures solutions est d'ajouter ce qui suit dans votre application.fichier de propriétés: "151910920 de" printemps".jpa.propriété.hiberner.enabl_lazy_load_no_trans=true

0
répondu sreekmatta 2018-08-01 23:24:38

encore une autre façon de faire la chose, vous pouvez utiliser TransactionTemplate pour enrouler autour du fetch paresseux. Comme

Collection<Comment> commentList = this.transactionTemplate.execute
(status -> topicById.getComments());
0
répondu aristotll 2018-09-01 17:43:50

ajoutez ceci à votre persistance.xml

<property name="hibernate.enable_lazy_load_no_trans" value="true" />
-1
répondu Mohammad-Hossein Jamali 2015-04-10 20:03:27

j'ai résolu en utilisant List au lieu de Set:

private List<Categories> children = new ArrayList<Categories>();
-8
répondu SaganTheBest 2014-07-08 08:42:06