Jak rozwiązać wyjątek Hibernacji „nie udało się leniwie zainicjować zbioru roli”

363

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

Eugene
źródło

Odpowiedzi:

214

Jeśli wiesz, że będziesz chciał widzieć wszystkie Comments przy każdym pobieraniu Topica, zmień mapowanie pola na comments:

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

Kolekcje są domyślnie ładowane z opóźnieniem, spójrz na to, jeśli chcesz dowiedzieć się więcej.

darrengorman
źródło
35
Przepraszam, ale chciałbym użyć leniwego ładowania. Dlatego zmieniłem typ „LinkedHashSet” t „PersistentList”. Nadal występuje wyjątek
Eugene
242
Może to być wykorzystane jako obejście, ale nie faktyczne rozwiązanie problemu. Co jeśli będziemy musieli przynosić leniwie?
Dycyc
14
ale w przypadku, gdy chcemy leniwy, to rozwiązanie nie będzie działać i większość przypadków chcemy tylko leniwi.
prashant thakre
103
Jest to typ odpowiedzi, która pojawia się wszędzie po przepełnieniu stosu. Krótko mówiąc, rozwiązuje problem i NIEPRAWIDŁOWOŚĆ. Dla przyszłych czytelników zrób sobie przysługę i dowiedz się, co dokładnie jest leniwe i chętnie sprowadzone, i zrozum konsekwencje.
Ced
13
@darrengorman Kiedy zacząłem JPA, zadałem pytanie wokół linii OP. Otrzymałem taką samą odpowiedź jak ty. Wkrótce, kiedy przeprowadzę test z setkami tysięcy wierszy, zgadnij, co się stało? Myślę, że jest to mylące, ponieważ stanowi zbyt prostą odpowiedź na problem, z którym najczęściej borykają się początkujący, a wkrótce będą mieli całą bazę danych załadowaną do pamięci, jeśli nie będą ostrożni (i nie zrobią tego, ponieważ nie zrobią tego bądź tego świadomy) :).
Ced
182

Z własnego doświadczenia mam następujące metody rozwiązania słynnego wyjątku LazyInitializationException:

(1) Użyj Hibernate.initialize

Hibernate.initialize(topics.getComments());

(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

Boris
źródło
5
(1) działał dla mnie idealnie. Mój przypadek: Hibernate.initialize (register.getVehicle (). GetOwner (). GetPerson (). GetAddress ());
Leonel Sanches da Silva
6
Wygląda na to, że Hibernate.initialize nie działa z EntityManager
marionmaiden
8
To powinna być poprawna odpowiedź. Na przykład w moim projekcie w pracy wyraźnie nie powinniśmy używać pobierania EAGER. Powoduje problemy w tym konkretnym systemie.
Steve Waters
Wydaje się atrakcyjne, ale brak dokumentacji do wdrożenia w innym przypadku ... czy możesz podać więcej linków lub wyjaśnień, jak wdrożyć to rozwiązanie?
Pipo,
58

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ć

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

więcej informacji na temat tej adnotacji można znaleźć tutaj

O innych rozwiązaniach:

fetch = FetchType.EAGER 

nie jest dobrą praktyką, należy ją stosować WYŁĄCZNIE w razie potrzeby.

Hibernate.initialize(topics.getComments());

Inicjator hibernacji wiąże klasy z technologią hibernacji. Jeśli chcesz być elastyczny, nie jest to dobra droga.

Mam nadzieję, że to pomoże

sarbuLopex
źródło
3
Adnotacja @Transactional działała dla mnie, ale zauważ, że Propagation.REQUIRED jest domyślną wersją, przynajmniej w Spring Boot 1.4.2 (Spring 4.3).
ben3000,
4
Tak, ale pomyślałem, że warto docenić fakt, że faktycznie można zmienić param propagacji
sarbuLopex
Czy to nie @Transactionaljest tylko wiosna?
Campa
@Campa tak to jest. Jeśli chcesz obsłużyć go ręcznie, powinieneś umieścić logikę biznesową w transakcji pobranej z menedżera encji
sarbuLopex
54

Geneza twojego problemu:

Domyślnie hibernacja leniwie ładuje kolekcje (relacje), co oznacza, że ​​za każdym razem, gdy używasz collectionw swoim kodzie (tutaj commentspole w Topicklasie) 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 ładujesz commentskolekcję):

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

Otrzymujesz kolekcję „komentarzy” (topic.getComments ()) w kontrolerze (gdzie JPA sessionsię zakończyła), co powoduje wyjątek. Również jeśli masz commentskolekcję w swoim pliku jsp w ten sposób (zamiast pobierać ją do kontrolera):

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

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 na FetchTypechętną:

Jeśli chcesz zainicjować opóźnioną kolekcję, a także sprawić, by działała, lepiej dodać ten fragment kodu do 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>

Ten kod powoduje, że zwiększy długość twojego JPA sessionlub, 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 .

gandalf
źródło
7
Dlaczego sesja JPS jest zamknięta? Jak sprawić, by nie był zamknięty? Jak wykonać leniwą kolekcję?
Dims,
1
Co określa limit dwóch kolekcji FetchType.Eager na jednostkę?
chrisinmtown
W Spring Boot możesz dodać „spring.jpa.open-in-view = true” do „application.properties”
Askar
28

Powodem jest to, że gdy używasz leniwego ładowania, sesja jest zamknięta.

Istnieją dwa rozwiązania.

  1. Nie używaj leniwego ładowania.

    Ustaw lazy=falsew XML lub Ustaw w @OneToMany(fetch = FetchType.EAGER)adnotacji.

  2. Użyj leniwego ładowania.

    Ustaw lazy=truew XML lub Ustaw@OneToMany(fetch = FetchType.LAZY)adnotacji.

    i dodaj OpenSessionInViewFilter filterswójweb.xml

Szczegóły Zobacz mój test POST .

saneryee
źródło
1
... a jednak oba rozwiązania nie są dobre. Sugeruj, że użycie EAGER może powodować ogromne problemy. Korzystanie z OpenSessionInViewFilter jest anty-wzorcem.
Rafael
27
@Controller
@RequestMapping(value = "/topic")
@Transactional

Rozwiązuję ten problem, dodając. @TransactionalMyślę, że może to otworzyć sesję

RuiZhi Wang
źródło
Dlaczego otrzymało to głosowanie ujemne? Dodanie transakcji do operacji przedłuża sesję
Tudor Grigoriu
1
Złą praktyką jest ad @ Transactional do kontrolera.
Rafael
@Rafael Dlaczego to zła praktyka?
Amr Ellafy
@AmrEllafy -> Oto dobre wyjaśnienie: stackoverflow.com/a/18498834/1261162
Rafael
22

Problem jest spowodowany dostępem do atrybutu przy zamkniętej sesji hibernacji. Nie masz transakcji hibernacji w kontrolerze.

Możliwe rozwiązania:

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

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

  3. użyj @Transactional w kontrolerze . Nie należy go tutaj używać, łączysz warstwę usług z prezentacją, nie jest to dobry projekt.
  4. użyj OpenSessionInViewFilter , zgłoszono wiele wad, możliwa niestabilność.

Ogólnie najlepszym rozwiązaniem jest 1.

abentan
źródło
2
Typ pobierania Eager zakładał, że hibernacja zostanie wyciągnięta z wszystkich danych w pierwszym zapytaniu, nie we wszystkich miejscach, w których jest poprawnie
Жасулан Бердибеков
Powinieneś POWIĘKSZYĆ, że NAJLEPSZE ROZWIĄZANIE TO 1 ... w rzeczywistości jest TYLKO DOBRYM rozwiązaniem, ponieważ wszystkie inne są anty-wzorami!
Rafael
19

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

GMK
źródło
15

Jak wyjaśniłem w tym artykule , najlepszym sposobem, aby sobie z tym poradzić, LazyInitializationExceptionjest pobranie go po czasie zapytania:

select t
from Topic t
left join fetch t.comments

ZAWSZE należy unikać następujących anty-wzorów:

Dlatego upewnij się, że Twoje FetchType.LAZYskojarzenia zostały zainicjowane w czasie zapytania lub w oryginalnym @Transactionalzakresie przy użyciu Hibernate.initializekolekcji wtórnych.

Vlad Mihalcea
źródło
1
Czy masz jakieś sugestie dotyczące pracy z leniwie zainicjowaną kolekcją w encji pobranej za pomocą metody findById () generowanego przez repozytorium? Nie piszę zapytania, a transakcja jest poza moim kodem.
chrisinmtown,
Sprawdź ten artykuł, aby uzyskać więcej informacji na temat inicjowania leniwych kolekcji.
Vlad Mihalcea,
Czy możesz wyjaśnić, co masz na myśli przez „w oryginalnym zakresie transakcyjnym” Nie jest to dla mnie jasne, ponieważ wydaje mi się, że ten błąd pojawia się podczas sesji otwartej (ale nie właściwej?)
Michiel Haisma
Będąc w zakresie o najwyższa metoda obsługi transakcji, znana również jako bramka transakcji. Sprawdź TrassctionInterceptorślad stosu i to jest to.
Vlad Mihalcea,
Jedna z najlepszych odpowiedzi jak dotąd ... to powinno być oznaczone jako poprawne. BTW ... zakładając, że OSIV jest anty-wzorcem, jak to możliwe, że jest domyślnie włączony w najnowszych wersjach Spring-boot? ... może nie jest tak źle?
Rafael
10

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:

@ElementCollection(fetch = FetchType.EAGER)
    public List<Long> ids;
javaboygo
źródło
1
w wielu przypadkach tak naprawdę nie chcesz tego robić.
Tracisz
Korzystanie z EAGER nie jest profesjonalnym rozwiązaniem.
Rafael
9

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

sreekmatta
źródło
1
Czy możesz powiedzieć OP, co dokładnie robi, jakieś skutki uboczne, wpływ na wydajność?
PeS
3
Z tyłu leniwego ładowania nowa sesja jest rozwidlana za każdym razem, gdy skojarzenie jest ładowane leniwie, stąd więcej połączeń jest rozwidlanych i tworzy to trochę presji na pulę połączeń. Jeśli masz ograniczenie liczby połączeń, to ta właściwość może być niepoprawna do użycia.
sreekmatta
2
dla niektórych uważany jest za anty-wzór vladmihalcea.com/…
Uri Loya
7

Dowiedziałem się, że zadeklarowanie @PersistenceContextjako EXTENDEDrównież rozwiązuje ten problem:

@PersistenceContext(type = PersistenceContextType.EXTENDED)
Elcin ABD
źródło
1
Cześć, bądź ostrożny z takimi zmianami. Tworzenie kontekstu trwałości w zakresie TRANSAKCJI jest leniwe, co było zamiarem PO. Pytanie brzmi, czy chcesz być bezpaństwowcem, czy nie. To ustawienie zależy od przeznaczenia systemu i nie powinno być zmieniane zbyt ... chętnie. Jeśli wiesz co mam na myśli. Przeczytaj tutaj stackoverflow.com/questions/2547817/…
kiedysktos
Niebezpieczny. To nie jest poprawna odpowiedź. Są inne, o wiele bardziej dokładne i bezpieczne.
Rafael
5

był to problem, z którym ostatnio się spotkałem, który rozwiązałem za pomocą

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

bardziej szczegółowe opisy tutaj i to uratowało mi dzień.

Tolgayilmaz
źródło
5

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 -

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
źródło
4

Aby rozwiązać problem w moim przypadku, po prostu brakowało tej linii

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

w pliku kontekstu aplikacji.

@TransactionalAdnotacji na metody nie została uwzględniona.

Mam nadzieję, że odpowiedź pomoże komuś

Mario Biasi
źródło
4

@ Brak adnotacji transakcyjnej na kontrolerze

@Controller
@RequestMapping("/")
@Transactional
public class UserController {
}
Xcoder
źródło
17
Twierdziłbym, że zarządzanie transakcjami należy do warstwy usług, w której znajduje się logika biznesowa.
Sõber
Nie brakuje adnotacji transakcyjnej. Administrator nie powinien mieć takich adnotacji. Te adnotacje powinny znajdować się na poziomie usługi.
Rafael
4

Za pomocą @Transactionaladnotacji 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:

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

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.

Alex
źródło
4

Dwie rzeczy, które powinieneś mieć fetch = FetchType.LAZY.

@Transactional

i

Hibernate.initialize(topicById.getComments());
karan patel
źródło
2

Dla osób pracujących z Criteria znalazłem to

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

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.

velis
źródło
2

W moim przypadku problemem był następujący kod:

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

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:

Hibernate.initialize(topicById.getComments());
entityManager.detach(topicById);
topicById.getComments() // works like a charm
kiedysktos
źródło
1

Powodem jest to, że próbujesz uzyskać listę komentarza na kontrolerze po zamknięciu sesji w usłudze.

topicById.getComments();

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.

aditya lath
źródło
2
Tak, jest to opis problemu, powinieneś również udzielić odpowiedzi wAnswer
Sarz
1

Kolekcja commentsw twojej klasie modelu Topicjest leniwie ładowana, co jest domyślnym zachowaniem, jeśli nie dodasz do niej adnotacji fetch = FetchType.EAGER.

Najprawdopodobniej findTopicByIDusł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.

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

Rozwiązaniem może być:

  1. Adnotuj za commentspomocąfetch = FetchType.EAGER

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)   
    private Collection<Comment> comments = new LinkedHashSet<Comment>();
  2. Jeśli nadal chcesz, aby komentarze były ładowane leniwie, skorzystaj z sesji stanowych Hibernacji , abyś mógł później pobierać komentarze na żądanie.

Yuci
źródło
1

W moim przypadku miałem mapowanie czarno-białe Ai Bpodobne

A ma

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

w DAOwarstwie metoda musi zostać opatrzona adnotacjami, @Transactionaljeśli mapowanie nie zostało opatrzone adnotacjami typem pobierania - chętny

Shirgill Farhan
źródło
1

Nie najlepsze rozwiązanie, ale dla tych, którzy stoją przed tym LazyInitializationExceptionszczególnie Serialization, pomoże. Tutaj sprawdzisz leniwie zainicjowane właściwości i ustawienia nulldla nich. W tym celu utwórz niższą klasę

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

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.

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

Nazwij tę checkLazyIntialzation()metodę po wszystkich miejscach, w których ładujesz dane.

 YourEntity obj= entityManager.find(YourEntity.class,1L);
  obj.checkLazyIntialzation();
Javad
źródło
0

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”

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

teraz, jeśli uzyskam dostęp do „set” po zamknięciu sesji, zgłasza wyjątek.

Moje rozwiązanie:

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

teraz mogę uzyskać dostęp do „set” nawet po zamknięciu sesji hibernacji.

ofiara
źródło
0

Jeszcze inny sposób, aby to zrobić, możesz użyć TransactionTemplate, aby owinąć leniwe pobieranie. Lubić

Collection<Comment> commentList = this.transactionTemplate.execute
(status -> topicById.getComments());
aristotll
źródło
0

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 @Transactionaladnotacji. 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ę @Transactionaldo metody findTopicByID(id)w swoim TopicServicepliku i wymusić pobranie kolekcji w tej metodzie (na przykład poprzez podanie jej rozmiaru):

    @Transactional(readOnly = true)
    public Topic findTopicById(Long id) {
        Topic topic = TopicRepository.findById(id).orElse(null);
        topic.getComments().size();
        return topic;
    }
Domingo Gallardo
źródło
0

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 ObjectMapperlub hashCodewygenerowane przez Lombok wywoła twoje metody niejawnie.

W niektórych szczególnych przypadkach możesz użyć @EntityGrpaphadnotacji, które pozwalają na eagerobciążenie, nawet jeśli masz je fetchType=lazyw swojej jednostce.

degr
źródło
0

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.

Vsharma
źródło
-11

Rozwiązałem przy użyciu listy zamiast zestawu:

private List<Categories> children = new ArrayList<Categories>();
SaganTheBest
źródło