Error en Hibernate Could not initialize proxy – no Session
Aunque Hibernate nos puede facilitar mucho los desarrollos gracias a la implementación de JPA, e incluso conociendo los error más comunes con Hibernate en algunas ocasiones nos aparecerá el típico .Error en Hibernate Could not initialize proxy – no Session
¿Qué es el error Could not initialize proxy – no Session?
Cuando intentamos acceder a un objeto que esta definido como Lazy y fuera del contexto de la sesión que esta abierta por Hibernate obtendremos el error Could not initialize proxy – no Session.
Es decir, este error nos va a ocurrir cuando cargamos un objeto o intentamos cargar un objeto lazy de la base de datos haciendo uso de un objeto proxy, pero la sesión de Hibernate ya esta cerrada.
Para entender mejor este error es esencial entender mejor algunos conceptos.
- La Session es el contexto persistente y el que maneja los objetos entre nuestra aplicación y la base de datos a la que estamos conectados.
- Carga Lazy, esos objetos no son cargados en el contexto de la sesión hasta que son accesibles en el código. Por ejemplo, podemos crear una relación oneToMany como lazy y la BBDD no nos traerá la colección marcada como Lazy hasta que hagamos un get sobre ese atributo.
- Proxy, Hibernate crea un objeto proxy que actúa como intermediario entre la base de datos y el objeto al que se accede, es decir, Hibernate proxy es una referencia de un objeto que podría no existir.
Una vez hemos entendido algo mejor por qué se produce este error vamos a ver ejemplos, con lo que entenderemos mejor los motivos.
Ejemplos con Hibernate Could not initialize proxy – no Session
Para ver los ejemplos vamos a partir de dos entidades, Company y Employee. La relación es 1..N, es decir, Company tiene una lista de Employees.
Creación de Entidades
@Entity public class Company { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column private int id; @Column private String name; @Column private String address; @OneToMany private Set<Employee> employees = new HashSet(); }
@Entity public class Employee { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private int id; @Column private String name; @Column private String surname; }
La anterior relación entre company y empleado tiene una carga LAZY que es la que establece JPA por defecto.
Error Hibernate Could not initialize proxy – no Session
Partiendo de las entidades anteriores vamos a provocar el Error Hibernate Could not initialize proxy – no Session. Para ello vamos a comenzar una session, obtener los employees, cerrar la session y volver a obtenerlos.
Session session = sessionFactory.openSession(); session.beginTransaction(); Company companySaved = session.find(Company.class, 1L); session.getTransaction().commit(); session.close(); log.info(companySaved.getEmployees().size());
El ejemplo anterior lanzará una excepción de tipo LazyInitializationException, ya que se esta intentando recuperar a los empleados de la relación una vez se ha cerrado la session siendo esta colección de carga LAZY.
Como solucionar Hibernate Could not initialize proxy – no Session
Vamos a ver las aproximaciones que tenemos para evitar obtener la excepción LazyInitializationException.
Utilizar FetchType.EAGER
Por defecto JPA nos va a establecer una aproximación LAZY para nuestras relaciones, de manera que solo se hará una carga de aquello que necesitamos, haciendo una query extra para la carga LAZY. Pero al estar con LAZY podemos incurrir en el error de Hibernate could not initialize proxy – no Session, por lo que se puede optar por usar la aproximación de FetchType.EAGER para evitar el error.
Con FetchType.EAGER lo que vamos a conseguir es cargar toda la relación en una única query con lo que no habrá que hacer peticiones adicionales a BBDD; por ejemplo en el caso anterior tendremos todos los employees ya en la variable y no habrá que hacer más peticiones a Base de Datos.
Hacer uso de Criteria
Otra manera que tenemos de solucionar los problemas de Lazy Exception es haciendo uso de Criteria.
De esta manera traeremos todo lo necesario de la Base de Datos en una única petición:
Criteria criteria = session.createCriteria(Company.class); criteria.setFetchMode("employees", FetchMode.EAGER);
Utilizar Join Fetch
Junto con los Criteria hacer una query empleando por ejemplo @Query es una de las formas más óptimas de evitar la excepción de LazyInitializationException.
Por ejemplo:
@Query("SELECT c FROM COMPANY c JOIN FETCH c.employees") public List<Company> getAll();
Esta es una de las mejores aproximaciones (junto con Criteria) ya que únicamente lanzamos una query para obtener tanto Company como Employees.
Hacer uso del Anti Patrón enable_lazy_load_no_trans
Si activamos la propiedad hibernate.enable_lazy_load_no_trans evitaríamos los problemas de LazyException, pero no es nada recomendado por un lado, ya que haría nuestra aplicación mucho más lenta en temas de rendimiento. Para el caso de ejemplo que tenemos haríamos una select para la Company y N selects para los employees.
Con esta aproximación tratamos los síntomas del problema, pero no la solución.
jpa: properties: hibernate: enable_lazy_load_no_trans: true
Conclusión
Para solucionar el Error en Hibernate Could not initialize proxy – no Session, podemos utilizar diferentes aproximaciones como hemos visto antes, pero hay que tener cuidado de como las aplicamos. Por ejemplo si aplicamos Eager y tenemos muchos hijos, nietos etc, podemos incurrir en una performance muy mala, o si aplicamos enable_lazy_load_no_trans.
Para evitar impactar al rendimiento lo mejor es detectar y acotar el problema para reducirlo lo máximo posible y ver si podemos utilizar una Join o una Criteria.
Si necesitas más información puedes escribirnos un comentario o un correo electrónico a refactorizando.web@gmail.com o también nos puedes contactar por nuestras redes sociales Facebook o twitter y te ayudaremos encantados!