Bagaimana cara memperbaiki org.hibernate.LazyInitializationException - tidak dapat menginisialisasi proxy - tanpa Sesi

188

Saya mendapatkan pengecualian berikut:

Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:167)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:215)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
    at sei.persistence.wf.entities.Element_$$_jvstc68_47.getNote(Element_$$_jvstc68_47.java)
    at JSON_to_XML.createBpmnRepresantation(JSON_to_XML.java:139)
    at JSON_to_XML.main(JSON_to_XML.java:84)

ketika saya mencoba menelepon dari saluran utama berikut:

Model subProcessModel = getModelByModelGroup(1112);
System.out.println(subProcessModel.getElement().getNote());

Saya menerapkan getModelByModelGroup(int modelgroupid)metode pertama seperti ini:

public static Model getModelByModelGroup(int modelGroupId, boolean openTransaction) {

    Session session = SessionFactoryHelper.getSessionFactory().getCurrentSession();     
    Transaction tx = null;

    if (openTransaction) {
        tx = session.getTransaction();
    }

    String responseMessage = "";

    try {
        if (openTransaction) {
            tx.begin();
        }
        Query query = session.createQuery("from Model where modelGroup.id = :modelGroupId");
        query.setParameter("modelGroupId", modelGroupId);

        List<Model> modelList = (List<Model>)query.list(); 
        Model model = null;

        for (Model m : modelList) {
            if (m.getModelType().getId() == 3) {
                model = m;
                break;
            }
        }

        if (model == null) {
            Object[] arrModels = modelList.toArray();
            if (arrModels.length == 0) {
                throw new Exception("Non esiste ");
            }

            model = (Model)arrModels[0];
        }

        if (openTransaction) {
            tx.commit();
        }

        return model;

   } catch(Exception ex) {
       if (openTransaction) {
           tx.rollback();
       }
       ex.printStackTrace();
       if (responseMessage.compareTo("") == 0) {
           responseMessage = "Error" + ex.getMessage();
       }
       return null;
    }
}

dan mendapat pengecualian. Kemudian seorang teman menyarankan saya untuk selalu menguji sesi dan mendapatkan sesi saat ini untuk menghindari kesalahan ini. Jadi saya melakukan ini:

public static Model getModelByModelGroup(int modelGroupId) {
    Session session = null;
    boolean openSession = session == null;
    Transaction tx = null;
    if (openSession) {
        session = SessionFactoryHelper.getSessionFactory().getCurrentSession(); 
        tx = session.getTransaction();
    }
    String responseMessage = "";

    try {
        if (openSession) {
            tx.begin();
        }
        Query query = session.createQuery("from Model where modelGroup.id = :modelGroupId");
        query.setParameter("modelGroupId", modelGroupId);

        List<Model> modelList = (List<Model>)query.list(); 
        Model model = null;

        for (Model m : modelList) {
            if (m.getModelType().getId() == 3) {
                model = m;
                break;
            }
        }

        if (model == null) {
            Object[] arrModels = modelList.toArray();
            if (arrModels.length == 0) {
                throw new RuntimeException("Non esiste");
            }

            model = (Model)arrModels[0];

            if (openSession) {
                tx.commit();
            }
            return model;
        } catch(RuntimeException ex) {
            if (openSession) {
                tx.rollback();
            }
            ex.printStackTrace();
            if (responseMessage.compareTo("") == 0) {
                responseMessage = "Error" + ex.getMessage();
            }
            return null;        
        }
    }
}

tapi tetap saja, dapatkan error yang sama. Saya telah membaca banyak untuk kesalahan ini dan menemukan beberapa solusi yang mungkin. Salah satunya adalah untuk mengatur lazyLoad ke false, tetapi saya tidak diizinkan untuk melakukan ini itu sebabnya saya disarankan untuk mengontrol sesi

Blerta Dhimitri
sumber

Jawaban:

93

Apa yang salah di sini adalah bahwa konfigurasi manajemen sesi Anda diatur untuk menutup sesi ketika Anda melakukan transaksi. Periksa apakah Anda memiliki sesuatu seperti:

<property name="current_session_context_class">thread</property>

dalam konfigurasi Anda.

Untuk mengatasi masalah ini, Anda dapat mengubah konfigurasi pabrik sesi atau membuka sesi lain dan hanya meminta objek yang dimuat malas. Tapi yang saya sarankan di sini adalah menginisialisasi koleksi malas ini di getModelByModelGroup itu sendiri dan panggil:

Hibernate.initialize(subProcessModel.getElement());

ketika Anda masih dalam sesi aktif.

Dan satu hal lagi. Nasihat yang ramah. Anda memiliki sesuatu seperti ini dalam metode Anda:

for (Model m : modelList) {
    if (m.getModelType().getId() == 3) {
        model = m;
        break;
    }
}

Silakan insted kode ini hanya menyaring model-model dengan tipe id sama dengan 3 dalam pernyataan permintaan hanya beberapa baris di atas.

Beberapa bacaan lagi:

konfigurasi pabrik sesi

masalah dengan sesi tertutup

goroncy
sumber
1
Terima kasih! Saya memecahkan masalah saya menggunakan openSession () alih-alih getCurrentSession () sebagai salah satu tautan yang Anda berikan kepada saya menyarankan ini, tetapi sekarang saya khawatir jika salah melakukannya
Blerta Dhimitri
2
Tidak, itu mungkin baik-baik saja. Tetapi baca lebih banyak lagi untuk dapat sepenuhnya mengontrol sesi dan transaksi Anda. Sangat penting untuk mengetahui dasar-dasarnya karena semua teknologi tingkat tinggi seperti Spring, Hibernate, dan lainnya beroperasi pada konsep yang sama.
goroncy
179

Jika Anda menggunakan Spring tandai kelas sebagai @Transaksional , maka Spring akan menangani manajemen sesi.

@Transactional
public class MyClass {
    ...
}

Dengan menggunakan @Transactional, banyak aspek penting seperti propagasi transaksi ditangani secara otomatis. Dalam hal ini jika metode transaksional lain disebut metode akan memiliki opsi untuk bergabung dengan transaksi yang sedang berlangsung menghindari pengecualian "tidak ada sesi".

PERINGATAN Jika Anda menggunakannya @Transactional, harap perhatikan perilaku yang dihasilkan. Lihat artikel ini untuk perangkap umum. Misalnya, pembaruan untuk entitas tetap ada meskipun Anda tidak secara eksplisit meneleponsave

pengguna2601995
sumber
21
Saya tidak bisa melebih-lebihkan pentingnya jawaban ini. Saya sangat merekomendasikan mencoba opsi ini terlebih dahulu.
sparkyspider
6
Perhatikan juga bahwa Anda harus menambahkan @EnableTransactionManagementkonfigurasi Anda untuk memungkinkan transaksi. " jika metode transaksional lain disebut metode akan memiliki opsi untuk bergabung dengan transaksi yang sedang berlangsung " perilaku ini berbeda untuk cara transaksi yang berbeda diimplementasikan, yaitu antarmuka proxy vs proxy kelas atau tenun AspectJ. Lihat dokumentasi .
Erik Hofer
1
Haruskah kita memahami Transactionalanotasi Spring dengan demikian disarankan, tidak hanya untuk memodifikasi transaksi, tetapi juga untuk mengakses hanya yang?
Stephane
8
Saya sangat merekomendasikan untuk menggunakan anotasi ini di atas kelas hanya untuk pengujian. Kode nyata harus menandai setiap metode sebagai transaksi di kelas secara terpisah. Kecuali jika semua metode di kelas akan membutuhkan koneksi terbuka dengan transaksi ke database.
m1ld
1
Tidakkah aman menempatkan @Transactional (readOnly = true) daripada hanya @Transactional?
Hamedz
105

Anda dapat mencoba mengatur

<property name="hibernate.enable_lazy_load_no_trans">true</property>

di hibernate.cfg.xml atau persistence.xml

Masalah yang perlu diingat dengan properti ini dijelaskan dengan baik di sini

Wilianto Indrawan
sumber
8
Bisakah Anda menjelaskan artinya juga?
Mohit Kanwar
2
Saya juga penasaran apa yang dilakukannya. Itu memang memperbaiki masalah yang saya alami, tetapi saya ingin mengerti mengapa.
Hassan
3
untuk persistence.xml:<property name="hibernate.enable_lazy_load_no_trans" value="true"/>
ACV
33
Anda menyadari bahwa itu hibernate.enable_lazy_load_no_transadalah Anti-Pola , bukan?
Vlad Mihalcea
6
JANGAN GUNAKAN PROPERTI INI, JIKA SPRING MENGELOLA TRANSAKSI ANDA. PROPERTI INI AKAN MENUJU PELAYANAN TRANSAKSI, SIMPUL SPRING AKAN
MENUTUP
54

Cara terbaik untuk mengatasinyaLazyInitializationException adalah dengan menggunakan JOIN FETCHarahan:

Query query = session.createQuery(
    "from Model m " +
    "join fetch m.modelType " +
    "where modelGroup.id = :modelGroupId"
);

Pokoknya, JANGAN gunakan Anti-Pola berikut seperti yang disarankan oleh beberapa jawaban:

Terkadang, proyeksi DTO adalah pilihan yang lebih baik daripada mengambil entitas, dan dengan cara ini, Anda tidak akan mendapatkannya LazyInitializationException.

Vlad Mihalcea
sumber
Bagaimana Saya Bisa Mengidentifikasi panggilan mana yang mengalami masalah? Saya merasa sulit untuk mengidentifikasi panggilan. Apakah ada cara Untuk tujuan pengujian saya menggunakan FetchType=EAGER, tetapi ini bukan solusi yang tepat, bukan?
Shantaram Tupe
Cukup gunakan logging. Dan EAGER itu buruk, ya.
Vlad Mihalcea
Kemudian, Anda harus menggunakan DTO atau menginisialisasi semua asosiasi sebelum meninggalkan @Transactionallayanan.
Vlad Mihalcea
2
Kita harus menganjurkan praktik perusahaan terbaik tetapi bukan perbaikan cepat.
etlds
21

Saya mendapatkan kesalahan yang sama untuk hubungan satu ke banyak untuk penjelasan di bawah ini.

@OneToMany(mappedBy="department", cascade = CascadeType.ALL)

Diubah seperti di bawah ini setelah menambahkan fetch = FetchType.EAGER, itu berhasil untuk saya.

@OneToMany(mappedBy="department", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
Smruti R Tripathy
sumber
26
Ya itu mungkin memperbaikinya tetapi sekarang Anda memuat seluruh pohon data. Ini akan memiliki dampak kinerja negatif dalam banyak kasus
astro8891
13

jika Anda menggunakan pegas data jpa, pegas boot Anda dapat menambahkan baris ini di application.properties

spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
Shaaban Ebrahim
sumber
7
Ini dianggap sebagai anti-pola. vladmihalcea.com/…
Sudip Bhandari
9

Pengecualian ini karena saat Anda menelepon session.getEntityById(), sesi akan ditutup. Jadi, Anda perlu melampirkan kembali entitas ke sesi. Atau solusi Easy hanya mengkonfigurasi default-lazy="false" untuk Anda entity.hbm.xmlatau jika Anda menggunakan anotasi tambahkan saja @Proxy(lazy=false)ke kelas entitas Anda.

Reddeiah Pidugu
sumber
5

Saya mengalami masalah yang sama. Saya pikir cara lain untuk memperbaikinya adalah Anda dapat mengubah kueri untuk bergabung mengambil Elemen dari Model sebagai berikut:

Query query = session.createQuery("from Model m join fetch m.element where modelGroup.id = :modelGroupId")
Tony Vu
sumber
4

Ini berarti bahwa objek yang Anda coba akses tidak dimuat, jadi tulislah kueri yang membuat gabungan ambil dari objek yang Anda coba akses.

Misalnya:

Jika Anda mencoba untuk mendapatkan ObjectB dari ObjectA di mana ObjectB adalah kunci asing di ObjectA.

Pertanyaan:

SELECT objA FROM ObjectA obj JOIN FETCH obj.objectB objB
rex roy
sumber
3

Ada beberapa jawaban bagus di sini yang menangani kesalahan ini dalam cakupan luas. Saya mengalami situasi tertentu dengan Spring Security yang memiliki perbaikan cepat, meskipun mungkin tidak optimal.

Selama otorisasi pengguna (segera setelah masuk dan melewati otentikasi) saya menguji entitas pengguna untuk otoritas tertentu di kelas kustom yang memperluas SimpleUrlAuthenticationSuccessHandler.

Entitas pengguna saya mengimplementasikan UserDetails dan memiliki seperangkat Peran yang dimuat malas yang melemparkan "org.hibernate.LazyInitializationException - tidak dapat menginisialisasi proxy - tidak ada Sesi" pengecualian. Mengubah Set itu dari "fetch = FetchType.LAZY" menjadi "fetch = FetchType.EAGER" memperbaiki ini untuk saya.

Burung hantu malam
sumber
2

Menghadapi Pengecualian yang sama dalam berbagai kasus penggunaan.

masukkan deskripsi gambar di sini

Use Case: Cobalah membaca data dari DB dengan proyeksi DTO.

Solusi: Gunakan metode get alih-alih memuat .

Operasi Generik

public class HibernateTemplate {
public static Object loadObject(Class<?> cls, Serializable s) {
    Object o = null;
    Transaction tx = null;
    try {
        Session session = HibernateUtil.getSessionFactory().openSession();
        tx = session.beginTransaction();
        o = session.load(cls, s); /*change load to get*/
        tx.commit();
        session.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return o;
}

}

Kelas Kegigihan

public class Customer {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Id")
private int customerId;

@Column(name = "Name")
private String customerName;

@Column(name = "City")
private String city;

//constructors , setters and getters

}

Antarmuka CustomerDAO

public interface CustomerDAO 
     {
   public CustomerTO getCustomerById(int cid);
     }

Kelas Obyek Transfer Entitas

public class CustomerTO {

private int customerId;

private String customerName;

private String city;

//constructors , setters and getters

}

Kelas Pabrik

public class DAOFactory {

static CustomerDAO customerDAO;
static {
    customerDAO = new HibernateCustomerDAO();
}

public static CustomerDAO getCustomerDAO() {
    return customerDAO;
}

}

DAO spesifik entitas

public class HibernateCustomerDAO implements CustomerDAO {

@Override
public CustomerTO getCustomerById(int cid) {
    Customer cust = (Customer) HibernateTemplate.loadObject(Customer.class, cid);
    CustomerTO cto = new CustomerTO(cust.getCustomerId(), cust.getCustomerName(), cust.getCity());
    return cto;
}

}

Mengambil data: Kelas Tes

CustomerDAO cdao = DAOFactory.getCustomerDAO();
CustomerTO c1 = cdao.getCustomerById(2);
System.out.println("CustomerName -> " + c1.getCustomerName() + " ,CustomerCity -> " + c1.getCity());

Sajikan Data

masukkan deskripsi gambar di sini

Permintaan dan output yang dihasilkan oleh Sistem Hibernate

Hibernate: pilih customer0_.Id sebagai Id1_0_0_, customer0_.City sebagai City2_0_0_, customer0_.Name as Name3_0_0_ dari CustomerLab31 customer0_ di mana customer0_.Id =?

Nama Pelanggan -> Cody, CustomerCity -> LA

Kms
sumber
1

Jika Anda menggunakan Grail'sFramework, mudah untuk menyelesaikan pengecualian inisialisasi malas dengan menggunakanLazy kata kunci pada bidang tertentu di Kelas Domain.

Sebagai contoh:

class Book {
    static belongsTo = [author: Author]
    static mapping = {
        author lazy: false
    }
}

Temukan informasi lebih lanjut di sini

Zeb
sumber
1

Dalam kasus saya, kesalahan penempatan session.clear()menyebabkan masalah ini.

absin
sumber
1

Ini berarti Anda menggunakan JPA atau hibernasi dalam kode Anda dan melakukan operasi modifikasi pada DB tanpa melakukan transaksi logika bisnis. Jadi solusi sederhana untuk ini adalah tandai sepotong kode Anda @Transaksional

Aman Goel
sumber
-1

menggunakan session.get (*. class, id); tapi jangan memuat fungsinya

Artavazd Manukyan
sumber
3
bisakah kamu menjelaskan ini?
newday
-2

Anda juga bisa menyelesaikannya dengan menambahkan lazy = false ke dalam file * .hbm.xml Anda atau Anda dapat memasukkan objek Anda di Hibernate.init (Objek) ketika Anda mendapatkan objek dari db

Sandeep Roniyaar
sumber
10
umumnya menambahkan lazy = false bukan ide yang baik. itu sebabnya malas benar secara default
MoienGK
OP dengan jelas mengatakan sebelumnya bahwa dia tidak diizinkan melakukan itu.
GingerHead
-2

Lakukan perubahan berikut pada servlet-context.xml

    <beans:property name="hibernateProperties">
        <beans:props>

            <beans:prop key="hibernate.enable_lazy_load_no_trans">true</beans:prop>

        </beans:props>
    </beans:property>
ArunDhwaj IIITH
sumber