J'ai ce problème:
org.hibernate.LazyInitializationException: échec de l'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 modèle, ressemble à ceci:
@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 à ceci:
<%@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>
Une exception est levée lors de l'affichage de jsp. Dans la lignée de c: forEach loop
D'après mon expérience, j'ai les méthodes suivantes pour résoudre la fameuse LazyInitializationException:
(1) Utilisez Hibernate.initialize
(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 aller chercher EAGER.
(3) Utilisez OpenSessionInViewFilter
LazyInitializationException se produit souvent dans la couche de vue. Si vous utilisez le framework 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.
la source
Je sais que c'est une vieille question mais je veux aider. Vous pouvez placer l'annotation transactionnelle sur la méthode de service dont vous avez besoin, dans ce cas, findTopicByID (id) devrait avoir
plus d'informations sur cette annotation peuvent être trouvées ici
À propos des autres solutions:
n'est pas une bonne pratique, elle doit être utilisée UNIQUEMENT si nécessaire.
L'initialiseur hibernate lie vos classes à la technologie hibernate. Si vous visez à être flexible, ce n'est pas une bonne façon de procéder.
J'espère que cela aide
la source
@Transactional
une chose réservée au printemps?L'origine de votre problème:
Par défaut, hibernate charge paresseusement les collections (relations), ce qui signifie que chaque fois que vous utilisez le
collection
dans votre code (comments
champ ici dans laTopic
classe), l'hibernation obtient cela de la base de données, maintenant le problème est que vous obtenez la collection dans votre contrôleur (où la session JPA est fermée) .C'est la ligne de code qui provoque l'exception (où vous chargez lacomments
collection):Vous obtenez la collection "comments" (topic.getComments ()) dans votre contrôleur (où
JPA session
s'est terminée) et cela provoque l'exception. De plus, si vous aviez obtenu lacomments
collection dans votre fichier jsp comme ceci (au lieu de l'obtenir dans votre contrôleur):Vous auriez 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 récupérée avec impatience) dans une classe Entity et parce que le chargement paresseux est plus efficace que le chargement avec impatience, je pense que cette façon de résoudre votre problème est meilleure que de simplement changer leFetchType
en désireux:Si vous souhaitez initialiser la collection paresseusement et que cela fonctionne également, il est préférable d'ajouter cet extrait de code à votre
web.xml
:Ce que ce code fait, c'est qu'il augmentera la longueur de votre
JPA session
ou, comme le dit la documentation, il est utilisé de"to allow for lazy loading in web views despite the original transactions already being completed."
cette façon, la session JPA sera ouverte un peu plus longtemps et, à cause de cela, vous pouvez paresseusement charger des collections dans vos fichiers jsp et vos classes de contrôleur. .la source
La raison en est que lorsque vous utilisez le chargement différé, la session est fermée.
Il y a deux solutions.
N'utilisez pas de charge paresseuse.
Définir
lazy=false
en XML ou@OneToMany(fetch = FetchType.EAGER)
annoter Définir dans.Utilisez la charge paresseuse.
Définir
lazy=true
en XML ou@OneToMany(fetch = FetchType.LAZY)
annoter Définir dans.et ajoutez
OpenSessionInViewFilter filter
votreweb.xml
Détails Voir mon POST .
la source
je résous ce problème en ajoutant
@Transactional
, je pense que cela peut ouvrir la sessionla source
Le problème est dû à l'accès à un attribut avec la session de mise en 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 @Transactional), pas dans le contrôleur. Il devrait y avoir le bon endroit pour le faire, cela fait partie de la logique de l'application, pas dans le contrôleur (dans ce cas, une interface pour charger le modèle). Toutes les opérations de la couche service doivent être transactionnelles. ie: Déplacez cette ligne vers la méthode TopicService.findTopicByID:
Collection commentList = topicById.getComments ();
Utilisez «impatient» au lieu de «paresseux» . Maintenant, vous n'utilisez pas «paresseux» .. ce n'est pas une vraie solution, si vous voulez utiliser paresseux, fonctionne comme une solution de contournement temporaire (très temporaire).
En général, la meilleure solution est le 1.
la source
Pour charger une collection paresseusement, il doit y avoir une session active. Dans une application Web, il existe deux façons de procéder. Vous pouvez utiliser le modèle Ouvrir la session dans la vue , dans lequel 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 des exceptions solide ou que vous puissiez lier toutes vos sessions et que votre application puisse se bloquer.
L'autre façon de gérer cela est de collecter toutes les données dont vous avez besoin dans votre contrôleur, de fermer votre session, puis de 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 plus, si vous obtenez une erreur de la base de données de cette façon, vous pouvez la gérer beaucoup mieux que si cela se produit dans votre 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.
la source
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:Vous devez TOUJOURS éviter les anti-modèles suivants:
hibernate.enable_lazy_load_no_trans
propriété de configuration HibernatePar conséquent, assurez-vous que vos
FetchType.LAZY
associations sont initialisées au moment de la requête ou dans la@Transactional
portée d' origine à l' aideHibernate.initialize
de collections secondaires.la source
TrassctionInterceptor
trace de la pile et c'est celle-là.Si vous essayez d'avoir une relation entre une entité et une collection ou une liste d'objets java (par exemple de type Long), cela voudrait quelque chose comme ceci:
la source
L'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
la source
J'ai découvert que déclarer
@PersistenceContext
commeEXTENDED
résout également ce problème:la source
c'était le problème que j'ai récemment rencontré et que j'ai résolu en utilisant
une description plus détaillée ici et cela m'a sauvé la journée.
la source
votre liste est en cours de chargement, donc la liste n'a pas été chargée. appeler pour figurer sur la liste ne suffit pas. utiliser dans Hibernate.initialize pour lancer la liste. Si vous ne travaillez pas, exécutez l'élément list et appelez Hibernate.initialize pour chacun. cela doit être avant de revenir de la portée de la transaction. regardez ce post.
rechercher -
la source
Pour résoudre le problème dans mon cas, il manquait juste cette ligne
dans le fichier de contexte d'application.
L'
@Transactional
annotation sur une méthode n'a pas été prise en compte.J'espère que la réponse aidera quelqu'un
la source
L'annotation @Transactional sur le contrôleur est manquante
la source
En utilisant l'
@Transactional
annotation de mise en veille prolongée , si vous obtenez un objet de la base de données avec des attributs récupérés paresseux, vous pouvez simplement les obtenir en récupérant ces attributs comme ceci: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 les ventes car vous l'avez explicitement demandé.la source
Vous devriez avoir deux choses
fetch = FetchType.LAZY
.et
la source
Pour ceux qui travaillent avec Criteria , j'ai trouvé que
a fait tout ce dont j'avais besoin.
Le mode de récupération initial pour les collections est défini sur FetchMode.LAZY pour fournir des performances, mais lorsque j'ai besoin des données, j'ajoute simplement cette ligne et je profite des objets entièrement remplis.
la source
Dans mon cas, le code suivant était un problème:
Parce qu'il s'est détaché de la base de données et qu'Hibernate n'a plus récupéré la liste du champ lorsque cela était nécessaire. Je l'initialise donc avant de détacher:
la source
La raison en est que vous essayez d'obtenir la commentList sur votre contrôleur après avoir fermé la session à l'intérieur du service.
Ci-dessus ne chargera la liste de commentaires que si votre session de mise en veille prolongée est active, ce que je suppose que vous avez fermé dans votre service.
Vous devez donc obtenir la liste de commentaires avant de fermer la session.
la source
Answer
La collection
comments
de votre classe de modèleTopic
est chargée paresseusement, ce qui est le comportement par défaut si vous ne l'annotez pas avecfetch = FetchType.EAGER
spécifiquement.Il est probable que votre
findTopicByID
service utilise une session Hibernate sans état. Une session sans état n'a pas le cache de premier niveau, c'est-à-dire aucun contexte de persistance. Plus tard, lorsque vous essayez d'itérercomments
, Hibernate lèvera une exception.La solution peut être:
Annoter
comments
avecfetch = FetchType.EAGER
Si vous souhaitez toujours que les commentaires soient chargés paresseusement, utilisez les sessions avec état d'Hibernate , afin de pouvoir récupérer les commentaires plus tard à la demande.
la source
Dans mon cas, j'avais la cartographie n / b
A
etB
commeA
adans le
DAO
calque, la méthode doit être annotée@Transactional
si vous n'avez pas annoté le mappage avec Fetch Type - Désireuxla source
Ce n'est pas la meilleure solution, mais pour ceux qui y font face en
LazyInitializationException
particulier,Serialization
cela aidera. Ici, vous allez vérifier les propriétés initialisées paresseusement et les définirnull
. Pour cela, créez la classe ci-dessousA l'intérieur de votre classe Entity dont les propriétés sont initialisées paresseusement, ajoutez une méthode comme celle illustrée ci-dessous. Ajoutez toutes vos propriétés de chargement paresseux dans cette méthode.
Appelez cette
checkLazyIntialzation()
méthode après sur tous les endroits où vous chargez des données.la source
Salut à tous, nous espérons assez tard que cela aide les autres, Merci d'avance à @GMK pour ce message Hibernate.initialize (object)
lorsque Lazy = "true"
maintenant, si j'accède à «set» après la fermeture de la session, il lève une exception.
Ma solution:
maintenant, je peux accéder à «set» même après avoir fermé la session Hibernate.
la source
Encore une autre façon de faire la chose, vous pouvez utiliser TransactionTemplate pour boucler la recherche paresseuse. Comme
la source
Le problème est dû au fait que le code accède à une relation JPA paresseuse lorsque la «connexion» à la base de données est fermée (le contexte de persistance est le nom correct en termes d'Hibernate / JPA).
Un moyen simple de le résoudre dans Spring Boot consiste à définir une couche de service et à utiliser l'
@Transactional
annotation. Cette annotation dans une méthode crée une transaction qui se propage dans la couche de référentiel et maintient ouvert le contexte de persistance jusqu'à la fin de la méthode. Si vous accédez à la collection à l'intérieur de la méthode transactionnelle, Hibernate / JPA récupérera les données de la base de données.Dans votre cas, il vous suffit d'annoter avec
@Transactional
la méthodefindTopicByID(id)
dans votreTopicService
et de forcer l'extraction de la collection dans cette méthode (par exemple, en demandant sa taille):la source
Pour vous débarrasser de l'exception d'initialisation paresseuse, vous ne devez pas appeler la collecte paresseuse lorsque vous travaillez avec un objet détaché.
À mon avis, la meilleure approche consiste à utiliser le DTO et non l'entité. Dans ce cas, vous pouvez définir explicitement les champs que vous souhaitez utiliser. Comme d'habitude, c'est assez. Pas besoin de s'inquiéter que quelque chose comme jackson
ObjectMapper
, ouhashCode
généré par Lombok appellera implicitement vos méthodes.Pour certains cas spécifiques, vous pouvez utiliser des
@EntityGrpaph
annotations, qui vous permettent d'effectuer uneeager
charge même si vous en avezfetchType=lazy
dans votre entité.la source
Il existe plusieurs solutions pour ce problème d'initialisation paresseuse -
1) Modifiez le type d'association Fetch de LAZY à EAGER mais ce n'est pas une bonne pratique car cela dégraderait les performances.
2) Utilisez FetchType.LAZY sur l'objet associé et utilisez également l'annotation transactionnelle dans votre méthode de couche de service afin que la session reste ouverte et lorsque vous appellerez topicById.getComments (), l'objet enfant (commentaires) sera chargé.
3) Essayez également d'utiliser un objet DTO au lieu d'une entité dans la couche contrôleur. Dans votre cas, la session est fermée au niveau de la couche contrôleur. Il vaut donc mieux convertir l'entité en DTO dans la couche de service.
la source
j'ai résolu en utilisant List au lieu de Set:
la source