Cómo arreglar org.hibernate.LazyInitializationException - no se pudo inicializar el proxy - sin sesión


188

Me sale la siguiente excepción:

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)

cuando intento llamar desde main las siguientes líneas:

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

getModelByModelGroup(int modelgroupid)Primero implementé el método así:

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

Y obtuve la excepción. Luego, un amigo me sugirió que siempre probara la sesión y obtuviera la sesión actual para evitar este error. Entonces hice esto:

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

pero aún así, obtiene el mismo error. He estado leyendo mucho para este error y encontré algunas posibles soluciones. Una de ellas fue establecer lazyLoad en false, pero no se me permite hacer esto, por eso me sugirieron controlar la sesión

Respuestas:


93

Lo que está mal aquí es que su configuración de administración de sesión está configurada para cerrar sesión cuando confirma la transacción. Comprueba si tienes algo como:

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

en su configuración

Para superar este problema, puede cambiar la configuración de la fábrica de sesiones o abrir otra sesión y solo pedir esos objetos cargados lentamente. Pero lo que sugeriría aquí es inicializar esta colección perezosa en getModelByModelGroup y llamar:

Hibernate.initialize(subProcessModel.getElement());

cuando todavía estás en sesión activa.

Y una última cosa. Un consejo amigable. Tienes algo como esto en tu método:

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

En lugar de este código, simplemente filtre esos modelos con una identificación de tipo igual a 3 en la instrucción de consulta solo un par de líneas arriba.

Un poco más de lectura:

configuración de fábrica de sesión

problema con sesión cerrada


1
¡Gracias! Resolví mi problema usando openSession () en lugar de getCurrentSession () ya que uno de los enlaces que me dio sugirió esto, pero ahora tengo miedo si es incorrecto hacerlo
Blerta Dhimitri

2
No, probablemente esté bien. Pero lea un poco más para poder controlar completamente sus sesiones y transacciones. Es realmente importante conocer los conceptos básicos porque todas las tecnologías de nivel superior como Spring, Hibernate y más funcionan con el mismo concepto.
goroncy

179

Si usa Spring, marque la clase como @Transactional , Spring se encargará de la gestión de la sesión.

@Transactional
public class MyClass {
    ...
}

Mediante el uso @Transactional, muchos aspectos importantes, como la propagación de transacciones, se manejan automáticamente. En este caso, si se llama a otro método transaccional, el método tendrá la opción de unirse a la transacción en curso evitando la excepción de "no sesión".

ADVERTENCIA Si lo usa @Transactional, tenga en cuenta el comportamiento resultante. Vea este artículo para las trampas comunes. Por ejemplo, las actualizaciones de las entidades persisten incluso si no llama explícitamentesave


21
No puedo exagerar la importancia de esta respuesta. Recomiendo probar esta opción primero.
sparkyspider

66
También tenga en cuenta que debe agregar @EnableTransactionManagementa su configuración para habilitar las transacciones. " Si se llama a otro método transaccional, el método tendrá la opción de unirse a la transacción en curso ". Este comportamiento es diferente para las diferentes formas en que se implementan las transacciones, es decir, el proxy de interfaz frente al proxy de clase o el tejido de AspectJ. Consulte la documentación .
Erik Hofer

1
¿Debemos entender que la Transactionalanotación Spring se recomienda, no solo para modificar transacciones, sino también para acceder solo a ellas?
Stephane

8
Recomiendo usar esta anotación en la parte superior de la clase solo para realizar pruebas. El código real debe marcar cada método como transacción en clase por separado. A menos que todos los métodos en clase requieran una conexión abierta con la transacción a la base de datos.
m1ld

1
¿No es seguro poner @Transactional (readOnly = true) en lugar de solo @Transactional?
Hamedz

105

Puedes intentar establecer

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

en hibernate.cfg.xml o persistence.xml

El problema a tener en cuenta con esta propiedad está bien explicado aquí.


8
¿Puedes explicar su significado también?
Mohit Kanwar

2
También tengo curiosidad por lo que esto hace. Solucionó el problema que estaba teniendo, pero me gustaría entender por qué.
Hassan

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


66
NO USE ESTA PROPIEDAD, SI SPRING MANEJA SUS TRANSACCIONES ESTA PROPIEDAD LLEVARÁ A LA EXPLOSIÓN DE TRANSACCIONES, SIMPLEMENTE SPRING
CERRARÁ

54

La mejor manera de manejar estoLazyInitializationException es usar la JOIN FETCHdirectiva:

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

De todos modos, NO use los siguientes antipatrones como sugieren algunas de las respuestas:

A veces, una proyección DTO es una mejor opción que buscar entidades, y de esta manera, no obtendrá ninguna LazyInitializationException.


¿Cómo puedo identificar qué llamada tiene problemas? Me resulta difícil identificar la llamada. Hay alguna manera ? Para fines de prueba que usé FetchType=EAGER, pero esta no es la solución correcta, ¿verdad?
Shantaram Tupe

Solo usa el registro. Y EAGER es malo, sí.
Vlad Mihalcea

Luego, debe usar DTO o inicializar todas las asociaciones antes de abandonar el @Transactionalservicio.
Vlad Mihalcea

2
Deberíamos defender la mejor práctica empresarial, pero no la solución rápida.
etlds

21

Obtuve el mismo error para una o varias relaciones para la anotación siguiente.

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

Cambió como se muestra a continuación después de agregar fetch = FetchType.EAGER, funcionó para mí.

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

26
Sí, puede solucionarlo, pero ahora está cargando todo el árbol de datos. Esto tendrá un impacto negativo en el rendimiento en la mayoría de los casos
astro8891


9

Esta excepción debido a cuando llamas session.getEntityById(), la sesión se cerrará. Por lo tanto, debe volver a adjuntar la entidad a la sesión. O solución fácil es configurar default-lazy="false" a su entity.hbm.xmlo si está utilizando anotaciones sólo tiene que añadir @Proxy(lazy=false)a su clase de entidad.


5

Encontré el mismo problema. Creo que otra forma de solucionar esto es que puede cambiar la consulta para unirse a buscar su Elemento del Modelo de la siguiente manera:

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

4

Esto significa que el objeto al que está intentando acceder no está cargado, por lo tanto, escriba una consulta que realice una búsqueda de unión del objeto al que está intentando acceder.

P.ej:

Si está intentando obtener ObjectB de ObjectA donde ObjectB es una clave foránea en ObjectA.

Consulta :

SELECT objA FROM ObjectA obj JOIN FETCH obj.objectB objB

3

Hay varias buenas respuestas aquí que manejan este error en un amplio alcance. Me encontré con una situación específica con Spring Security que tenía una solución rápida, aunque probablemente no óptima.

Durante la autorización del usuario (inmediatamente después de iniciar sesión y pasar la autenticación) estaba probando una entidad de usuario para una autoridad específica en una clase personalizada que extiende SimpleUrlAuthenticationSuccessHandler.

Mi entidad de usuario implementa UserDetails y tiene un conjunto de roles con carga diferida que arrojó la excepción "org.hibernate.LazyInitializationException - no se pudo inicializar proxy - sin sesión". Cambiar ese conjunto de "fetch = FetchType.LAZY" a "fetch = FetchType.EAGER" me arregló esto.



2

Enfrentó la misma excepción en un caso de uso diferente.

ingrese la descripción de la imagen aquí

Caso de uso: intente leer datos de DB con proyección DTO.

Solución: utilice el método get en lugar de load .

Operación Genérica

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

}

Clase de persistencia

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

}

Interfaz CustomerDAO

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

Clase de objeto de transferencia de entidad

public class CustomerTO {

private int customerId;

private String customerName;

private String city;

//constructors , setters and getters

}

Clase de fábrica

public class DAOFactory {

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

public static CustomerDAO getCustomerDAO() {
    return customerDAO;
}

}

Entidad específica 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;
}

}

Recuperando datos: clase de prueba

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

Datos actuales

ingrese la descripción de la imagen aquí

Consulta y salida generada por el sistema Hibernate

Hibernar: seleccione customer0_.Id como Id1_0_0_, customer0_.City como City2_0_0_, customer0_.Name como Name3_0_0_ de CustomerLab31 customer0_ donde customer0_.Id =?

CustomerName -> Cody, CustomerCity -> LA


1

Si está utilizando Grail'sFramework, es simple resolver la excepción de inicialización diferida mediante el uso de Lazypalabras clave en un campo específico en la Clase de dominio.

Por ejemplo:

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

Encuentra más información aquí


1

En mi caso, un lugar equivocado session.clear()estaba causando este problema.


1

Esto significa que está utilizando JPA o hiberna en su código y está realizando una operación de modificación en la base de datos sin realizar la transacción de lógica de negocios. Entonces, una solución simple para esto es marcar su código @Transactional



-2

también puede resolverlo agregando lazy = false en su archivo * .hbm.xml o puede iniciar su objeto en Hibernate.init (Object) cuando obtiene un objeto de db


10
generalmente agregar perezoso = falso no es una buena idea. es por eso que perezoso es cierto por defecto
MoienGK

El OP claramente dijo de antemano que no se le permite hacer eso.
GingerHead

-2

Realice los siguientes cambios en 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>
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.