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
Dari pengalaman saya, saya memiliki metode berikut untuk menyelesaikan LazyInitializationException yang terkenal:
(1) Gunakan Hibernate.initialize
(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.
sumber
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
info lebih lanjut tentang anotasi ini dapat ditemukan di sini
Tentang solusi lain:
bukan praktik yang baik, itu harus digunakan SAJA jika perlu.
Penginisialisasi hibernasi mengikat kelas Anda dengan teknologi hibernasi. Jika Anda bertujuan untuk fleksibel bukan cara yang baik untuk pergi.
Semoga ini bisa membantu
sumber
@Transactional
hanya Musim Semi?Asal usul masalah Anda:
Secara default hibernate dengan malas memuat koleksi (hubungan) yang berarti kapan pun Anda menggunakan
collection
kode Anda (di sinicomments
bidang diTopic
kelas) 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 memuatcomments
koleksi):Anda mendapatkan koleksi "komentar" (topic.getComments ()) di controller Anda (di mana
JPA session
telah berakhir) dan itu menyebabkan pengecualian. Juga jika Anda punyacomments
koleksi di file jsp Anda seperti ini (alih-alih mendapatkannya di controller Anda):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 mengubahFetchType
ke keinginan:Jika Anda ingin koleksi malas diinisialisasi, dan juga membuatnya berfungsi, lebih baik menambahkan potongan kode ini ke
web.xml
:Apa yang dilakukan oleh kode ini adalah bahwa itu akan menambah panjang Anda
JPA session
atau 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 .sumber
Alasannya adalah bahwa ketika Anda menggunakan muatan malas, sesi ditutup.
Ada dua solusi.
Jangan gunakan muatan malas.
Set
lazy=false
dalam XML atau@OneToMany(fetch = FetchType.EAGER)
anotasi Set In.Gunakan muatan malas.
Set
lazy=true
dalam XML atau@OneToMany(fetch = FetchType.LAZY)
anotasi Set In.dan menambahkan
OpenSessionInViewFilter filter
di Andaweb.xml
Detail Lihat POST saya .
sumber
saya memecahkan masalah ini dengan menambahkan
@Transactional
, saya pikir ini bisa membuat sesi terbukasumber
Masalahnya disebabkan oleh mengakses atribut dengan sesi hibernasi ditutup. Anda belum melakukan transaksi hibernate di controller.
Solusi yang memungkinkan:
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 ();
Gunakan 'bersemangat' bukan 'malas' . Sekarang Anda tidak menggunakan 'malas' .. itu bukan solusi nyata, jika Anda ingin menggunakan malas, bekerja seperti solusi sementara (sangat sementara).
Secara umum, solusi terbaik adalah 1.
sumber
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.
sumber
Seperti yang saya jelaskan dalam artikel ini , cara terbaik untuk mengatasinya
LazyInitializationException
adalah dengan mengambilnya pada waktu kueri, seperti ini:Anda harus SELALU menghindari anti-pola berikut:
hibernate.enable_lazy_load_no_trans
properti konfigurasi HibernateOleh karena itu, pastikan bahwa
FetchType.LAZY
asosiasi Anda diinisialisasi pada waktu kueri atau dalam@Transactional
lingkup asli menggunakanHibernate.initialize
untuk koleksi sekunder.sumber
TrassctionInterceptor
jejak tumpukan dan itulah satu-satunya.Jika Anda mencoba untuk memiliki hubungan antara entitas dan Koleksi atau Daftar objek java (misalnya tipe Panjang), itu akan seperti ini:
sumber
Salah satu solusi terbaik adalah menambahkan yang berikut ini di file application.properties Anda: spring.jpa.properties.hibernate.enable_lazy_load_no_trans = true
sumber
Saya menemukan bahwa menyatakan
@PersistenceContext
sebagaiEXTENDED
juga memecahkan masalah ini:sumber
itu adalah masalah yang baru-baru ini saya hadapi yang saya pecahkan dengan menggunakan
dekripsi yang lebih rinci di sini dan ini menyelamatkan hari saya.
sumber
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 -
sumber
Untuk mengatasi masalah dalam kasus saya, itu hanya melewatkan baris ini
dalam file konteks aplikasi.
The
@Transactional
penjelasan lebih metode tidak diperhitungkan.Semoga jawabannya akan membantu seseorang
sumber
@Otot transaksi pada pengontrol hilang
sumber
Dengan menggunakan
@Transactional
anotasi hibernasi , jika Anda mendapatkan objek dari database dengan atribut malas yang diambil, Anda bisa mendapatkannya dengan mengambil atribut ini seperti ini:Di sini, dalam transaksi yang dikelola proxy Hibernate, fakta menelepon
ticket.getSales()
melakukan kueri lain untuk mengambil penjualan karena Anda secara eksplisit menanyakannya.sumber
Dua hal yang harus Anda miliki
fetch = FetchType.LAZY
.dan
sumber
Bagi mereka yang bekerja dengan Kriteria , saya menemukan itu
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.
sumber
Dalam kasus saya, kode berikut adalah masalah:
Karena terlepas dari database dan Hibernate tidak lagi mengambil daftar dari bidang ketika dibutuhkan. Jadi saya menginisialisasi sebelum melepaskan:
sumber
Alasannya adalah Anda mencoba untuk mendapatkan commentList di controller Anda setelah menutup sesi di dalam layanan.
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.
sumber
Answer
Koleksi
comments
di kelas model AndaTopic
dimuat dengan malas, yang merupakan perilaku default jika Anda tidak membubuhi keterangan denganfetch = FetchType.EAGER
spesifik.Kemungkinan besar
findTopicByID
layanan Anda menggunakan sesi Hibernate tanpa kewarganegaraan. Sesi tanpa kewarganegaraan tidak memiliki cache tingkat pertama, yaitu, tidak ada konteks kegigihan. Nanti ketika Anda mencoba untuk beralihcomments
, Hibernate akan melempar pengecualian.Solusinya dapat:
Beranotasi
comments
denganfetch = FetchType.EAGER
Jika Anda masih ingin komentar dimuat dengan malas, gunakan sesi status Hibernate , sehingga Anda dapat mengambil komentar nanti atas permintaan.
sumber
Dalam kasus saya, saya memiliki pemetaan b / w
A
danB
sukaA
telahdi
DAO
layer, metode harus dijelaskan dengan@Transactional
jika Anda belum memberi penjelasan pemetaan dengan Fetch Type - Eagersumber
Bukan solusi terbaik, tetapi bagi mereka yang menghadapi
LazyInitializationException
terutama padaSerialization
ini akan membantu. Di sini Anda akan memeriksa properti yang diinisialisasi malas dan pengaturannyanull
. Untuk itu buat kelas di bawah iniDi 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.
Panggil
checkLazyIntialzation()
metode ini setelah semua tempat Anda memuat data.sumber
Hai Semua posting sangat terlambat harap ini membantu orang lain, Berterima kasih sebelumnya ke @GMK untuk posting ini Hibernate.initialize (objek)
ketika Malas = "benar"
sekarang jika saya mengakses 'set' setelah sesi penutupan itu melempar pengecualian.
Solusi saya:
sekarang saya dapat mengakses 'set' bahkan setelah menutup Hibernate Session.
sumber
Namun cara lain untuk melakukan hal itu, Anda dapat menggunakan TransactionTemplate untuk membungkus malas mengambil. Suka
sumber
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
@Transactional
metodefindTopicByID(id)
di AndaTopicService
dan memaksakan pengambilan koleksi dalam metode itu (misalnya, dengan menanyakan ukurannya):sumber
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
, atauhashCode
dihasilkan oleh Lombok akan memanggil metode Anda secara implisit.Untuk beberapa kasus tertentu, Anda dapat menggunakan
@EntityGrpaph
anotasi, yang memungkinkan Anda untuk melakukaneager
pemuatan walaupun Anda memilikinyafetchType=lazy
di entitas Anda.sumber
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.
sumber
saya diselesaikan menggunakan Daftar alih-alih Set:
sumber