J'ai ce problème:
org.hibernate.LazyInitializationException: échec d'initialisation paresseuse d'une collection de rôles: mvc3.model.Topic.comments, aucune session ou session n'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 model, se présente comme 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 page jsp ressemble à ce qui suit:
<%@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>
L'exception est levée lors de l'affichage de jsp. Dans la ligne avec c: forEach loop
Si vous savez que vous voudrez voir tous les Comment
s chaque fois que vous récupérerez un Topic
, changez le mappage de vos champs pour comments
en:
@OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)
private Collection<Comment> comments = new LinkedHashSet<Comment>();
Les collections sont chargées par défaut, regardez this si vous voulez en savoir plus.
D'après mon expérience, j'ai les méthodes suivantes pour résoudre la fameuse exception LazyInitializationException:
(1) Utilisez Hibernate.initialize
Hibernate.initialize(topics.getComments());
(2) Utilisez JOIN FETCH
Vous pouvez utiliser la syntaxe JOIN FETCH dans votre JPQL pour extraire explicitement la collection enfant. C'est un peu comme EAGER chercher.
(3) Use OpenSessionInViewFilter
LazyInitializationException se produit souvent dans la couche de vue. Si vous utilisez la structure Spring, vous pouvez utiliser OpenSessionInViewFilter. Cependant, je ne vous suggère pas de le faire. Cela peut entraîner des problèmes de performances s’il n’est pas utilisé correctement.
Par défaut, hibernate charge paresseusement les collections (relations), ce qui signifie que chaque fois que vous utilisez le collection
dans votre code (ici comments
dans la classe Topic
name__), le problème est que vous récupérez la collection dans votre contrôleur (où la session JPA est fermée). Il s’agit de la ligne de code qui provoque l’exception (où vous chargez la collection comments
name__):
Collection<Comment> commentList = topicById.getComments();
Vous obtenez une collection de "commentaires" (topic.getComments ()) dans votre contrôleur (où JPA session
a pris fin), ce qui provoque une exception. De plus, si vous aviez 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 auriez toujours la même exception pour la même raison.
Parce que vous ne pouvez avoir que deux collections avec le FetchType.Eager
(collection extraite avec empressement) dans une classe Entity et que le chargement différé est plus efficace que le chargement avec impatience, je pense que cette façon de résoudre votre problème est préférable au simple changement de FetchType
name__:
Si vous souhaitez que la collection soit initialisée paresseuse et que cela fonctionne également, 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>
Ce code fait qu’il augmentera la longueur de votre JPA session
ou, comme le dit la documentation, il est utilisé "to allow for lazy loading in web views despite the original transactions already being completed."
. Ainsi, la session JPA sera ouverte un peu plus longtemps et vous permettra de charger des collections dans vos fichiers jsp. classes de contrôleur.
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 peuvent être trouvées ici
A propos des autres solutions:
fetch = FetchType.EAGER
n’est pas une bonne pratique, il convient de l’utiliser UNIQUEMENT si nécessaire.
Hibernate.initialize(topics.getComments());
L'initialiseur d'hibernation lie vos classes à la technologie d'hibernation. Si vous souhaitez être flexible, ce n'est pas une bonne façon de faire.
J'espère que ça aide
La raison en est que lorsque vous utilisez la charge différée, la session est fermée.
Il y a deux solutions.
Ne pas utiliser la charge paresseuse.
Définissez lazy=false
en XML ou Définissez @OneToMany(fetch = FetchType.EAGER)
dans une annotation.
Utilisez la charge paresseuse.
Définissez lazy=true
en XML ou Définissez @OneToMany(fetch = FetchType.LAZY)
dans une annotation.
et ajoutez OpenSessionInViewFilter filter
dans votre web.xml
Détail Voir mon POST .
Le problème est dû à l'accès à un attribut avec la session de veille prolongée fermée. Vous n'avez pas de transaction d'hibernation dans le contrôleur.
Solutions possibles:
Faites toute cette logique, dans la couche service, (avec le @Transactional), pas dans le contrôleur. Il devrait y avoir le bon endroit pour faire cela, cela fait partie de la logique de l'application, pas du contrôleur (dans ce cas, une interface pour charger le modèle). Toutes les opérations de la couche service doivent être transactionnelles. c'est-à-dire: déplacez cette ligne vers la méthode TopicService.findTopicByID:
Collection commentList = topicById.getComments ();
tilisez 'impatient' au lieu de 'paresseux'. Maintenant, vous n’utilisez pas "paresseux". Ce n’est pas une solution réelle. Si vous voulez utiliser du paresseux, fonctionne comme une solution de contournement temporaire (très temporaire).
En général, la meilleure solution est le 1.
Pour charger une collection paresseuse, une session doit être active. Dans une application Web, il existe deux manières de procéder. Vous pouvez utiliser le modèle Ouvrir une session dans la vue , où vous utilisez un intercepteur pour ouvrir la session au début de la demande et la fermer à la fin. Le risque est que vous ayez une gestion solide des exceptions ou que vous puissiez bloquer toutes vos sessions et que votre application puisse se bloquer.
Une autre façon de gérer cela consiste à collecter toutes les données dont vous avez besoin dans votre contrôleur, à fermer votre session, puis à les insérer dans votre modèle. Personnellement, je préfère cette approche car elle semble un peu plus proche de l’esprit du modèle MVC. De même, si vous obtenez une erreur de la base de données de cette manière, vous pourrez la gérer beaucoup mieux que si cela se produisait dans votre moteur de rendu de vue. Votre ami dans ce scénario est Hibernate.initialize (myTopic.getComments ()). Vous devrez également rattacher l'objet à la session, car vous créez une nouvelle transaction avec chaque demande. Utilisez session.lock (myTopic, LockMode.NONE) pour cela.
@Controller
@RequestMapping(value = "/topic")
@Transactional
je résous ce problème en ajoutant @Transactional
, je pense que cela peut rendre la session ouverte
Si vous essayez d’avoir une relation entre une entité et une collection ou une liste d’objets Java (par exemple, type long), vous voudrez quelque chose comme ceci:
@ElementCollection(fetch = FetchType.EAGER)
public List<Long> ids;
J'ai découvert que déclarer @PersistenceContext
en tant que EXTENDED
résout également ce problème:
@PersistenceContext(type = PersistenceContextType.EXTENDED)
Comme je l'ai expliqué dans cet article , le meilleur moyen de gérer le LazyInitializationException
est de le récupérer au moment de l'interrogation, comme suit:
select t
from Topic t
left join fetch t.comments
Vous devriez TOUJOURS éviter les anti-modèles suivants:
hibernate.enable_lazy_load_no_trans
HibernatePar conséquent, assurez-vous que vos associations FetchType.LAZY
sont initialisées au moment de la requête ou dans la portée @Transactional
d'origine à l'aide de Hibernate.initialize
pour les collections secondaires.
L'annotation @Transactional sur le contrôleur est manquante
@Controller
@RequestMapping("/")
@Transactional
public class UserController {
}
votre liste est paresseuse, donc la liste n'a pas été chargée. Appeler pour figurer sur la liste ne suffit pas. utiliser dans Hibernate.initialize afin d’initialiser la liste. Si dosnt fonctionne sur l'élément de la liste et appelle Hibernate.initialize pour chacun. cette nécessité doit être avant de revenir de la portée de la transaction. regardez this post.
rechercher -
Node n = // .. get the node
Hibernate.initialize(n); // initializes 'parent' similar to getParent.
Hibernate.initialize(n.getChildren()); // pass the lazy collection into the session
c’est le problème que j’ai rencontré récemment et que j’ai résolu avec
<f:attribute name="collectionType" value="Java.util.ArrayList" />
description plus détaillée ici et cela m'a sauvé la journée.
Pour résoudre le problème dans mon cas, il manquait juste cette ligne
<tx:annotation-driven transaction-manager="myTxManager" />
dans le fichier de contexte d'application.
L'annotation @Transactional
sur une méthode n'a pas été prise en compte.
J'espère que la réponse aidera quelqu'un
Une des meilleures solutions consiste à ajouter les éléments suivants dans votre fichier application.properties: spring.jpa.properties.hibernate.enable_lazy_load_no_trans = true
En utilisant l'annotation hibernate @Transactional
, si vous obtenez un objet de la base de données avec des attributs extraits paresseux, vous pouvez simplement les obtenir en récupérant ces attributs comme suit:
@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()
permet d'effectuer une autre requête pour extraire les ventes, car vous l'avez explicitement demandé.
Dans mon cas, le code suivant posait problème:
entityManager.detach(topicById);
topicById.getComments() // exception thrown
Parce qu’elle s’est détachée de la base de données et que Hibernate n’a plus extrait la liste du terrain au moment voulu. Alors je l'initialise avant de détacher:
Hibernate.initialize(topicById.getComments());
entityManager.detach(topicById);
topicById.getComments() // works like a charm
Pour ceux qui travaillent avec Critères , j'ai constaté que
criteria.setFetchMode("lazily_fetched_member", FetchMode.EAGER);
a fait tout ce que j'avais besoin avait fait.
Le mode de récupération initiale des collections est défini sur FetchMode.LAZY pour offrir des performances optimales, mais lorsque j'ai besoin des données, j'ajoute simplement cette ligne pour profiter des objets entièrement remplis.
La collection comments
dans votre classe de modèle Topic
est chargée paresseusement, ce qui est le comportement par défaut si vous ne l'annotez pas spécifiquement avec fetch = FetchType.EAGER
.
Il est fort probable que votre service findTopicByID
utilise une session Hibernate sans état. Une session sans état ne possède pas le cache de premier niveau, c'est-à-dire pas de contexte de persistance. Plus tard, lorsque vous essayez d’itérer comments
name__, Hibernate lève une 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:
Annoter comments
avec fetch = FetchType.EAGER
@OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)
private Collection<Comment> comments = new LinkedHashSet<Comment>();
Si vous souhaitez toujours que les commentaires soient chargés paresseusement, utilisez les sessions avec état de Hibernate , de manière à pouvoir récupérer les commentaires plus tard, à la demande.
Bonjour à tous, en postant assez tard, espérons que cela aidera les autres, Remerciant d’avance @GMK pour ce message Hibernate.initialize (object)
quand Lazy = "true"
Set<myObject> set=null;
hibernateSession.open
set=hibernateSession.getMyObjects();
hibernateSession.close();
maintenant, si j'accède à 'set' après la fermeture de la session, une exception est levée.
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 Hibernate Session.
Encore une autre façon de faire la chose, vous pouvez utiliser TransactionTemplate pour envelopper la récupération paresseuse. Comme
Collection<Comment> commentList = this.transactionTemplate.execute
(status -> topicById.getComments());
Dans mon cae, j’avais la cartographie en noir et blanc A et B comme
Un a
@OneToMany(mappedBy = "a", cascade = CascadeType.ALL)
Set<B> bs;
dans la couche DAO, la méthode doit être annotée avec @Transactional
si vous n'avez pas annoté le mappage avec Fetch Type - Eager
La raison en est que vous essayez d’obtenir la liste de commentaires sur votre contrôleur après la fermeture de la session dans le service.
topicById.getComments();
Ci-dessus ne chargera la liste de commentaires que si votre session de veille prolongée est active, ce que je suppose que vous avez fermé dans votre service.
Donc, vous devez obtenir la liste de commentaires avant de fermer la session.