Bagaimana mengatasi "gagal untuk menginisialisasi kumpulan peran" pengecualian Hibernate

363

Saya punya masalah ini:

org.hibernate.LazyInitializationException: gagal malas menginisialisasi kumpulan peran: mvc3.model.Topic.comments, tidak ada sesi atau sesi ditutup

Berikut modelnya:

@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, yang memanggil model tampak seperti berikut:

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

     }

}

Halaman jsp terlihat seperti berikut:

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

Pengecualian meningkat, saat melihat jsp. Sejalan dengan c: forEach loop

Eugene
sumber

Jawaban:

214

Jika Anda tahu bahwa Anda ingin melihat semua Commentsetiap kali Anda mengambil a Topiclalu ubah pemetaan bidang Anda commentsmenjadi:

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

Koleksi malas dimuat secara default, lihat ini jika Anda ingin tahu lebih banyak.

darrengorman
sumber
35
Maaf, tetapi saya ingin menggunakan lazy-load. Jadi, saya telah mengubah tipe 'LinkedHashSet' menjadi 'PersistentList'. Pengecualian masih terjadi
Eugene
242
Ini dapat digunakan sebagai solusi, tetapi bukan solusi aktual untuk masalah tersebut. Bagaimana jika kita perlu mengambil dengan malas?
Dkyc
14
tetapi jika kita ingin malas maka solusi ini tidak akan bekerja dan sebagian besar kasus kita ingin malas saja.
prashant thakre
103
Ini adalah jenis jawaban yang muncul di mana-mana di stack overflow. Singkatnya, to the point, memecahkan masalah dan MISLEADING. Kepada pembaca yang akan datang, bantulah diri Anda sendiri dan pelajari apa yang sebenarnya malas dan diambil dengan penuh semangat, dan pahami konsekuensinya.
Ced
13
@darrengorman Ketika saya memulai JPA saya memposting pertanyaan di sekitar baris OP's. Saya menerima respons yang sama seperti yang Anda berikan. Segera setelah saya menjalankan beberapa tes dengan ratusan ribu baris, coba tebak apa yang terjadi? Saya pikir itu menyesatkan karena memberikan jawaban yang terlalu sederhana untuk masalah yang sebagian besar pemula akan hadapi dan segera mereka akan memiliki seluruh database mereka dimuat dalam memori jika mereka tidak hati-hati (dan mereka tidak akan, karena mereka tidak akan sadari)) :).
Ced
182

Dari pengalaman saya, saya memiliki metode berikut untuk menyelesaikan LazyInitializationException yang terkenal:

(1) Gunakan Hibernate.initialize

Hibernate.initialize(topics.getComments());

(2) Gunakan JOIN FETCH

Anda dapat menggunakan sintaks JOIN FETCH di JPQL Anda untuk secara eksplisit mengambil koleksi anak. Ini adalah beberapa cara seperti mengambil EAGER.

(3) Gunakan OpenSessionInViewFilter

LazyInitializationException sering terjadi di lapisan tampilan. Jika Anda menggunakan kerangka kerja Spring, Anda bisa menggunakan OpenSessionInViewFilter. Namun, saya tidak menyarankan Anda untuk melakukannya. Ini dapat menyebabkan masalah kinerja jika tidak digunakan dengan benar.

Boris
sumber
5
(1) bekerja untuk saya dengan sempurna. Kasus saya: Hibernate.initialize (registry.getVehicle (). GetOwner (). GetPerson (). GetAddress ());
Leonel Sanches da Silva
6
Tampaknya Hibernate.initialize tidak berfungsi dengan EntityManager
marionmaiden
8
Ini seharusnya jawaban yang benar. Misalnya dalam proyek saya di tempat kerja kami secara eksplisit tidak seharusnya menggunakan pengambilan EAGER. Ini menyebabkan masalah dalam sistem khusus ini.
Steve Waters
Tampak menarik tetapi tidak memiliki dokumentasi untuk diterapkan dalam kasus lain ... bisakah Anda memberikan beberapa tautan atau penjelasan lebih lanjut tentang cara menerapkan solusi ini?
Pipo
58

Saya tahu ini pertanyaan lama tapi saya ingin membantu. Anda dapat menempatkan anotasi transaksional pada metode layanan yang Anda butuhkan, dalam hal ini findTopicByID (id) harus memiliki

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

info lebih lanjut tentang anotasi ini dapat ditemukan di sini

Tentang solusi lain:

fetch = FetchType.EAGER 

bukan praktik yang baik, itu harus digunakan SAJA jika perlu.

Hibernate.initialize(topics.getComments());

Penginisialisasi hibernasi mengikat kelas Anda dengan teknologi hibernasi. Jika Anda bertujuan untuk fleksibel bukan cara yang baik untuk pergi.

Semoga ini bisa membantu

sarbuLopex
sumber
3
Anotasi @Transactional berfungsi untuk saya, tetapi perhatikan bahwa Propagation.REQUIRED adalah default, setidaknya di Spring Boot 1.4.2 (Spring 4.3).
ben3000
4
Ya itu, tapi saya pikir itu bisa dihargai untuk menjelaskan Anda benar-benar bisa mengubah param propagasi
sarbuLopex
Bukan @Transactionalhanya Musim Semi?
Campa
@Ampa ya itu. Jika Anda ingin menanganinya secara manual, Anda harus memasukkan logika bisnis Anda ke dalam transaksi yang diambil dari manajer entitas
sarbuLopex
54

Asal usul masalah Anda:

Secara default hibernate dengan malas memuat koleksi (hubungan) yang berarti kapan pun Anda menggunakan collectionkode Anda (di sini commentsbidang di Topickelas) hibernate mendapatkan itu dari database, sekarang masalahnya adalah Anda mendapatkan koleksi di controller Anda (di mana sesi JPA ditutup). Ini adalah baris kode yang menyebabkan pengecualian (tempat Anda memuat commentskoleksi):

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

Anda mendapatkan koleksi "komentar" (topic.getComments ()) di controller Anda (di mana JPA sessiontelah berakhir) dan itu menyebabkan pengecualian. Juga jika Anda punya commentskoleksi di file jsp Anda seperti ini (alih-alih mendapatkannya di controller Anda):

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

Anda masih memiliki pengecualian yang sama untuk alasan yang sama.

Memecahkan masalah:

Karena Anda hanya dapat memiliki hanya dua koleksi dengan FetchType.Eager(koleksi yang diambil dengan penuh semangat) di kelas Entity dan karena pemuatan malas lebih efisien daripada pemuatan penuh semangat, saya pikir cara menyelesaikan masalah Anda ini lebih baik daripada hanya mengubah FetchTypeke keinginan:

Jika Anda ingin koleksi malas diinisialisasi, dan juga membuatnya berfungsi, lebih baik menambahkan potongan kode ini ke 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>

Apa yang dilakukan oleh kode ini adalah bahwa itu akan menambah panjang Anda JPA sessionatau seperti yang dikatakan dalam dokumentasi, itu digunakan "to allow for lazy loading in web views despite the original transactions already being completed."sehingga dengan cara ini sesi JPA akan terbuka sedikit lebih lama dan karena itu Anda dapat dengan malas memuat koleksi di file jsp dan kelas controller Anda .

Gandalf
sumber
7
Mengapa sesi JPS ditutup? Bagaimana cara membuatnya tidak ditutup? Bagaimana cara melakukan koleksi malas?
Dims
1
Apa yang mendefinisikan batas dua koleksi FetchType.Eager per Entity?
chrisinmtown
Di Spring Boot, Anda dapat menambahkan 'spring.jpa.open-in-view = true' to 'application.properties'
Askar
28

Alasannya adalah bahwa ketika Anda menggunakan muatan malas, sesi ditutup.

Ada dua solusi.

  1. Jangan gunakan muatan malas.

    Set lazy=falsedalam XML atau @OneToMany(fetch = FetchType.EAGER)anotasi Set In.

  2. Gunakan muatan malas.

    Set lazy=truedalam XML atau @OneToMany(fetch = FetchType.LAZY)anotasi Set In.

    dan menambahkan OpenSessionInViewFilter filterdi Andaweb.xml

Detail Lihat POST saya .

saneryee
sumber
1
... namun kedua solusi tidak baik. Menyarankan menggunakan EAGER dapat menciptakan masalah besar. Menggunakan OpenSessionInViewFilter adalah anti-pola.
Rafael
27
@Controller
@RequestMapping(value = "/topic")
@Transactional

saya memecahkan masalah ini dengan menambahkan @Transactional, saya pikir ini bisa membuat sesi terbuka

RuiZhi Wang
sumber
Mengapa ini menerima suara minus? Menambahkan transaksi ke operasi memperpanjang sesi
Tudor Grigoriu
1
Merupakan praktik yang buruk untuk memasang @Transaksional ke controller.
Rafael
@ Rafael Mengapa ini praktik yang buruk?
Amr Ellafy
@AmrEllafy -> Ini dia penjelasan yang baik: stackoverflow.com/a/18498834/1261162
Rafael
22

Masalahnya disebabkan oleh mengakses atribut dengan sesi hibernasi ditutup. Anda belum melakukan transaksi hibernate di controller.

Solusi yang memungkinkan:

  1. Lakukan semua logika ini, di lapisan layanan , (dengan @Transaksional), bukan di controller. Seharusnya ada tempat yang tepat untuk melakukan ini, itu adalah bagian dari logika aplikasi, bukan di controller (dalam hal ini, antarmuka untuk memuat model). Semua operasi di lapisan layanan harus transaksional. yaitu: Pindahkan baris ini ke metode TopicService.findTopicByID:

    Collection commentList = topicById.getComments ();

  2. Gunakan 'bersemangat' bukan 'malas' . Sekarang Anda tidak menggunakan 'malas' .. itu bukan solusi nyata, jika Anda ingin menggunakan malas, bekerja seperti solusi sementara (sangat sementara).

  3. gunakan @Transaksional di Controller . Seharusnya tidak digunakan di sini, Anda mencampur lapisan layanan dengan presentasi, itu bukan desain yang baik.
  4. menggunakan OpenSessionInViewFilter , banyak kerugian dilaporkan, kemungkinan ketidakstabilan.

Secara umum, solusi terbaik adalah 1.

abentan
sumber
2
Tipe pengambilan Eager diasumsikan bahwa hibernate akan menarik semua data dalam kueri pertama, tidak semua tempat itu benar
Жасулан Бердибеков
Anda harus DAPATKAN bahwa SOLUSI TERBAIK ADALAH 1 ... pada kenyataannya adalah solusi HANYA BAIK karena semua yang lain adalah anti-pola!
Rafael
19

Untuk malas memuat koleksi harus ada sesi aktif. Dalam aplikasi web ada dua cara untuk melakukan ini. Anda bisa menggunakan pola Sesi Terbuka Dalam Tampilan , di mana Anda menggunakan interseptor untuk membuka sesi di awal permintaan dan menutupnya di akhir. Risiko di sana adalah bahwa Anda harus memiliki penanganan pengecualian yang kuat atau Anda dapat mengikat semua sesi Anda dan aplikasi Anda bisa hang.

Cara lain untuk menangani ini adalah dengan mengumpulkan semua data yang Anda butuhkan di controller Anda, tutup sesi Anda, dan kemudian masukkan data ke dalam model Anda. Saya pribadi lebih suka pendekatan ini, karena tampaknya sedikit lebih dekat dengan semangat pola MVC. Juga jika Anda mendapatkan kesalahan dari database dengan cara ini Anda bisa menanganinya jauh lebih baik daripada jika itu terjadi dalam tampilan renderer Anda. Teman Anda dalam skenario ini adalah Hibernate.initialize (myTopic.getComments ()). Anda juga harus memasang kembali objek ke sesi, karena Anda membuat transaksi baru dengan setiap permintaan. Gunakan session.lock (myTopic, LockMode.NONE) untuk itu.

GMK
sumber
15

Seperti yang saya jelaskan dalam artikel ini , cara terbaik untuk mengatasinya LazyInitializationExceptionadalah dengan mengambilnya pada waktu kueri, seperti ini:

select t
from Topic t
left join fetch t.comments

Anda harus SELALU menghindari anti-pola berikut:

Oleh karena itu, pastikan bahwa FetchType.LAZYasosiasi Anda diinisialisasi pada waktu kueri atau dalam @Transactionallingkup asli menggunakan Hibernate.initializeuntuk koleksi sekunder.

Vlad Mihalcea
sumber
1
Vlad apakah Anda punya saran untuk bekerja dengan koleksi malas-diinisialisasi dalam entitas yang diambil oleh metode findById () yang dibuat oleh Spring? Saya tidak menulis kueri dan transaksi di luar kode saya.
chrisinmtown
Lihat artikel ini untuk detail lebih lanjut tentang menginisialisasi koleksi malas.
Vlad Mihalcea
Bisakah Anda mengklarifikasi apa yang Anda maksud dengan 'dalam lingkup transaksional @ asli' Ini tidak jelas bagi saya karena saya tampaknya mendapatkan kesalahan ini saat dalam sesi terbuka (tapi bukan yang benar?)
Michiel Haisma
Sementara di dalam lingkup o metode layanan transaksional paling atas, juga dikenal sebagai gateway transaksi. Lihat TrassctionInterceptorjejak tumpukan dan itulah satu-satunya.
Vlad Mihalcea
Salah satu jawaban terbaik sejauh ini ... ini harus ditandai sebagai benar. Btw ... anggap OSIV adalah anti-pola bagaimana mungkin yang diaktifkan secara default pada musim semi-boot versi terbaru? ... mungkin tidak seburuk itu?
Rafael
10

Jika Anda mencoba untuk memiliki hubungan antara entitas dan Koleksi atau Daftar objek java (misalnya tipe Panjang), itu akan seperti ini:

@ElementCollection(fetch = FetchType.EAGER)
    public List<Long> ids;
javaboygo
sumber
1
dalam banyak kasus, Anda benar-benar tidak ingin melakukan itu. Anda kehilangan semua manfaat pemuatan malas di sini
kiedysktos
Menggunakan EAGER bukan solusi profesional.
Rafael
9

Salah satu solusi terbaik adalah menambahkan yang berikut ini di file application.properties Anda: spring.jpa.properties.hibernate.enable_lazy_load_no_trans = true

sreekmatta
sumber
1
Bisakah Anda memberi tahu OP apa yang dilakukannya dengan tepat, segala efek samping, dampak kinerja?
PeS
3
Di belakang pemuatan malas, sesi baru bercabang setiap kali asosiasi dimuat malas, maka lebih banyak koneksi bercabang dan menciptakan sedikit tekanan pada kumpulan koneksi. Jika Anda memiliki batasan jumlah koneksi, maka properti ini mungkin bukan yang benar untuk digunakan.
sreekmatta
2
bagi sebagian orang, itu dianggap sebagai anti-pola vladmihalcea.com/...
Uri Loya
7

Saya menemukan bahwa menyatakan @PersistenceContextsebagai EXTENDEDjuga memecahkan masalah ini:

@PersistenceContext(type = PersistenceContextType.EXTENDED)
Elcin ABD
sumber
1
Hai, hati-hati dengan perubahan seperti itu. TRANSAKSI ciptaan persistensi konteks tertutup malas, yang merupakan tujuan OP. Jadi pertanyaannya adalah apakah Anda ingin menjadi kewarganegaraan atau tidak. Pengaturan ini tergantung pada tujuan sistem dan tidak boleh diubah terlalu ... bersemangat. Jika kamu tahu maksud saya. Baca di sini stackoverflow.com/questions/2547817/…
kiedysktos
Berbahaya. Ini bukan jawaban yang benar. Ada yang lain di atas jauh lebih akurat dan aman.
Rafael
5

itu adalah masalah yang baru-baru ini saya hadapi yang saya pecahkan dengan menggunakan

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

dekripsi yang lebih rinci di sini dan ini menyelamatkan hari saya.

tolgayilmaz
sumber
5

daftar Anda malas dimuat, jadi daftar itu tidak dimuat. panggilan untuk masuk daftar tidak cukup. gunakan di Hibernate.initialize untuk init daftar. Jika dosnt berfungsi jalankan pada elemen daftar dan panggil Hibernate.initialize untuk masing-masing. ini harus sebelum Anda kembali dari ruang lingkup transaksi. lihat posting ini .
pencarian untuk -

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
sumber
4

Untuk mengatasi masalah dalam kasus saya, itu hanya melewatkan baris ini

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

dalam file konteks aplikasi.

The @Transactionalpenjelasan lebih metode tidak diperhitungkan.

Semoga jawabannya akan membantu seseorang

Mario Biasi
sumber
4

@Otot transaksi pada pengontrol hilang

@Controller
@RequestMapping("/")
@Transactional
public class UserController {
}
Xcoder
sumber
17
Saya berpendapat bahwa manajemen transaksi milik lapisan layanan di mana logika bisnis berada.
Sober
Anotasi transaksional tidak hilang. Kontroler seharusnya tidak memiliki penjelasan seperti itu. Anotasi tersebut harus pada tingkat Layanan.
Rafael
4

Dengan menggunakan @Transactionalanotasi hibernasi , jika Anda mendapatkan objek dari database dengan atribut malas yang diambil, Anda bisa mendapatkannya dengan mengambil atribut ini seperti ini:

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

Di sini, dalam transaksi yang dikelola proxy Hibernate, fakta menelepon ticket.getSales()melakukan kueri lain untuk mengambil penjualan karena Anda secara eksplisit menanyakannya.

Alex
sumber
4

Dua hal yang harus Anda miliki fetch = FetchType.LAZY.

@Transactional

dan

Hibernate.initialize(topicById.getComments());
karan patel
sumber
2

Bagi mereka yang bekerja dengan Kriteria , saya menemukan itu

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

melakukan semua yang saya butuhkan telah dilakukan.

Mode pengambilan awal untuk koleksi diatur ke FetchMode.LAZY untuk memberikan kinerja, tetapi ketika saya membutuhkan data, saya hanya menambahkan baris itu dan menikmati objek yang terisi penuh.

velis
sumber
2

Dalam kasus saya, kode berikut adalah masalah:

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

Karena terlepas dari database dan Hibernate tidak lagi mengambil daftar dari bidang ketika dibutuhkan. Jadi saya menginisialisasi sebelum melepaskan:

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

Alasannya adalah Anda mencoba untuk mendapatkan commentList di controller Anda setelah menutup sesi di dalam layanan.

topicById.getComments();

Di atas akan memuat commentList hanya jika sesi hibernasi Anda aktif, yang saya kira Anda menutup dalam layanan Anda.

Jadi, Anda harus mendapatkan commentList sebelum menutup sesi.

aditya bilah
sumber
2
Ya, ini adalah pernyataan masalah, Anda juga harus memberikan jawaban dalamAnswer
Sarz
1

Koleksi commentsdi kelas model Anda Topicdimuat dengan malas, yang merupakan perilaku default jika Anda tidak membubuhi keterangan dengan fetch = FetchType.EAGERspesifik.

Kemungkinan besar findTopicByIDlayanan Anda menggunakan sesi Hibernate tanpa kewarganegaraan. Sesi tanpa kewarganegaraan tidak memiliki cache tingkat pertama, yaitu, tidak ada konteks kegigihan. Nanti ketika Anda mencoba untuk beralih comments, Hibernate akan melempar pengecualian.

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

Solusinya dapat:

  1. Beranotasi commentsdenganfetch = FetchType.EAGER

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)   
    private Collection<Comment> comments = new LinkedHashSet<Comment>();
  2. Jika Anda masih ingin komentar dimuat dengan malas, gunakan sesi status Hibernate , sehingga Anda dapat mengambil komentar nanti atas permintaan.

Yuci
sumber
1

Dalam kasus saya, saya memiliki pemetaan b / w Adan Bsuka

A telah

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

di DAOlayer, metode harus dijelaskan dengan @Transactionaljika Anda belum memberi penjelasan pemetaan dengan Fetch Type - Eager

Shirgill Farhan
sumber
1

Bukan solusi terbaik, tetapi bagi mereka yang menghadapi LazyInitializationExceptionterutama pada Serializationini akan membantu. Di sini Anda akan memeriksa properti yang diinisialisasi malas dan pengaturannya null. Untuk itu buat kelas di bawah ini

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

Di dalam kelas Entity Anda yang memiliki properti malas diinisialisasi menambahkan metode seperti yang ditunjukkan di bawah ini. Tambahkan semua properti pemuatan malas Anda di dalam metode ini.

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

Panggil checkLazyIntialzation()metode ini setelah semua tempat Anda memuat data.

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

Hai Semua posting sangat terlambat harap ini membantu orang lain, Berterima kasih sebelumnya ke @GMK untuk posting ini Hibernate.initialize (objek)

ketika Malas = "benar"

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

sekarang jika saya mengakses 'set' setelah sesi penutupan itu melempar pengecualian.

Solusi saya:

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

sekarang saya dapat mengakses 'set' bahkan setelah menutup Hibernate Session.

vic
sumber
0

Namun cara lain untuk melakukan hal itu, Anda dapat menggunakan TransactionTemplate untuk membungkus malas mengambil. Suka

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

Masalah ini disebabkan karena kode mengakses hubungan JPA malas ketika "koneksi" ke database ditutup ( konteks persistensi adalah nama yang benar dalam hal Hibernate / JPA).

Cara sederhana untuk menyelesaikannya di Spring Boot adalah dengan mendefinisikan lapisan layanan dan menggunakan @Transactional anotasi. Anotasi dalam metode ini menciptakan transaksi yang menyebar ke lapisan repositori dan tetap membuka konteks kegigihan sampai metode selesai. Jika Anda mengakses koleksi di dalam metode transaksional, Hibernate / JPA akan mengambil data dari database.

Dalam kasus Anda, Anda hanya perlu membuat anotasi dengan @Transactionalmetode findTopicByID(id)di Anda TopicServicedan memaksakan pengambilan koleksi dalam metode itu (misalnya, dengan menanyakan ukurannya):

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

Untuk menghilangkan pengecualian inisialisasi malas, Anda tidak boleh meminta koleksi malas ketika Anda beroperasi dengan objek terpisah.

Dari pendapat saya, pendekatan terbaik adalah menggunakan DTO, dan bukan entitas. Dalam hal ini, Anda dapat secara eksplisit mengatur bidang yang ingin Anda gunakan. Seperti biasa sudah cukup. Tidak perlu khawatir bahwa sesuatu seperti jackson ObjectMapper, atau hashCodedihasilkan oleh Lombok akan memanggil metode Anda secara implisit.

Untuk beberapa kasus tertentu, Anda dapat menggunakan @EntityGrpaphanotasi, yang memungkinkan Anda untuk melakukan eagerpemuatan walaupun Anda memilikinya fetchType=lazydi entitas Anda.

menurun
sumber
0

Ada beberapa solusi untuk masalah Inisialisasi malas ini -

1) Ubah asosiasi Ambil tipe dari LAZY ke EAGER tetapi ini bukan praktik yang baik karena ini akan menurunkan kinerja.

2) Gunakan FetchType.LAZY pada Obyek terkait dan juga gunakan anotasi Transaksional dalam metode lapisan layanan Anda sehingga sesi akan tetap terbuka dan ketika Anda akan memanggil topicById.getComments (), objek anak (komentar) akan dimuat.

3) Juga, coba gunakan objek DTO daripada entitas di lapisan pengontrol. Dalam kasus Anda, sesi ditutup pada lapisan pengontrol. JADI lebih baik untuk mengonversi entitas ke DTO di lapisan layanan.

vsharma
sumber
-11

saya diselesaikan menggunakan Daftar alih-alih Set:

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