Come risolvere org.hibernate.LazyInitializationException - impossibile inizializzare il proxy - nessuna sessione


188

Ottengo la seguente eccezione:

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)

quando provo a chiamare dalla linea principale le seguenti linee:

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

Ho implementato il getModelByModelGroup(int modelgroupid)metodo in primo luogo in questo modo:

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

e ottenuto l'eccezione. Quindi un amico mi ha suggerito di testare sempre la sessione e ottenere la sessione corrente per evitare questo errore. Quindi ho fatto questo:

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

ma ancora, ottieni lo stesso errore. Ho letto molto per questo errore e ho trovato alcune possibili soluzioni. Uno di questi era impostare lazyLoad su false ma non mi è permesso farlo, ecco perché mi è stato suggerito di controllare la sessione

Risposte:


93

Ciò che è sbagliato qui è che la configurazione della gestione della sessione è impostata per chiudere la sessione quando si esegue il commit della transazione. Controlla se hai qualcosa come:

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

nella tua configurazione.

Per ovviare a questo problema è possibile modificare la configurazione del factory di sessione o aprire un'altra sessione e richiedere solo quegli oggetti caricati in modo pigro. Ma ciò che suggerirei qui è di inizializzare questa raccolta pigra in getModelByModelGroup stesso e chiamare:

Hibernate.initialize(subProcessModel.getElement());

quando sei ancora in sessione attiva.

E un'ultima cosa. Un consiglio amichevole. Hai qualcosa di simile nel tuo metodo:

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

Installa questo codice semplicemente filtra quei modelli con id di tipo uguale a 3 nell'istruzione query solo un paio di righe sopra.

Qualche altra lettura:

configurazione di fabbrica della sessione

problema con la sessione chiusa


1
Grazie! Ho risolto il mio problema usando openSession () invece di getCurrentSession () come suggerito da uno dei link che mi hai dato, ma ora ho paura che sia sbagliato farlo
Blerta Dhimitri,

2
No, probabilmente va bene. Ma leggi altro per essere in grado di controllare completamente le tue sessioni e transazioni. È davvero importante conoscere le basi perché tutte le tecnologie di livello superiore come Spring, Hibernate e altre operano sullo stesso concetto.
Goroncy,

179

Se si utilizza Spring, contrassegnare la classe come @Transactional , Spring gestirà la gestione della sessione.

@Transactional
public class MyClass {
    ...
}

Usando @Transactional, molti aspetti importanti come la propagazione delle transazioni vengono gestiti automaticamente. In questo caso, se viene chiamato un altro metodo transazionale, il metodo avrà la possibilità di unire la transazione in corso evitando l'eccezione "nessuna sessione".

ATTENZIONE Se lo usi @Transactional, tieni presente il comportamento risultante. Vedi questo articolo per insidie ​​comuni. Ad esempio, gli aggiornamenti alle entità persistono anche se non si chiama esplicitamentesave


21
Non posso sopravvalutare l'importanza di questa risposta. Consiglio vivamente di provare prima questa opzione.
sparkyspider,

6
Si noti inoltre che è necessario aggiungere @EnableTransactionManagementalla configurazione per abilitare le transazioni. " se viene chiamato un altro metodo transazionale, il metodo avrà la possibilità di unire la transazione in corso " questo comportamento è diverso per i diversi modi in cui vengono implementate le transazioni, vale a dire proxy interfaccia vs proxy classe o tessitura AspectJ. Fare riferimento alla documentazione .
Erik Hofer,

1
Dovremmo capire che l' Transactionalannotazione di primavera è quindi consigliata, non solo per modificare le transazioni, ma anche per accedere a quelle solo?
Stephane,

8
Consiglio vivamente di usare questa annotazione in cima alla classe solo per i test. Il codice reale dovrebbe contrassegnare ciascun metodo come transazione in classe separatamente. A meno che tutti i metodi in classe richiedano una connessione aperta con transazione al database.
m1ld

1
Non è sicuro mettere @Transactional (readOnly = true) anziché solo @Transactional?
Hamedz,

105

Puoi provare a impostare

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

in hibernate.cfg.xml o persistence.xml

Il problema da tenere a mente con questa proprietà è ben spiegato qui


8
Puoi spiegarne anche il significato?
Mohit Kanwar,

2
Sono anche curioso di sapere cosa faccia. Ha risolto il problema che stavo riscontrando, ma mi piacerebbe capire perché.
Hassan,

3
per persistence.xml:<property name="hibernate.enable_lazy_load_no_trans" value="true"/>
ACV


6
NON UTILIZZARE QUESTA PROPRIETÀ, SE LA PRIMAVERA GESTISCE LE VOSTRE OPERAZIONI QUESTA PROPRIETÀ TERRÀ A ESPLOSIONE DELLE TRANSAZIONI, SEMPLICEMENTE LA PRIMAVERA
SPEGNERÀ

54

Il modo migliore per gestireLazyInitializationException è utilizzare la JOIN FETCHdirettiva:

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

Comunque, NON usare i seguenti Anti-Pattern come suggerito da alcune delle risposte:

A volte, una proiezione DTO è una scelta migliore rispetto al recupero di entità, e in questo modo non ne otterrai nessuna LazyInitializationException.


Come posso identificare quale chiamata sta avendo problemi? Trovo difficile identificare la chiamata. C'è un modo? A scopo di test ho usato FetchType=EAGER, ma questa non è la soluzione corretta, giusto?
Shantaram Tupe,

Usa la registrazione. E EAGER è cattivo, sì.
Vlad Mihalcea,

Quindi, è necessario utilizzare DTO o inizializzare tutte le associazioni prima di uscire dal @Transactionalservizio.
Vlad Mihalcea,

2
Dovremmo sostenere la migliore pratica aziendale ma non la soluzione rapida.
etlds

21

Stavo ottenendo lo stesso errore per una o più relazioni per l'annotazione di seguito.

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

Modificato come di seguito dopo aver aggiunto fetch = FetchType.EAGER, ha funzionato per me.

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

26
Sì, potrebbe risolverlo, ma ora stai caricando l'intero albero di dati. Ciò avrà un impatto negativo sulle prestazioni nella maggior parte dei casi
astro8891,

13

se si utilizza jpa di dati di primavera, avvio di primavera è possibile aggiungere questa riga in application.properties

spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true

7
Questo è considerato un anti-pattern. vladmihalcea.com/…
Sudip Bhandari,

9

Questa eccezione a causa di quando si chiama session.getEntityById(), la sessione verrà chiusa. Quindi è necessario ricollegare l'entità alla sessione. Oppure la soluzione Easy è semplicemente configurata default-lazy="false" sulla tua entity.hbm.xmlo se stai usando le annotazioni aggiungi semplicemente @Proxy(lazy=false)alla tua classe di entità.


5

Ho riscontrato lo stesso problema. Penso che un altro modo per risolvere questo problema è che puoi cambiare la query per unirti a prendere il tuo elemento dal modello come segue:

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

4

Ciò significa che l'oggetto a cui si sta tentando di accedere non è caricato, quindi scrivere una query che recuperi l' unione dell'oggetto a cui si sta tentando di accedere.

Per esempio:

Se si sta tentando di ottenere ObjectB da ObjectA in cui ObjectB è una chiave esterna in ObjectA.

Query:

SELECT objA FROM ObjectA obj JOIN FETCH obj.objectB objB

3

Ci sono diverse buone risposte qui che gestiscono questo errore in un ampio ambito. Mi sono imbattuto in una situazione specifica con Spring Security che ha avuto una soluzione rapida, anche se probabilmente non ottimale.

Durante l'autorizzazione dell'utente (immediatamente dopo l'accesso e il superamento dell'autenticazione) stavo testando un'entità utente per un'autorità specifica in una classe personalizzata che estende SimpleUrlAuthenticationSuccessHandler.

La mia entità utente implementa UserDetails e ha un set di ruoli caricati in modo pigro che ha generato l'eccezione "org.hibernate.LazyInitializationException - impossibile inizializzare il proxy - nessuna sessione". La modifica del set da "fetch = FetchType.LAZY" a "fetch = FetchType.EAGER" ha risolto questo problema per me.



2

Di fronte alla stessa eccezione in caso di utilizzo diverso.

inserisci qui la descrizione dell'immagine

Caso d'uso: provare a leggere i dati dal DB con la proiezione DTO.

Soluzione: utilizzare il metodo get anziché load .

Operazione generica

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

}

Classe di persistenza

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

}

Interfaccia clienteDAO

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

Classe oggetto trasferimento entità

public class CustomerTO {

private int customerId;

private String customerName;

private String city;

//constructors , setters and getters

}

Classe di fabbrica

public class DAOFactory {

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

public static CustomerDAO getCustomerDAO() {
    return customerDAO;
}

}

DAO specifico dell'entità

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

}

Recupero dati: classe test

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

Dati attuali

inserisci qui la descrizione dell'immagine

Query e output generati da Hibernate System

Sospensione: selezionare customer0_.Id come Id1_0_0_, customer0_.City come City2_0_0_, customer0_.Name come Name3_0_0_ da CustomerLab31 customer0_ dove customer0_.Id =?

CustomerName -> Cody, CustomerCity -> LA


1

Se si utilizza Grail'sFramework, è semplice risolvere l' eccezione di inizializzazione lazy utilizzando la Lazyparola chiave su un campo specifico in Domain Class.

Per esempio:

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

Ulteriori informazioni qui


1

Nel mio caso un mal posto session.clear()stava causando questo problema.


1

Ciò significa che stai utilizzando JPA o ibernazione nel tuo codice e stai eseguendo operazioni di modifica su DB senza effettuare la transazione della logica di business. Una soluzione così semplice per questo è contrassegnare il tuo pezzo di codice @Transactional



-2

potresti anche risolverlo aggiungendo lazy = false nel tuo file * .hbm.xml oppure puoi avviare il tuo oggetto in Hibernate.init (Object) quando ottieni l'oggetto da db


10
in genere l'aggiunta di lazy = false non è una buona idea. ecco perché lazy è vero per impostazione predefinita
MoienGK,

L'OP ha chiaramente affermato in anticipo che non gli è permesso farlo.
GingerHead,

-2

Apportare le seguenti modifiche in 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>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.