Het oplossen van de slaapstand-uitzondering “kan een verzameling rollen niet lui initialiseren”

Ik heb dit probleem:

org.hibernate.LazyInitializationException: kon een verzameling van role: mvc3.model.Topic.comments niet lui initialiseren, er is geen sessie of sessie gesloten

Hier is het model:

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

De controller, die het model aanroept, ziet er als volgt uit:

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

De jsp-pagina ziet er als volgt uit:

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

Uitzondering is verhoogd, bij het bekijken van jsp. In de regel met c:forEachlus


Antwoord 1, autoriteit 100%

Als je weet dat je alle Comments wilt zien elke keer dat je een Topicophaalt, verander dan je veldtoewijzing voor Commentnaar:

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

Collecties worden standaard lui geladen, bekijk ditals je meer wilt weten.


Antwoord 2, autoriteit 86%

Vanuit mijn ervaring heb ik de volgende methoden om de beroemde LazyInitializationException op te lossen:

(1) Hibernate.initialize gebruiken

Hibernate.initialize(topics.getComments());

(2) JOIN FETCH gebruiken

U kunt de JOIN FETCH-syntaxis in uw JPQL gebruiken om de onderliggende verzameling expliciet op te halen. Dit lijkt een beetje op het ophalen van EAGER.

(3) Gebruik OpenSessionInViewFilter

LazyInitializationException komt vaak voor in de weergavelaag. Als u Spring-framework gebruikt, kunt u OpenSessionInViewFilter gebruiken. Ik raad u echter niet aan om dit te doen. Het kan leiden tot prestatieproblemen als het niet correct wordt gebruikt.


Antwoord 3, autoriteit 36%

Ik weet dat het een oude vraag is, maar ik wil je helpen.
U kunt de transactie-annotatie op de servicemethode plaatsen die u nodig hebt, in dit geval zou findTopicByID(id) moeten hebben

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

meer informatie over deze annotatie vindt u hier

Over de andere oplossingen:

fetch = FetchType.EAGER 

is geen goede gewoonte, het mag ALLEEN worden gebruikt als dat nodig is.

Hibernate.initialize(topics.getComments());

De slaapstand-initialisatiefunctie bindt uw klassen aan de slaapstandtechnologie. Als je ernaar streeft om flexibel te zijn, is dat geen goede manier om te gaan.

Hopelijk helpt het


Antwoord 4, autoriteit 29%

De oorsprong van uw probleem:

Standaard laadt hibernate lui de collecties (relaties), wat betekent dat wanneer je de collectionin je code gebruikt (hier commentsveld
in de klas Topic)
de hibernate haalt dat uit de database, nu is het probleem dat je de verzameling in je controller krijgt (waar de
JPA-sessie is gesloten). Dit is de coderegel die de uitzondering veroorzaakt
(waar je de verzameling commentslaadt):

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

Je krijgt een verzameling “opmerkingen” (topic.getComments()) in je controller (waar JPA sessionis beëindigd) en dat veroorzaakt de uitzondering. Ook als je had
de verzameling commentsin je jsp-bestand als volgt (in plaats van het in je controller te krijgen):

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

Je zou om dezelfde reden nog steeds dezelfde uitzondering hebben.

Het probleem oplossen:

Omdat je maar twee verzamelingen kunt hebben met de FetchType.EAGER(gretig opgehaalde verzameling) in een Entity-klasse en omdat lui laden meer is
efficiënt is dan gretig laden, ik denk dat deze manier om je probleem op te lossen beter is dan alleen het FetchTypete veranderen in gretig:

Als je de collectie lazy wilt laten initialiseren en dit ook wilt laten werken,
het is beter om dit codefragment toe te voegen aan uw 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>

Wat deze code doet, is dat het de lengte van uw JPA sessionverlengt of, zoals de documentatie zegt, het wordt gebruikt "to allow for lazy loading in web views despite the original transactions already being completed."dus
zo blijft de JPA sessie wat langer open en daardoor
je kunt lui collecties laden in je jsp-bestanden en controllerklassen.


Antwoord 5, autoriteit 14%

@Controller
@RequestMapping(value = "/topic")
@Transactional

ik los dit probleem op door @Transactionaltoe te voegen, ik denk dat dit de sessie kan openen


Antwoord 6, autoriteit 13%

De reden is dat wanneer je lazy load gebruikt, de sessie wordt gesloten.

Er zijn twee oplossingen.

  1. Gebruik geen lazyload.

    Stel lazy=falsein in XML of stel @OneToMany(fetch = FetchType.EAGER)in in annotatie.

  2. Gebruik luie lading.

    Stel lazy=truein in XML of stel @OneToMany(fetch = FetchType.LAZY)in als annotatie.

    en voeg OpenSessionInViewFilter filtertoe aan uw web.xml

Detail Zie mijn POST.


Antwoord 7, autoriteit 12%

Het probleem wordt veroorzaakt door toegang tot een kenmerk terwijl de slaapstandsessie is gesloten. U heeft geen slaapstandtransactie in de controller.

Mogelijke oplossingen:

  1. Doe al deze logica, in de servicelaag, (met de @Transactional), niet in de controller. Er moet de juiste plaats zijn om dit te doen, het maakt deel uit van de logica van de app, niet in de controller (in dit geval een interface om het model te laden). Alle bewerkingen in de servicelaag moeten transactioneel zijn.
    d.w.z.: verplaats deze regel naar de TopicService.findTopicByID-methode:

    Verzameling commentList = topicById.getComments();

  2. Gebruik ‘gretig’ in plaats van ‘lui’. Nu gebruik je ‘lui’ niet.. het is geen echte oplossing, als je lui wilt gebruiken, werkt als een tijdelijke (zeer tijdelijke) oplossing.

  3. gebruik @Transactional in de controller. Het zou hier niet moeten worden gebruikt, je mengt de servicelaag met de presentatie, het is geen goed ontwerp.
  4. gebruik OpenSessionInViewFilter, veel nadelen gemeld, mogelijke instabiliteit.

Over het algemeen is de beste oplossing de 1.


Antwoord 8, autoriteit 11%

De beste manier om de LazyInitializationExceptionis om het op het moment van de vraag op te halen, zoals dit:

select t
from Topic t
left join fetch t.comments

U moet ALTIJD de volgende anti-patronen vermijden:

Zorg er daarom voor dat uw FetchType.LAZY-koppelingen worden geïnitialiseerd op het moment van de zoekopdracht of binnen het oorspronkelijke bereik van @Transactionalmet behulp van Hibernate.initializevoor secundaire collecties.


Antwoord 9, autoriteit 8%

Om een collectie te lazyloaden, moet er een actieve sessie zijn. In een web-app zijn er twee manieren om dit te doen. U kunt het patroon Open Session In Viewgebruiken, waarbij u een interceptorom de sessie aan het begin van het verzoek en sluit het aan het einde. Het risico is dat je een solide uitzonderingsbehandeling moet hebben of dat je al je sessies zou kunnen vastbinden en je app zou kunnen vastlopen.

De andere manier om dit aan te pakken, is door alle gegevens die u nodig hebt in uw controller te verzamelen, uw sessie af te sluiten en de gegevens vervolgens in uw model te proppen. Ik geef persoonlijk de voorkeur aan deze benadering, omdat het een beetje dichter bij de geest van het MVC-patroon lijkt. Ook als je op deze manier een fout uit de database krijgt, kun je er veel beter mee omgaan dan wanneer het in je view-renderer gebeurt. Je vriend in dit scenario is Hibernate .initialiseren(myTopic.getComments()). U zult het object ook opnieuw aan de sessie moeten koppelen, aangezien u bij elke aanvraag een nieuwe transactie aanmaakt. Gebruik daarvoor session.lock(myTopic,LockMode.NONE).


Antwoord 10, autoriteit 5%

Als je een relatie probeert te maken tussen een entiteit en een verzameling of een lijst met java-objecten (bijvoorbeeld lang type), zou het zoiets als dit willen:

@ElementCollection(fetch = FetchType.EAGER)
    public List<Long> ids;

Antwoord 11, autoriteit 5%

Een van de beste oplossingen is om het volgende toe te voegen aan uw bestand application.properties:
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true


Antwoord 12, autoriteit 4%

Twee dingen die u moet hebben voor fetch = FetchType.LAZY.

@Transactional

en

Hibernate.initialize(topicById.getComments());

Antwoord 13, autoriteit 3%

Ik ontdekte dat het verklaren van @PersistenceContextals EXTENDEDook dit probleem oplost:

@PersistenceContext(type = PersistenceContextType.EXTENDED)

Antwoord 14, autoriteit 3%

Er zijn meerdere oplossingen voor dit luie initialisatieprobleem –

1) Wijzig het koppelingstype Fetch van LAZY in EAGER, maar dit is geen goede gewoonte, omdat dit de prestaties verslechtert.

2) Gebruik FetchType.LAZY op bijbehorend object en gebruik ook Transactionele annotatie in uw servicelaagmethode, zodat de sessie open blijft en wanneer u topicById.getComments() aanroept, wordt het onderliggende object (opmerkingen) geladen.

p>

3) Probeer ook een DTO-object te gebruiken in plaats van een entiteit in de controllerlaag. In jouw geval wordt de sessie gesloten op de controllerlaag. DUS beter om entiteit om te zetten naar DTO in servicelaag.


Antwoord 15, autoriteit 2%

het was het probleem waarmee ik onlangs werd geconfronteerd en dat ik heb opgelost met

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

meer gedetailleerde beschrijving hieren dit redde mijn dag.


Antwoord 16, autoriteit 2%

je lijst laadt traag, dus de lijst is niet geladen.
bellen om op de lijst te komen is niet genoeg.
gebruik in Hibernate.initialize om de lijst te initiëren.
Als dit niet werkt, voert u het lijstelement uit en roept u Hibernate.initialize voor elk aan.
dit moet zijn voordat u terugkeert uit het transactiebereik.
bekijk ditbericht.
zoeken naar –

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

Antwoord 17, autoriteit 2%

Om het probleem in mijn geval op te lossen ontbrak deze regel gewoon

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

in het applicatiecontextbestand.

Er is geen rekening gehouden met de @Transactional-annotatie over een methode.

Ik hoop dat het antwoord iemand zal helpen


Antwoord 18, autoriteit 2%

@Transactionele annotatie op controller ontbreekt

@Controller
@RequestMapping("/")
@Transactional
public class UserController {
}

Antwoord 19, autoriteit 2%

Door de hibernate @Transactionalannotatie te gebruiken, als u een object uit de database krijgt met luie opgehaalde attributen, kunt u deze eenvoudig verkrijgen door deze attributen als volgt op te halen:

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

Hier, in een Hibernate-proxy-beheerde transactie, doet het feit van het aanroepen van ticket.getSales()een andere query om verkopen op te halen, omdat je hier expliciet om hebt gevraagd.


Antwoord 20

Het probleem wordt veroorzaakt doordat de code toegang heeft tot een luie JPA-relatie wanneer de “verbinding” met de database wordt gesloten (persistentiecontextis de juiste naam in termen van Hibernate/JPA).

p>

Een eenvoudige manier om dit op te lossen in Spring Boot is door een servicelaag te definiëren en de annotatie @Transactionalte gebruiken. Deze annotatie in een methode creëert een transactie die zich voortplant in de repository-laag en de persistentiecontext openhoudt totdat de methode is voltooid. Als u de collectie binnen de transactiemethode opent, haalt Hibernate/JPA de gegevens op uit de database.

In jouw geval hoef je alleen maar te annoteren met @Transactionalde methode findTopicByID(id)in je TopicServiceen het ophalen van de verzameling in die methode (bijvoorbeeld door te vragen naar de grootte):

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

Antwoord 21

Voor degenen die werken met Criteria, Ik vond dat

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

heeft alles gedaan wat ik nodig had.

Initiële ophaalmodus voor collecties is ingesteld op FetchMode.LAZY om prestaties te leveren, maar wanneer ik de gegevens nodig heb, voeg ik gewoon die regel toe en geniet van de volledig gevulde objecten.


Antwoord 22

In mijn geval was de volgende code een probleem:

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

Omdat het is losgekoppeld van de database en de sluimerstand niet langer de lijst uit het veld ophaalde wanneer dat nodig was. Dus initialiseer ik het voordat ik het losmaak:

Hibernate.initialize(topicById.getComments());
entityManager.detach(topicById);
topicById.getComments() // works like a charm

Antwoord 23

Niet de beste oplossing, maar voor degenen die te maken hebben met LazyInitializationException, vooral bij Serialization, zal dit helpen. Hier ga je lui geïnitialiseerde eigenschappen controleren en nulldaarop instellen. Maak daarvoor de onderstaande klas

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

Binnen je Entity-klasse waarvan je lui geïnitialiseerde eigenschappen hebt, voeg je een methode toe zoals hieronder wordt getoond. Voeg al uw lui ladende eigenschappen toe binnen deze methode.

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

Noem deze checkLazyIntialzation()methode na op alle plaatsen waar u gegevens laadt.

YourEntity obj= entityManager.find(YourEntity.class,1L);
  obj.checkLazyIntialzation();

Antwoord 24

De reden is dat je de commentList op je controller probeert te krijgen nadat je de sessie binnen de service hebt gesloten.

topicById.getComments();

Hierboven wordt de commentList alleen geladen als je slaapstand actief is, wat ik denk dat je in je service hebt gesloten.

Dus je moet de commentList hebben voordat je de sessie sluit.


Antwoord 25

De verzameling commentsin uw modelklasse Topicis lui geladen, wat het standaardgedrag is als u deze niet annoteert met fetch = FetchType.EAGERspecifiek.

Het is hoogstwaarschijnlijk dat uw findTopicByID-service een stateless Hibernate-sessie gebruikt. Een stateless sessieheeft niet de eerste niveau cache, dat wil zeggen, geen persistentie-context. Wanneer u later commentsprobeert te herhalen, zal Hibernate een uitzondering genereren.

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

De oplossing kan zijn:

  1. Annoteer commentsmet fetch = FetchType.EAGER

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)
    private Collection<Comment> comments = new LinkedHashSet<Comment>();
    
  2. Als je nog steeds wilt dat reacties lui worden geladen, gebruik dan Hibernate’s stateful-sessies, zodat u later op verzoek opmerkingen kunt ophalen.


Antwoord 26

In mijn geval had ik de toewijzing b/w Aen Bzoals

Aheeft

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

in de DAO-laag moet de methodeworden geannoteerd met @Transactionalals u de toewijzing niet heeft geannoteerd met Ophaaltype – enthousiast


Antwoord 27

Dit is een oude vraag, maar de onderstaande informatie kan mensen helpen die hier een antwoord op zoeken.

Het antwoord van @VladMihalcea is nuttig. U mag niet vertrouwen op FetchType.EAGER, in plaats daarvan moet u de opmerkingen indien nodig in de entiteit Topicladen.

Als u uw zoekopdrachten niet expliciet definieert, zodat u een join fetchkunt specificeren, gebruik dan @NamedEntityGraphen @EntityGraphu kuntde FetchType.LAZY(@OneToMany-associaties gebruiken standaard LAZY) tijdens runtimeen de opmerkingen laden op de tegelijkertijd met het Topicalleen indien nodig. Dat betekent dat u het laden van de opmerkingen beperkt tot alleen die methoden (query’s) die dat echt vereisen. Een entiteitsgrafiek zoals JPA definieerthet:

Een entiteitsgrafiek kan worden gebruikt met de vindmethode of als een query-hint om
overschrijven of uitbreiden van FetchType-semantiek.

Je zou het kunnen gebruiken op basis van het JPA-voorbeeld hier. Als u Spring Data JPA gebruikt, kunt u deze ook gebruiken op basis van het voorbeeld geleverd door Spring.


Antwoord 28

Hallo iedereen die vrij laat post, hoop dat het anderen helpt,
Bij voorbaat dank aan @GMK voor dit bericht Hibernate.initialize(object)

wanneer Lazy=”true”

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

Als ik nu ‘set’ open na het sluiten van de sessie, wordt er een uitzondering gegenereerd.

Mijn oplossing:

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

nu heb ik toegang tot ‘set’, zelfs na het sluiten van de sluimerstand.


Antwoord 29

Nog een andere manier om het te doen, je kunt TransactionTemplateom de luie fetch te omzeilen.
Vind ik leuk

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

Antwoord 30

Om van de luie initialisatie-uitzondering af te komen, moet je geen lazy collection aanroepen als je met een losstaand object werkt.

Naar mijn mening is de beste aanpak om DTO te gebruiken, en niet de entiteit. In dit geval kunt u expliciet velden instellen die u wilt gebruiken. Zoals gewoonlijk is het genoeg. U hoeft zich geen zorgen te maken dat iets als jackson ObjectMapperof hashCodegegenereerd door Lombok uw methoden impliciet aanroept.

Voor sommige specifieke gevallen kunt u de annotatie @EntityGrpaphgebruiken, waarmee u eagerkunt laten laden, zelfs als u fetchType=lazyin uw entiteit.

Other episodes