Org.hibernate.LazyInitializationException Nasıl Onarılır - Proxy başlatılamadı - Oturum Yok


188

Aşağıdaki istisnayı alıyorum:

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)

aşağıdaki hatlardan aramaya çalıştığımda:

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

getModelByModelGroup(int modelgroupid)Öncelikle böyle bir yöntem uyguladım :

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;
    }
}

ve istisnayı yakaladı. Sonra bir arkadaşım her zaman oturumu test etmemi ve bu hatayı önlemek için geçerli oturumu almamı önerdi. Ben de bunu yaptım:

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;        
        }
    }
}

ancak yine de aynı hatayı alın. Bu hata için çok şey okudum ve bazı olası çözümler buldum. Bunlardan biri lazyLoad'ı false olarak ayarlamaktı, ancak bunu yapmama izin verilmiyor, bu yüzden oturumu kontrol etmem önerildi

Yanıtlar:


93

Burada yanlış olan şey, işlem yaptığınızda oturum yönetimi yapılandırmanızın oturumu kapatmaya ayarlanmış olmasıdır. Şunun gibi bir şey olup olmadığını kontrol edin:

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

yapılandırma.

Bu sorunun üstesinden gelmek için, oturum fabrikasının yapılandırmasını değiştirebilir veya başka bir oturum açabilir ve yalnızca bu tembel yüklenen nesneleri isteyebilirsiniz. Ama burada önermek getModelByModelGroup kendisi bu tembel koleksiyonu başlatmak ve aramaktır:

Hibernate.initialize(subProcessModel.getElement());

hala aktif oturumda olduğunuzda.

Ve son bir şey. Dostça bir tavsiye. Yönteminizde böyle bir şey var:

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

Lütfen bu kodun sadece yukarıdaki satırın birkaç satırında sorgu deyiminde 3'e eşit kimliğe sahip modelleri filtreleyin.

Biraz daha okuma:

oturum fabrika yapılandırması

kapalı oturumda sorun


1
Teşekkür ederim! Bana verdiğin bağlantılardan biri olarak getCurrentSession () yerine openSession () kullanarak sorunumu çözdüm, ancak şimdi bunu yapmak yanlışsa korkuyorum
Blerta Dhimitri

2
Hayır, muhtemelen iyi. Ancak oturumlarınızı ve işlemlerinizi tam olarak kontrol edebilmek için biraz daha okuyun. Temelleri bilmek gerçekten önemlidir, çünkü Spring, Hibernate ve daha fazlası gibi tüm üst düzey teknolojiler aynı kavram üzerinde çalışır.
goroncy

179

Spring kullanıyorsanız sınıfı @Tacacactional olarak işaretlerseniz, Spring oturum yönetimini yönetir .

@Transactional
public class MyClass {
    ...
}

Kullanarak @Transactional, bu tür işlem yayılımı gibi birçok önemli yönleri otomatik olarak işlenir. Bu durumda, başka bir işlem yöntemi çağrılırsa, yöntem "oturum yok" istisnasından kaçınarak devam eden işleme katılma seçeneğine sahip olacaktır.

UYARI Eğer kullanımını yaparsanız @Transactional, ortaya çıkan davranış unutmayın. Sık karşılaşılan tuzaklar için bu makaleye bakın . Örneğin, açıkça aramasanız bile varlıklara yönelik güncellemeler devam edersave


21
Bu cevabın önemini abartmam. Önce bu seçeneği denemenizi tavsiye ederim.
sparkyspider

6
@EnableTransactionManagementİşlemleri etkinleştirmek için yapılandırmanıza eklemeniz gerektiğini de unutmayın . " başka bir işlem yöntemi çağrılırsa, yöntem devam eden işleme katılma seçeneğine sahip olacaktır " bu davranış, işlemlerin uygulanma biçimlerinin farklı olması için farklıdır, yani arabirim proxy'si ile sınıf proxy'si veya AspectJ dokuma. Belgelere bakın .
Erik Hofer

1
Bu Transactionalnedenle Bahar ek açıklamasının sadece işlemleri değiştirmek için değil, aynı zamanda yalnızca bunlara erişmek için de önerildiğini anlamalıyız ?
Stephane

8
Bu ek açıklamayı yalnızca test için sınıfın üstünde kullanmanızı şiddetle tavsiye ederim. Gerçek kod, her yöntemi ayrı ayrı sınıfta işlem olarak işaretlemelidir. Sınıftaki tüm yöntemler sürece veritabanına işlem ile açık bağlantı gerektirmez.
m1ld

1
Sadece @Transactional yerine @Transactional (readOnly = true) koymak güvenli değil mi?
Hamedz

105

Ayarlamayı deneyebilirsiniz

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

hibernate.cfg.xml veya persistence.xml dosyasında

Bu özelliğin akılda tutulması gereken sorun burada iyi açıklanmıştır


8
Anlamını da açıklayabilir misiniz?
Mohit Kanwar

2
Bunun ne yaptığını da merak ediyorum. Yaşadığım sorunu düzeltti, ama nedenini anlamak istiyorum.
Hassan

3
<property name="hibernate.enable_lazy_load_no_trans" value="true"/>
.xml

33
Bunun hibernate.enable_lazy_load_no_transbir Anti-Desenler olduğunu anlıyorsunuz , değil mi?
Vlad Mihalcea

6
İLKBAHAR İŞLEMLERİNİZİ YÖNETİYORSA BU MÜLKİYİ KULLANMAYIN BU MÜLKİYET İŞLEMLERİ PATLAMAYA
HAZIR

54

Bununla başa çıkmanın en iyi yoluLazyInitializationExceptionJOIN FETCH direktifi kullanmaktır :

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

Her neyse, bazı cevapların önerdiği şekilde aşağıdaki Anti-Pattern'leri KULLANMAYIN:

Bazen, bir DTO projeksiyonu , objeleri almaktan daha iyi bir seçimdir ve bu şekilde herhangi bir şey elde edemezsiniz LazyInitializationException.


Hangi çağrının sorun yaşadığını nasıl belirleyebilirim? Çağrıyı tanımlamakta zorlanıyorum. Herhangi bir yol var mı ? Test amacıyla kullandım FetchType=EAGER, ama bu doğru bir çözüm değil, değil mi?
Shantaram Tupe

Sadece günlüğe kaydetmeyi kullanın. Ve EAGER kötü, evet.
Vlad Mihalcea

Ardından, @Transactionalhizmetten ayrılmadan önce DTO'ları kullanmalı veya tüm ilişkilendirmeleri başlatmalısınız .
Vlad Mihalcea

2
En iyi kurumsal uygulamayı savunmalıyız, ancak hızlı düzeltmeyi değil.
etlds

21

Aşağıdaki açıklama için bir ila birçok ilişki için aynı hatayı alıyordum.

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

Fetch = FetchType.EAGER eklendikten sonra aşağıdaki gibi değiştirildi, benim için çalıştı.

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

26
Evet düzeltebilir, ancak şimdi tüm veri ağacını yüklüyorsunuz. Bunun çoğu durumda olumsuz performans etkileri olacaktır
astro8891


9

Bu istisna, aradığınızda session.getEntityById()oturum kapatılacaktır. Bu nedenle varlığı oturuma yeniden eklemeniz gerekir. Veya Kolay çözüm sadece default-lazy="false" sizin için yapılandırılır entity.hbm.xmlveya ek açıklamalar kullanıyorsanız @Proxy(lazy=false)varlık sınıfınıza ekleyin .


5

Aynı sorunla karşılaştım. Bunu düzeltmek için başka bir yol aşağıdaki gibi Model öğesinden getirmek için sorguyu değiştirmek olduğunu düşünüyorum:

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

4

Bu, erişmeye çalıştığınız nesnenin yüklü olmadığı anlamına gelir; bu nedenle erişmeye çalıştığınız nesnenin birleştirme getirmesini sağlayan bir sorgu yazın .

Örneğin:

ObjectB, ObjectA'da ObjectA'da yabancı bir anahtar olduğu ObjectA'dan almaya çalışıyorsanız.

Sorgu :

SELECT objA FROM ObjectA obj JOIN FETCH obj.objectB objB

3

Burada bu hatayı geniş bir kapsamda ele alan birkaç iyi cevap var. Spring Security ile, muhtemelen optimal olmasa da, hızlı bir şekilde düzeltilen belirli bir duruma girdim.

Kullanıcı yetkilendirme sırasında (oturum açtıktan ve kimlik doğrulamasını geçtikten hemen sonra) bir kullanıcı varlığını SimpleUrlAuthenticationSuccessHandler'ı genişleten özel bir sınıfta belirli bir yetki için test ediyordum.

Kullanıcı varlığım UserDetails uygular ve "org.hibernate.LazyInitializationException başlatılamadı - proxy - Oturum yok" istisnasını atayan bir dizi tembel yüklenen Rol vardır. Bu Seti "fetch = FetchType.LAZY" yerine "fetch = FetchType.EAGER" olarak değiştirmek bunu benim için düzeltti.



2

Farklı kullanım durumunda aynı İstisna ile karşı karşıya.

resim açıklamasını buraya girin

Kullanım Durumu: DTO projeksiyonu ile DB'den veri okumaya çalışın .

Çözüm: Kullanım olsun yöntemi yerine yük .

Genel İşlem

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;
}

}

Kalıcılık Sınıfı

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

}

Müşteri DAO arayüzü

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

Varlık Aktarım Nesnesi Sınıfı

public class CustomerTO {

private int customerId;

private String customerName;

private String city;

//constructors , setters and getters

}

Fabrika Sınıfı

public class DAOFactory {

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

public static CustomerDAO getCustomerDAO() {
    return customerDAO;
}

}

Varlığa özgü DAO

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;
}

}

Veri alınıyor: Test Sınıfı

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

Mevcut Veriler

resim açıklamasını buraya girin

Hazırda Bekletme Sistemi tarafından oluşturulan sorgu ve çıktı

Hazırda bekletme: Id1_0_0_ olarak customer0_.Id, City2_0_0_ olarak customer0_.City, customer0_.Name'i CustomerLab31 customer0_ dan customer3_0_Id = olarak Name3_0_0_ olarak seçin?

MüşteriAdı -> Cody, CustomerCity -> LA


1

Grail'sFramework kullanıyorsanız , tembel başlatma istisnasınıLazy Etki Alanı Sınıfı'ndaki belirli bir alanda anahtar kelime kullanarak çözmek kolaydır .

Örneğin:

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

Daha fazla bilgiyi burada bulabilirsiniz


1

Benim durumumda yanlış yerleştirilmiş session.clear()bu soruna neden oluyordu.


1

Bu, kodunuzda JPA veya hazırda bekletme modunu kullandığınız ve iş mantığı işlemini yapmadan DB'de değiştirme işlemi yaptığınız anlamına gelir. Bunun için basit bir çözüm, kod parçanızı @Transactional olarak işaretlemektir



-2

* .hbm.xml dosyanıza tembel = false ekleyerek de çözebilirsiniz veya db'den nesne aldığınızda nesnenizi Hibernate.init (Object) içinde başlatabilirsiniz.


10
genellikle tembel = yanlış eklemek iyi bir fikir değildir. bu yüzden tembel varsayılan olarak doğrudur
MoienGK

OP önceden bunu yapmasına izin verilmediğini açıkça söyledi.
GingerHead

-2

Servlet-context.xml dosyasında aşağıdaki değişiklikleri yapın

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

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

        </beans:props>
    </beans:property>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.