org.hibernate.LazyInitializationException - could not initialize proxy - no Session
Buen día,
Tengo un inconveniente, en un desarrollo web que estoy realizando.
Estoy usando :
Hibernate, Java, servicios web.
Tengo 2 proyectos:
ServicioWeb
Este proyecto posee las entidades.java, entidades.hbm.xml, las configuraciones hibernate.cfg.xml y hibernate.reveng.xml
Por ejemplo:
Tengo la entidad SairBriAcciones que está relacionada con la entidad SairBriEstatus :
private BigDecimal idAccion;
private SairBriEstatus sairBriEstatus;
private SairBriBrief sairBriBrief;
private String accion;
private Date fecha;
private Set sairBriAccionAreas = new HashSet(0);
public SairBriAcciones() {
}
public SairBriAcciones(BigDecimal idAccion) {
this.idAccion = idAccion;
}
public SairBriAcciones(BigDecimal idAccion, SairBriEstatus sairBriEstatus,
SairBriBrief sairBriBrief, String accion, Date fecha,
Set sairBriAccionAreas) {
this.idAccion = idAccion;
this.sairBriEstatus = sairBriEstatus;
this.sairBriBrief = sairBriBrief;
this.accion = accion;
this.fecha = fecha;
this.sairBriAccionAreas = sairBriAccionAreas;
}
public BigDecimal getIdAccion() {
return this.idAccion;
}
public void setIdAccion(BigDecimal idAccion) {
this.idAccion = idAccion;
}
public SairBriEstatus getSairBriEstatus() {
return this.sairBriEstatus;
}
public void setSairBriEstatus(SairBriEstatus sairBriEstatus) {
this.sairBriEstatus = sairBriEstatus;
}
public SairBriBrief getSairBriBrief() {
return this.sairBriBrief;
}
public void setSairBriBrief(SairBriBrief sairBriBrief) {
this.sairBriBrief = sairBriBrief;
}
public String getAccion() {
return this.accion;
}
public void setAccion(String accion) {
this.accion = accion;
}
public Date getFecha() {
return this.fecha;
}
public void setFecha(Date fecha) {
this.fecha = fecha;
}
public Set getSairBriAccionAreas() {
return this.sairBriAccionAreas;
}
public void setSairBriAccionAreas(Set sairBriAccionAreas) {
this.sairBriAccionAreas = sairBriAccionAreas;
}
}
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 01/12/2016 12:38:37 PM by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
<class name="ec.com.rgt.sair.hbm.SairBriAcciones" table="SAIR_BRI_ACCIONES" schema="USRSAIR">
<id name="idAccion" type="big_decimal">
<column name="ID_ACCION" precision="22" scale="0" />
<generator class="assigned" />
</id>
<many-to-one name="sairBriEstatus" class="ec.com.rgt.sair.hbm.SairBriEstatus" fetch="select" >
<column name="ID_ESTATUS" precision="22" scale="0" />
</many-to-one>
<many-to-one name="sairBriBrief" class="ec.com.rgt.sair.hbm.SairBriBrief" fetch="select" >
<column name="ID_BRIEF" precision="22" scale="0" />
</many-to-one>
<property name="accion" type="string">
<column name="ACCION" length="200" />
</property>
<property name="fecha" type="date">
<column name="FECHA" length="7" />
</property>
</class>
</hibernate-mapping>
Tengo una clase prueba en el proyecto ServicioWeb para probar si funciona la consulta, la cual es la siguiente:
La prueba anterior si me funciona y me muestra la información, pero cunando agrego la siguiente linea, salta la excepción org.hibernate.LazyInitializationException: could not initialize proxy - no Session:
public static void main(String[] args) {
DAO dao=new DAO();
List<SairBriAcciones> list=dao.find("from SairBriAcciones a ");
for (SairBriAcciones acciones : list) {
System.out.println(acciones.getAccion());
System.out.println(acciones.getSairBriEstatus().getNombre());//Se agrega esta linea para consultar la relación
}
}
}
La excepción es la siguiente:
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:132)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:174)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
at ec.com.rgt.sair.hbm.SairBriEstatus_$$_javassist_91.getNombre(SairBriEstatus_$$_javassist_91.java)
at ec.com.rgt.sair.controller.Prueba.main(Prueba.java:16)
Así que investigue, investigue ,investigue ... y encontre 2 soluciones:
- Usar join fetch en las consultas cuando se desea consultar alguna entidad relacionada
- Activar el atributo lazy="false" en el archivo
List<SairBriAcciones> list=dao.find("from SairBriAcciones a join fetch a.sairBriEstatus ");//Se agrega el join fetch
<column name="ID_ESTATUS" precision="22" scale="0" />
</many-to-one>
Probé ambas y funcionaron, asi que decidi utilizar la primera porque la segunda opción (según lo que investigue) siempre te trae la información de la entidad relacionada haciendo que el rendimiento sea vea afectado, en cambio la primera solo se trae la información cuando usas el join fetch.
Por ejemplo de las dos clases pruebas anteriores:
Aplicando la primera solución para la consulta sin el join fetch, esta solo trae la tabla la información de SairBriAcciones y no la SairBriEstatus(estaría bien porque solo necesito la tabla SairBriAcciones ), para la segunda consulta aplicando el join fetch trae la información de ambas SairBriAcciones y SairBriEstatus porque yo se lo dije que la traiga por medio del join fetch.
Ahora aplicando la segunda solución del atributo lazy="false" para la consulta sin el join fetch este trae la información de ambas tablas SairBriAcciones y SairBriEstatus, entonces aqui es donde el redimiento se ve afectado porque no necesito la informacion de la tabla SairBriEstatus porque solo necesito la de SairBriAcciones, pero el atributo lazy="false" significa que siempre la va atraer.
Bueno eso fue lo que aprendí investigando y lo confirme con las pruebas.
Pero el problema viene en el otro proyecto.
Este el método de consulta que proporcione para que el otro proyecto(Cliente) lo consuma:
public List<SairBriAcciones> consulta_acciones(String hql){
@SuppressWarnings("unchecked")
List<SairBriAcciones> list=dao.find(hql);
return list;
}
Cliente
Este proyecto consume el método consulta_acciones que proporcione del proyecto ServicioWeb.
También posee la clase prueba.
Este es el método para consumir el servicioweb:
Entonces realizando la misma prueba (en el Cliente):
ServiciosWeb web=new ServiciosWeb();
List<SairBriAcciones> list=web.consultaAcciones("from SairBriAcciones a ");
for (SairBriAcciones acciones : list) {
System.out.println(acciones.getAccion());
}
}
Obtengo otra vez el error:
at com.sun.xml.internal.ws.fault.SOAP11Fault.getProtocolException(Unknown Source)
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.createException(Unknown Source)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(Unknown Source)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(Unknown Source)
at com.sun.xml.internal.ws.client.sei.SEIStub.invoke(Unknown Source)
at com.sun.proxy.$Proxy30.consultaAcciones(Unknown Source)
at ec.com.rgt.sair.brief.ServiciosWeb.consultaAcciones(ServiciosWeb.java:126)
at ec.com.rgt.sair.util.Prueba.main(Prueba.java:108)
Pero si agrego el join fetch a la relación sige saltanto la excepción, para que no salte tengo que agregar el join fetch a todas las relaciones(son 2 relaciones "SairBriEstatus y SairBriBrief" que tiene la tabla SairBriAcciones) pero estaria haciendo lo mismo que el atributo lazy="false".
Lo que deseo es que solo traiga la información de la tabla SairBriAcciones, pero no lo consigo tengo usar todos los join fetch en todas las relaciones o utilizar el atributo lazy="false" para conseguirlo, pero el rendimiento de mi aplicación baja muchisimo.
Entonces deseo saber si alguien a pasado por este problema y como lo soluciono.
Repito deseo lo la información de la tabla SairBriAcciones en el Cliente sin activar la carga de las demás tablas.
Saludos.
- Inicie sesión o regístrese para enviar comentarios
Ciclo de vida de la session
No logro ubicar en donde estás usando la Session de hibernate, pero, si me ha pasado, hay que tener mucho cuidado con el manejo de la session y más si usas la carga de los atributos de la entidad con el tipo Lazy, es posible que estés cerrando la session explicitamento o implicitamente y mucho después accedas a los atributos de la entidad.
DAO: public class DAO
DAO:
HibernateUtiles:
Solo pasa en el Cliente, pero si coloco el atributo lazy="false" o uso los join fetch en todas las relaciones, hay si consulta.
Pero solo quiero consultar una tabla como la clase prueba del primer proyecto.
El atributo lazy es lo que no
El atributo lazy es lo que no deseo usar, pero para que pueda consultar el query en el cliente me obliga a usarlo, xq si no no sale la consulta
Hibernate.initialize
Puedes usar:
Y luego ya puedes acceder a los atributos de tu objeto (que se supone representa la relación con otra tabla) o bien una colección.
O puedes investigar como usar la implementación de Hibernate Open session in view.
Ambas soluciones son unos parches horrendos, pero servirán para lo que necesitas.
Deja la consulta preparada
Si o si tendiras que utilizar los fetch de tu consulta, aunque parezca que estas haciendo lo mismo con el EAGER no es así, el EAGER implica que para TODAS las consultas que hagas se tendra que traer la información, SIMPRE, esto afectara a todas las consultas posteriores que hagan mencion a esa entidad.
No dejes que el cliente tire las consultas que el quiera a tu base de datos, en lugar de eso dejale las consultas preparadas, es decir dejale algo asi como getSairBriAcciones() y getSairBriAccionesFetchLaOtraTabla().
Usar la carga EAGER es la ultima opción que te daría debido a que es implicará que todas las consultas futuras iran por la información y tendrias un problema de rendimiento aun mayor al que mencionas ahora, si lo dejas como LAZY pues tienes espacio para maniobrar.
Por favor crea código respetando el estilo de java, es decir los metodos se nombranAsi(conCamelCase).
El que tu abras la transacción y la cierres de forma manual es una mala practica debido a que tu tienes que asumir esa responsabilidad, ¿que pasaria si necesitas usar exactamente el mismo metodo pero en otro metodo donde ya se abre y se cierra una transacción?, el @Transactional es tu amigo.