Mam ten problem:
org.hibernate.LazyInitializationException: nie udało się leniwie zainicjować kolekcji roli: mvc3.model.Topic.comments, żadna sesja ani sesja nie została zamknięta
Oto 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;
}
}
Kontroler, który wywołuje model, wygląda następująco:
@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);
}
}
Strona jsp wygląda następująco:
<%@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>
Wyjątek powstaje podczas przeglądania jsp. W linii z pętlą c: forEach
Z własnego doświadczenia mam następujące metody rozwiązania słynnego wyjątku LazyInitializationException:
(1) Użyj Hibernate.initialize
(2) Użyj JOIN FETCH
Możesz użyć składni JOIN FETCH w swoim JPQL, aby jawnie pobrać kolekcję potomną. To trochę jak ściąganie EAGERA.
(3) Użyj OpenSessionInViewFilter
LazyInitializationException często występują w warstwie widoku. Jeśli korzystasz z frameworka Spring, możesz użyć OpenSessionInViewFilter. Jednak nie sugeruję, abyś to zrobił. Może to prowadzić do problemów z wydajnością, jeśli nie będzie używane poprawnie
źródło
Wiem, że to stare pytanie, ale chcę pomóc. Możesz umieścić adnotację transakcyjną na potrzebnej metodzie usługi, w takim przypadku findTopicByID (id) powinien mieć
więcej informacji na temat tej adnotacji można znaleźć tutaj
O innych rozwiązaniach:
nie jest dobrą praktyką, należy ją stosować WYŁĄCZNIE w razie potrzeby.
Inicjator hibernacji wiąże klasy z technologią hibernacji. Jeśli chcesz być elastyczny, nie jest to dobra droga.
Mam nadzieję, że to pomoże
źródło
@Transactional
jest tylko wiosna?Geneza twojego problemu:
Domyślnie hibernacja leniwie ładuje kolekcje (relacje), co oznacza, że za każdym razem, gdy używasz
collection
w swoim kodzie (tutajcomments
pole wTopic
klasie) hibernacja pobiera to z bazy danych, teraz problem polega na tym, że otrzymujesz kolekcję w kontrolerze (gdzie sesja JPA jest zamknięty) .Jest to wiersz kodu, który powoduje wyjątek (gdzie ładujeszcomments
kolekcję):Otrzymujesz kolekcję „komentarzy” (topic.getComments ()) w kontrolerze (gdzie
JPA session
się zakończyła), co powoduje wyjątek. Również jeśli maszcomments
kolekcję w swoim pliku jsp w ten sposób (zamiast pobierać ją do kontrolera):Nadal będziesz mieć ten sam wyjątek z tego samego powodu.
Rozwiązanie problemu:
Ponieważ możesz mieć tylko dwie kolekcje z
FetchType.Eager
(kolekcją chętnie pobieraną) w klasie Entity i ponieważ opóźnione ładowanie jest bardziej wydajne niż ładowanie z niecierpliwością, uważam, że ten sposób rozwiązania problemu jest lepszy niż zmiana naFetchType
chętną:Jeśli chcesz zainicjować opóźnioną kolekcję, a także sprawić, by działała, lepiej dodać ten fragment kodu do
web.xml
:Ten kod powoduje, że zwiększy długość twojego
JPA session
lub, jak mówi dokumentacja, jest używany,"to allow for lazy loading in web views despite the original transactions already being completed."
więc w ten sposób sesja JPA będzie otwarta nieco dłużej i dzięki temu możesz leniwie ładować kolekcje w plikach jsp i klasach kontrolerów .źródło
Powodem jest to, że gdy używasz leniwego ładowania, sesja jest zamknięta.
Istnieją dwa rozwiązania.
Nie używaj leniwego ładowania.
Ustaw
lazy=false
w XML lub Ustaw w@OneToMany(fetch = FetchType.EAGER)
adnotacji.Użyj leniwego ładowania.
Ustaw
lazy=true
w XML lub Ustaw@OneToMany(fetch = FetchType.LAZY)
adnotacji.i dodaj
OpenSessionInViewFilter filter
swójweb.xml
Szczegóły Zobacz mój test POST .
źródło
Rozwiązuję ten problem, dodając.
@Transactional
Myślę, że może to otworzyć sesjęźródło
Problem jest spowodowany dostępem do atrybutu przy zamkniętej sesji hibernacji. Nie masz transakcji hibernacji w kontrolerze.
Możliwe rozwiązania:
Zrób całą tę logikę w warstwie serwisowej (za pomocą @Transactional), a nie w kontrolerze. Powinno to być odpowiednie miejsce, aby to zrobić, jest to część logiki aplikacji, a nie kontrolera (w tym przypadku interfejs do ładowania modelu). Wszystkie operacje w warstwie usług powinny być transakcyjne. tj .: Przenieś tę linię do metody TopicService.findTopicByID:
Kolekcja commentList = topicById.getComments ();
Użyj „chętny” zamiast „leniwy” . Teraz nie używasz „leniwego” .. to nie jest prawdziwe rozwiązanie, jeśli chcesz użyć leniwego, działa jak tymczasowe (bardzo tymczasowe) obejście.
Ogólnie najlepszym rozwiązaniem jest 1.
źródło
Aby leniwie załadować kolekcję, musi istnieć aktywna sesja. W aplikacji internetowej można to zrobić na dwa sposoby. Możesz użyć wzorca Otwórz sesję w widoku , w którym używasz przechwytywacza, aby otworzyć sesję na początku żądania i zamknąć ją na końcu. Istnieje ryzyko, że musisz mieć solidną obsługę wyjątków lub możesz powiązać wszystkie sesje, a aplikacja może się zawiesić.
Innym sposobem na poradzenie sobie z tym jest zebranie wszystkich potrzebnych danych w kontrolerze, zamknięcie sesji, a następnie włożenie danych do modelu. Osobiście wolę to podejście, ponieważ wydaje się nieco bliższe duchowi wzoru MVC. Również jeśli otrzymujesz błąd z bazy danych w ten sposób, możesz sobie z nim poradzić znacznie lepiej niż gdyby to się wydarzyło w twoim rendererze widoku. Twoim przyjacielem w tym scenariuszu jest Hibernate.initialize (myTopic.getComments ()). Będziesz także musiał ponownie dołączyć obiekt do sesji, ponieważ tworzysz nową transakcję przy każdym żądaniu. W tym celu użyj session.lock (myTopic, LockMode.NONE).
źródło
Jak wyjaśniłem w tym artykule , najlepszym sposobem, aby sobie z tym poradzić,
LazyInitializationException
jest pobranie go po czasie zapytania:ZAWSZE należy unikać następujących anty-wzorów:
hibernate.enable_lazy_load_no_trans
właściwości konfiguracji HibernacjaDlatego upewnij się, że Twoje
FetchType.LAZY
skojarzenia zostały zainicjowane w czasie zapytania lub w oryginalnym@Transactional
zakresie przy użyciuHibernate.initialize
kolekcji wtórnych.źródło
TrassctionInterceptor
ślad stosu i to jest to.Jeśli próbujesz utworzyć relację między bytem a kolekcją lub listą obiektów Java (na przykład typu Long), chciałbyś otrzymać coś takiego:
źródło
Jednym z najlepszych rozwiązań jest dodanie następujących plików do pliku application.properties: spring.jpa.properties.hibernate.enable_lazy_load_no_trans = true
źródło
Dowiedziałem się, że zadeklarowanie
@PersistenceContext
jakoEXTENDED
również rozwiązuje ten problem:źródło
był to problem, z którym ostatnio się spotkałem, który rozwiązałem za pomocą
bardziej szczegółowe opisy tutaj i to uratowało mi dzień.
źródło
twoja lista jest ładna, więc lista nie została załadowana. połączenie, aby wejść na listę, nie wystarczy. użyj w Hibernate.initialize w celu zainicjowania listy. Jeśli dosnt działa na elemencie listy i wywołuje Hibernate.initialize dla każdego. musi to nastąpić przed powrotem z zakresu transakcji. spójrz na ten post.
Szukaj -
źródło
Aby rozwiązać problem w moim przypadku, po prostu brakowało tej linii
w pliku kontekstu aplikacji.
@Transactional
Adnotacji na metody nie została uwzględniona.Mam nadzieję, że odpowiedź pomoże komuś
źródło
@ Brak adnotacji transakcyjnej na kontrolerze
źródło
Za pomocą
@Transactional
adnotacji hibernacji , jeśli otrzymujesz obiekt z bazy danych z leniwie pobranymi atrybutami, możesz po prostu uzyskać je, pobierając te atrybuty w następujący sposób:Tutaj, w transakcji Hibernate zarządzanej przez proxy, fakt wywołania
ticket.getSales()
wykonuje kolejne zapytanie w celu pobrania sprzedaży, ponieważ zostało to wyraźnie zadane.źródło
Dwie rzeczy, które powinieneś mieć
fetch = FetchType.LAZY
.i
źródło
Dla osób pracujących z Criteria znalazłem to
zrobiłem wszystko, co potrzebne.
Początkowy tryb pobierania dla kolekcji jest ustawiony na FetchMode.LAZY, aby zapewnić wydajność, ale kiedy potrzebuję danych, po prostu dodam tę linię i cieszę się z w pełni wypełnionych obiektów.
źródło
W moim przypadku problemem był następujący kod:
Ponieważ odłączył się od bazy danych i Hibernacja nie pobierała już listy z pola, gdy była potrzebna. Więc inicjuję go przed odłączeniem:
źródło
Powodem jest to, że próbujesz uzyskać listę komentarza na kontrolerze po zamknięciu sesji w usłudze.
Powyżej załaduje commentList tylko wtedy, gdy twoja sesja hibernacji jest aktywna, co, jak sądzę, zostało zamknięte w twoim serwisie.
Musisz więc uzyskać listę komentarzy przed zamknięciem sesji.
źródło
Answer
Kolekcja
comments
w twojej klasie modeluTopic
jest leniwie ładowana, co jest domyślnym zachowaniem, jeśli nie dodasz do niej adnotacjifetch = FetchType.EAGER
.Najprawdopodobniej
findTopicByID
usługa korzysta z bezstanowej sesji hibernacji. Sesja bezstanowa nie ma pamięci podręcznej pierwszego poziomu, tzn. Nie ma kontekstu trwałości. Później, gdy spróbujesz wykonać iteracjęcomments
, Hibernacja zgłosi wyjątek.Rozwiązaniem może być:
Adnotuj za
comments
pomocąfetch = FetchType.EAGER
Jeśli nadal chcesz, aby komentarze były ładowane leniwie, skorzystaj z sesji stanowych Hibernacji , abyś mógł później pobierać komentarze na żądanie.
źródło
W moim przypadku miałem mapowanie czarno-białe
A
iB
podobneA
maw
DAO
warstwie metoda musi zostać opatrzona adnotacjami,@Transactional
jeśli mapowanie nie zostało opatrzone adnotacjami typem pobierania - chętnyźródło
Nie najlepsze rozwiązanie, ale dla tych, którzy stoją przed tym
LazyInitializationException
szczególnieSerialization
, pomoże. Tutaj sprawdzisz leniwie zainicjowane właściwości i ustawienianull
dla nich. W tym celu utwórz niższą klasęWewnątrz klasy Entity, którą leniwie inicjujesz, dodaj metodę pokazaną poniżej. Dodaj wszystkie swoje leniwe właściwości ładowania w ramach tej metody.
Nazwij tę
checkLazyIntialzation()
metodę po wszystkich miejscach, w których ładujesz dane.źródło
Cześć Wszystkie posty dość późno, mam nadzieję, że to pomoże innym, z góry dziękując @GMK za ten post Hibernate.initialize (obiekt)
kiedy Lazy = „prawda”
teraz, jeśli uzyskam dostęp do „set” po zamknięciu sesji, zgłasza wyjątek.
Moje rozwiązanie:
teraz mogę uzyskać dostęp do „set” nawet po zamknięciu sesji hibernacji.
źródło
Jeszcze inny sposób, aby to zrobić, możesz użyć TransactionTemplate, aby owinąć leniwe pobieranie. Lubić
źródło
Problem jest spowodowany, ponieważ kod uzyskuje dostęp do leniwej relacji JPA, gdy „połączenie” z bazą danych jest zamknięte ( kontekst trwałości jest poprawną nazwą w kategoriach Hibernacja / JPA).
Prostym sposobem rozwiązania tego problemu w Spring Boot jest zdefiniowanie warstwy usługi i użycie
@Transactional
adnotacji. Ta adnotacja w metodzie tworzy transakcję, która propaguje się do warstwy repozytorium i utrzymuje otwarty kontekst trwałości aż do zakończenia metody. Jeśli uzyskasz dostęp do kolekcji w ramach metody transakcyjnej Hibernacja / JPA pobierze dane z bazy danych.W twoim przypadku wystarczy dodać adnotację
@Transactional
do metodyfindTopicByID(id)
w swoimTopicService
pliku i wymusić pobranie kolekcji w tej metodzie (na przykład poprzez podanie jej rozmiaru):źródło
Aby pozbyć się leniwego wyjątku inicjalizacji, nie powinieneś dzwonić do leniwej kolekcji podczas pracy z odłączonym obiektem.
Moim zdaniem najlepszym podejściem jest użycie DTO, a nie encji. W takim przypadku możesz jawnie ustawić pola, których chcesz użyć. Jak zwykle to wystarczy. Nie musisz się martwić, że coś takiego jak Jackson
ObjectMapper
lubhashCode
wygenerowane przez Lombok wywoła twoje metody niejawnie.W niektórych szczególnych przypadkach możesz użyć
@EntityGrpaph
adnotacji, które pozwalają naeager
obciążenie, nawet jeśli masz jefetchType=lazy
w swojej jednostce.źródło
Istnieje wiele rozwiązań tego problemu z leniwą inicjalizacją -
1) Zmień typ pobierania skojarzenia z LAZY na EAGER, ale nie jest to dobra praktyka, ponieważ obniży to wydajność.
2) Użyj FetchType.LAZY na powiązanym obiekcie, a także użyj adnotacji transakcyjnej w metodzie warstwy usługi, aby sesja pozostała otwarta, a kiedy wywołasz topicById.getComments (), obiekt potomny (komentarze) zostanie załadowany.
3) Spróbuj także użyć obiektu DTO zamiast bytu w warstwie kontrolera. W twoim przypadku sesja jest zamykana na warstwie kontrolera. TAK więc lepiej przekonwertować jednostkę na DTO w warstwie serwisowej.
źródło
Rozwiązałem przy użyciu listy zamiast zestawu:
źródło