Comment résoudre l'exception "Hibernate" d'initialisation paresseuse d'une collection de rôles "

363

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

Eugène
la source

Réponses:

214

Si vous savez que vous voudrez voir tous les Comments à chaque fois que vous récupérez un, Topicmodifiez votre mappage de champ pour comments:

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

Les collections sont chargées paresseusement par défaut, jetez un œil à ceci si vous voulez en savoir plus.

darrengorman
la source
35
Désolé, mais j'aimerais utiliser lazy-load. J'ai donc changé le type 'LinkedHashSet' en 'PersistentList'. Une exception se produit toujours
Eugene
242
Cela peut être utilisé comme solution de contournement, mais pas comme une véritable solution au problème. Et si nous devons aller chercher paresseusement?
Dkyc
14
mais au cas où si nous voulons paresseux, cette solution ne fonctionnera pas et la plupart des cas, nous voulons seulement paresseux.
prashant thakre
103
C'est le type de réponse qui apparaît partout en cas de débordement de pile. Bref, au point, résout le problème et les erreurs. Aux futurs lecteurs, rendez-vous service et apprenez ce qui est exactement paresseux et recherché avec impatience, et comprenez les conséquences.
Ced
13
@darrengorman Quand j'ai commencé JPA, j'ai posté une question dans le sens de celle d'OP. J'ai reçu la même réponse que vous avez donnée. Assez tôt quand j'ai effectué un test avec des centaines de milliers de lignes, devinez ce qui s'est passé? Je pense que c'est trompeur car il fournit une réponse trop simple à un problème auquel les débutants seront le plus souvent confrontés et assez rapidement ils auront toute leur base de données chargée en mémoire s'ils ne font pas attention (et ils ne le feront pas, car ils ne le feront pas sachez-le) :).
Ced
182

D'après mon expérience, j'ai les méthodes suivantes pour résoudre la fameuse 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 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.

Boris
la source
5
(1) a parfaitement fonctionné pour moi. Mon cas: Hibernate.initialize (registry.getVehicle (). GetOwner (). GetPerson (). GetAddress ());
Leonel Sanches da Silva
6
Il semble que Hibernate.initialize ne fonctionne pas avec
EntityManager
8
Ce devrait être la bonne réponse. Par exemple, dans mon projet au travail, nous ne sommes explicitement pas censés utiliser la récupération EAGER. Cela cause des problèmes dans ce système particulier.
Steve Waters
Semble attrayant mais manque de documentation à implémenter dans un autre cas ... pourriez-vous s'il vous plaît fournir quelques liens ou explications supplémentaires sur la façon d'implémenter cette solution?
Pipo
58

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

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

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

À propos des autres solutions:

fetch = FetchType.EAGER 

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

Hibernate.initialize(topics.getComments());

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

sarbuLopex
la source
3
L'annotation @Transactional a fonctionné pour moi, mais notez que Propagation.REQUIRED est la valeur par défaut, au moins dans Spring Boot 1.4.2 (Spring 4.3).
ben3000
4
Oui, mais j'ai pensé qu'il pourrait être apprécié de préciser que vous pouvez réellement modifier le
paramètre de
N'est-ce pas @Transactionalune chose réservée au printemps?
Campa
@Campa oui c'est ça. Si vous voulez le gérer manuellement, vous devez mettre votre logique métier dans une transaction récupérée du gestionnaire d'entités
sarbuLopex
54

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 collectiondans votre code ( commentschamp ici dans la Topicclasse), 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 la commentscollection):

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

Vous obtenez la collection "comments" (topic.getComments ()) dans votre contrôleur (où JPA sessions'est terminée) et cela provoque l'exception. De plus, si vous aviez obtenu la commentscollection 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.

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 le FetchTypeen 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:

<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 que ce code fait, c'est qu'il augmentera la longueur de votre JPA sessionou, 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. .

Gandalf
la source
7
Pourquoi la session JPS est-elle fermée? Comment faire pour qu'il ne soit pas fermé? Comment effectuer une collection paresseuse?
Dims
1
Qu'est-ce qui définit une limite de deux collections FetchType.Eager par entité?
chrisinmtown
Dans Spring Boot, vous pouvez ajouter 'spring.jpa.open-in-view = true' à 'application.properties'
Askar
28

La raison en est que lorsque vous utilisez le chargement différé, la session est fermée.

Il y a deux solutions.

  1. N'utilisez pas de charge paresseuse.

    Définir lazy=falseen XML ou @OneToMany(fetch = FetchType.EAGER)annoter Définir dans.

  2. Utilisez la charge paresseuse.

    Définir lazy=trueen XML ou @OneToMany(fetch = FetchType.LAZY)annoter Définir dans.

    et ajoutez OpenSessionInViewFilter filtervotreweb.xml

Détails Voir mon POST .

saneryee
la source
1
... et pourtant les deux solutions ne sont pas bonnes. Suggérer d'utiliser EAGER peut créer d'énormes problèmes. L'utilisation d'OpenSessionInViewFilter est un anti-modèle.
Rafael
27
@Controller
@RequestMapping(value = "/topic")
@Transactional

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

RuiZhi Wang
la source
Pourquoi cela a-t-il reçu un vote négatif? L'ajout d'une transaction à l'opération prolonge la session
Tudor Grigoriu
1
Il est inapproprié de publier @Transactional sur le contrôleur.
Rafael
@Rafael Pourquoi c'est une mauvaise pratique?
Amr Ellafy
@AmrEllafy -> Voici une bonne explication: stackoverflow.com/a/18498834/1261162
Rafael
22

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:

  1. 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 ();

  2. 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).

  3. utilisez @Transactional dans le contrôleur . Il ne doit pas être utilisé ici, vous mélangez la couche de service avec la présentation, ce n'est pas un bon design.
  4. utiliser OpenSessionInViewFilter , nombreux inconvénients signalés, instabilité possible.

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

abentan
la source
2
Le type de récupération d'Eager suppose que la mise en veille prolongée sera extraite de toutes les données dans la première requête, pas à tous les endroits où elles sont correctement
Жасулан Бердибеков
Vous devriez MAJUSCULER que la MEILLEURE SOLUTION EST 1 ... en fait, c'est la SEULE BONNE solution car toutes les autres sont anti-modèles!
Rafael
19

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.

GMK
la source
15

Comme je l'ai expliqué dans cet article , la meilleure façon de gérer le LazyInitializationExceptionest 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 FetchType.LAZYassociations sont initialisées au moment de la requête ou dans la @Transactionalportée d' origine à l' aide Hibernate.initializede collections secondaires.

Vlad Mihalcea
la source
1
Vlad, avez-vous des suggestions pour travailler avec une collection initialisée paresseusement dans une entité récupérée par la méthode findById () d'un référentiel généré par Spring? Je n'écris pas la requête et la transaction est en dehors de mon code.
chrisinmtown
Consultez cet article pour plus de détails sur l'initialisation des collections paresseuses.
Vlad Mihalcea
Pourriez-vous clarifier ce que vous entendez par `` dans la portée @Transactional originale '' Cela ne me semble pas clair car je semble obtenir cette erreur lors d'une session ouverte (mais pas la bonne?)
Michiel Haisma
À l'intérieur de la portée de la méthode de service transactionnel la plus élevée, également connue sous le nom de passerelle de transaction. Découvrez la TrassctionInterceptortrace de la pile et c'est celle-là.
Vlad Mihalcea
Une des meilleures réponses de loin ... cela devrait être marqué comme correct. BTW ... en supposant que l'OSIV est un anti-modèle, comment est-il possible qui est activé par défaut sur les versions récentes à ressort? ... n'est-ce pas si mal?
Rafael
10

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:

@ElementCollection(fetch = FetchType.EAGER)
    public List<Long> ids;
javaboygo
la source
1
dans de nombreux cas, vous ne voulez vraiment pas faire cela. Vous perdez tous les avantages du chargement paresseux ici
kiedysktos
Utiliser EAGER n'est pas une solution professionnelle.
Rafael
9

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

sreekmatta
la source
1
Pouvez-vous dire à l'OP ce qu'il fait exactement, les effets secondaires, l'impact sur les performances?
PeS
3
À l'arrière du chargement paresseux, une nouvelle session est bifurquée chaque fois qu'une association est chargée paresseusement, donc plus de connexions sont bifurquées et crée un peu de pression sur le pool de connexions. Si vous avez une limite sur le nombre de connexions, cette propriété n'est peut-être pas correcte à utiliser.
sreekmatta
2
pour certains, il est considéré comme un anti-pattern vladmihalcea.com/…
Uri Loya
7

J'ai découvert que déclarer @PersistenceContextcomme EXTENDEDrésout également ce problème:

@PersistenceContext(type = PersistenceContextType.EXTENDED)
Elcin ABD
la source
1
Salut, soyez prudent avec de tels changements. La création de contexte de persistance de portée TRANSACTION est paresseuse, ce qui était l'intention du PO. La question est donc de savoir si vous voulez être apatride ou non. Ce paramètre dépend de l'objectif du système et ne doit pas être modifié trop ... avec impatience. Si tu vois ce que je veux dire. Lisez ici stackoverflow.com/questions/2547817/…
kiedysktos
Dangereux. Ce n'est pas la bonne réponse. Il y en a d'autres ci-dessus beaucoup plus précis et sûrs.
Rafael
5

c'était 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" />

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

tolgayilmaz
la source
5

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 -

Node n = // .. get the node
Hibernate.initialize(n); // initializes 'parent' similar to getParent.
Hibernate.initialize(n.getChildren()); // pass the lazy collection into the session 
Avihai Marchiano
la source
4

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' @Transactionalannotation sur une méthode n'a pas été prise en compte.

J'espère que la réponse aidera quelqu'un

Mario Biasi
la source
4

L'annotation @Transactional sur le contrôleur est manquante

@Controller
@RequestMapping("/")
@Transactional
public class UserController {
}
Xcoder
la source
17
Je dirais que la gestion des transactions appartient à la couche de service où réside la logique métier.
Sõber
L'annotation transactionnelle n'est pas manquante. Le contrôleur ne devrait pas avoir une telle annotation. Ces annotations doivent être au niveau du service.
Rafael
4

En utilisant l' @Transactionalannotation 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:

@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 les ventes car vous l'avez explicitement demandé.

Alex
la source
4

Vous devriez avoir deux choses fetch = FetchType.LAZY.

@Transactional

et

Hibernate.initialize(topicById.getComments());
Karan Patel
la source
2

Pour ceux qui travaillent avec Criteria , j'ai trouvé que

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

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.

velis
la source
2

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

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

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.

topicById.getComments();

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.

latte aditya
la source
2
Oui, c'est l'énoncé du problème, vous devez également fournir une réponse dans unAnswer
Sarz
1

La collection commentsde votre classe de modèle Topicest chargée paresseusement, ce qui est le comportement par défaut si vous ne l'annotez pas avec fetch = FetchType.EAGERspécifiquement.

Il est probable que votre findTopicByIDservice 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érer comments, Hibernate lèvera 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:

  1. Annoter commentsavecfetch = FetchType.EAGER

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)   
    private Collection<Comment> comments = new LinkedHashSet<Comment>();
  2. 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.

Yuci
la source
1

Dans mon cas, j'avais la cartographie n / b Aet Bcomme

A a

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

dans le DAOcalque, la méthode doit être annotée @Transactionalsi vous n'avez pas annoté le mappage avec Fetch Type - Désireux

Shirgill Farhan
la source
1

Ce n'est pas la meilleure solution, mais pour ceux qui y font face en LazyInitializationExceptionparticulier, Serializationcela aidera. Ici, vous allez vérifier les propriétés initialisées paresseusement et les définir null. Pour cela, créez la classe ci-dessous

public class RepositoryUtil {
    public static final boolean isCollectionInitialized(Collection<?> collection) {
        if (collection instanceof PersistentCollection)
            return ((PersistentCollection) collection).wasInitialized();
        else 
            return true;
    }   
}

A 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.

public void checkLazyIntialzation() {
    if (!RepositoryUtil.isCollectionInitialized(yourlazyproperty)) {
        yourlazyproperty= null;
    }

Appelez cette checkLazyIntialzation()méthode après sur tous les endroits où vous chargez des données.

 YourEntity obj= entityManager.find(YourEntity.class,1L);
  obj.checkLazyIntialzation();
Javad
la source
0

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"

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

maintenant, si j'accède à «set» après la fermeture de la session, il lève une 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 avoir fermé la session Hibernate.

vic
la source
0

Encore une autre façon de faire la chose, vous pouvez utiliser TransactionTemplate pour boucler la recherche paresseuse. Comme

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

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' @Transactionalannotation. 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 @Transactionalla méthode findTopicByID(id)dans votre TopicServiceet de forcer l'extraction de la collection dans cette méthode (par exemple, en demandant sa taille):

    @Transactional(readOnly = true)
    public Topic findTopicById(Long id) {
        Topic topic = TopicRepository.findById(id).orElse(null);
        topic.getComments().size();
        return topic;
    }
Domingo Gallardo
la source
0

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, ou hashCodegénéré par Lombok appellera implicitement vos méthodes.

Pour certains cas spécifiques, vous pouvez utiliser des @EntityGrpaphannotations, qui vous permettent d'effectuer une eagercharge même si vous en avez fetchType=lazydans votre entité.

deg
la source
0

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.

vsharma
la source
-11

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

private List<Categories> children = new ArrayList<Categories>();
SaganTheBest
la source