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 Comment
s wilt zien elke keer dat je een Topic
ophaalt, verander dan je veldtoewijzing voor Comment
naar:
@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 collection
in je code gebruikt (hier comments
veld
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 comments
laadt):
Collection<Comment> commentList = topicById.getComments();
Je krijgt een verzameling “opmerkingen” (topic.getComments()) in je controller (waar JPA session
is beëindigd) en dat veroorzaakt de uitzondering. Ook als je had
de verzameling comments
in 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 FetchType
te 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 session
verlengt 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 @Transactional
toe 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.
-
Gebruik geen lazyload.
Stel
lazy=false
in in XML of stel@OneToMany(fetch = FetchType.EAGER)
in in annotatie. -
Gebruik luie lading.
Stel
lazy=true
in in XML of stel@OneToMany(fetch = FetchType.LAZY)
in als annotatie.en voeg
OpenSessionInViewFilter filter
toe aan uwweb.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:
-
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();
-
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.
- gebruik @Transactional in de controller. Het zou hier niet moeten worden gebruikt, je mengt de servicelaag met de presentatie, het is geen goed ontwerp.
- 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 LazyInitializationException
is 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:
FetchType.EAGER
- OSIV (open sessie in beeld)
hibernate.enable_lazy_load_no_trans
Configuratie-eigenschap voor slaapstand
Zorg er daarom voor dat uw FetchType.LAZY
-koppelingen worden geïnitialiseerd op het moment van de zoekopdracht of binnen het oorspronkelijke bereik van @Transactional
met behulp van Hibernate.initialize
voor 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 @PersistenceContext
als EXTENDED
ook 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 @Transactional
annotatie 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 @Transactional
te 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 @Transactional
de methode findTopicByID(id)
in je TopicService
en 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 null
daarop 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 comments
in uw modelklasse Topic
is lui geladen, wat het standaardgedrag is als u deze niet annoteert met fetch = FetchType.EAGER
specifiek.
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 comments
probeert 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:
-
Annoteer
comments
metfetch = FetchType.EAGER
@OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL) private Collection<Comment> comments = new LinkedHashSet<Comment>();
-
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 A
en B
zoals
A
heeft
@OneToMany(mappedBy = "a", cascade = CascadeType.ALL)
Set<B> bs;
in de DAO
-laag moet de methodeworden geannoteerd met @Transactional
als 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 Topic
laden.
Als u uw zoekopdrachten niet expliciet definieert, zodat u een join fetch
kunt specificeren, gebruik dan @NamedEntityGraph
en @EntityGraph
u kuntde FetchType.LAZY
(@OneToMany
-associaties gebruiken standaard LAZY) tijdens runtimeen de opmerkingen laden op de tegelijkertijd met het Topic
alleen 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 ObjectMapper
of hashCode
gegenereerd door Lombok uw methoden impliciet aanroept.
Voor sommige specifieke gevallen kunt u de annotatie @EntityGrpaph
gebruiken, waarmee u eager
kunt laten laden, zelfs als u fetchType=lazy
in uw entiteit.