Hibernate + Spring

Hola amigos, tal vez para algunos de ud este título les parezca ya muy trillado, sin embargo para muchos como yo aun es difícil de entender cómo hacer posible esto, quizá algunos primero configuran el applicationContext.xml de Spring y después agregan la configuración del Hibérnate, otros tantos como yo comienzan configurando el hibernate.cfg.xml y después quieren configurar el Spring. Supongo que no debería de haber diferencia ya que el orden de los factores no altera el producto.

Les comento brevemente, con MyEclipse estoy desarrollando un proyecto al cual agregue Hibérnate 3.1, ya tiene el Mapeo, el POJO y el DAO de cada clase pero ahora quiero integrar Spring y mi problema comienza para saber que versión debo escoger ya que esta la 2.0, 2.5 y 3.0 (realmente no se que diferencia hay entre estas). Termino seleccionando 2.5 pero ahora no se que librerías debo escoger, de entrada tengo CoreLibraries pero selecciono AOP Libraries , persistence Core Libraries y claro Hibernate 3.1 Core Libraries ademas de Hibernate 3.1 Advanced Support Libraries. Finalemente el IDE me da la opcion de crear una referencia de Spring a la configuración donde esta SessionFactory y resulta lo siguiente:

        <bean id="sessionFactory"
                class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
                <property name="configLocation"
                        value="classpath:hibernate.cfg.xml">
                </property>
        </bean>

El archivo hibernate.cfg.xml solo tiene las referencias de los mapeos de mis tablas, tengo un archivo llamado hibernate.properties donde tengo la conexión a la bd y demás parámetros.

Hasta ahí cuando levando mi proyecto, el log no me informa que el proyecto ya tiene Spring.

Si alguno de ud me pudiera decir que me hace falta, he intentado agregar beans de org.springframework.web.servlet.DispatcherServlet pero me marca diferentes errores, no sé que estoy haciendo mal.

Bueno espero su ayuda. Gracias y saludos.

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de JaimeItlzc

Blog @Ezamudio

Blog @Ezamudio
Recientemente actualicé una aplicación que utiliza Spring 2.5, para que utilice Spring 3.0 y quiero compartir algunos detalles de esta experiencia. No quiero decir que esta es la manera de actualizar pero pues espero le sirva a alguien y si hay mejores maneras también las puedan poner aquí como comentarios.
Primero que nada, quiero mencionar que los proyectos que actualicé no utilizan Maven ni Ivy ni nada por el estilo; de ahí la complejidad y la mención en mi blog; si fuera con Maven sería más sencillo y no habría tanto problema.
El primer cambio que noté con Spring 3.0 es que ya no existe un JAR grande (como era spring-2.5.6.jar por ejemplo) sino que ahora hay varios JARs, uno por módulo, y la nomenclatura se complicó un poco, por ejemplo org.springframework.core-3.0.0.RELEASE.jar.
Autor Ezamudio

Imagen de AlexSnake

Un avance...

Hola amigos, he bajado el codigo del proyecto javamexico y eso me ayudo a encontrar la configuración que debia hacer para hacer esta conexión y la comparto para aquellos que al igual que yo no habian podido hacer esto.
En el archivo applicationContext.xml debe tener los siguientes beans:

 <bean id="dataSource"  class= "org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="org.gjt.mm.mysql.Driver"/>
                <property name="url"             value="jdbc:mysql://ip:puerto/bd"/>
                <property name="username"        value="usuario"/>
                <property name="password"        value="contraseña"/>
                <property name="initialSize"     value="15"/>
                <property name="maxActive"       value="30"/>
                <property name="maxIdle"         value="10"/>
        </bean>
       
        <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
                <property name="configLocation" value="classpath:hibernate.cfg.xml"/>
                <property name="hibernateProperties" value="classpath:hibernate.properties"/>
        </bean>

    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
        <property name="dataSource" ref="dataSource" />
    </bean>

        <bean id="hibInterceptor" class="org.springframework.orm.hibernate3.HibernateInterceptor">
                <property name="sessionFactory" ref="sessionFactory" />
        </bean>

y en el web.xml queda asi:

        <context-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>
                        /WEB-INF/applicationContext.xml
                </param-value>
        </context-param>
       
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

Esto ya hace posible que levante el server, pero lametablemente y para mi mala suerte me sale el siguiente error en cada POJO de las tablas:

WARN - No configuration found. Configuring ehcache from ehcache-failsafe.xml found in the classpath: jar:file:/C:/Workspace/.metadata/.me_tcat/webapps/APPLICATION/WEB-INF/lib/ehcache-1.1.jar!/ehcache-failsafe.xml
[19-08-10 09:17:06]ERROR- CGLIB Enhancement failed: persistence.Usuario
java.lang.NoSuchMethodError: org.objectweb.asm.ClassVisitor.visit(IILjava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V

Quiza alguien de ud sabe el por que de este error. Espero sus comentarios al respecto, saludos.

Imagen de JaimeItlzc

Ty Spamming

Thanks you Spamming. Que onda AlexSnake podrias darte una checada a estos comentarios de un post pasado.
Ayuda con Class-Path dentro de Jar

Saludos.

Imagen de AlexSnake

No entiendo la referencia

Pues no se que tenga que ver el post que refieres con el problema que tengo, sin embaro he buscado en la red el por que del problema y me di cuenta que debo agregar hibernate.cglib.use_reflection_optimizer=false en el archivo hibernate.properties sin embargo, eso no soluciona el problema solo lo cambia al siguiente error:

[19-08-10 12:20:50]ERROR- CGLIB Enhancement failed: persistence.Usuario
java.lang.IllegalAccessError
        at net.sf.cglib.core.ClassEmitter.setTarget(ClassEmitter.java:45)
        at org.hibernate.proxy.CGLIBLazyInitializer.getProxyFactory(CGLIBLazyInitializer.java:116)
        at org.hibernate.proxy.CGLIBProxyFactory.postInstantiate(CGLIBProxyFactory.java:41)
        at org.springframework.orm.hibernate3.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:732)
        at org.springframework.orm.hibernate3.AbstractSessionFactoryBean.afterPropertiesSet(AbstractSessionFactoryBean.java:211)
        at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:45)
        at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3827)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
[19-08-10 12:20:50]WARN - could not create proxy factory for:persistence.Usuario
org.hibernate.HibernateException: CGLIB Enhancement failed: persistence.Usuario

Y por lo que he visto en otros foros, el problema radica en los jars por la incompatibilidad que pudiera haber en estos, la cuestion es de que ya baje los mas recientes y sigue el error, tal vez me hace falta configurar algo.. no lo se, espero que alguien pueda orientarme.

Imagen de AlexSnake

Mas dudas

Hola a todos los que visiten este foro, espero encontrar en alguno de ud una respuesta que me ayude a entender que es lo que está pasando. Hace unas semanas intente integrar Spring y Hibernate, apoyado con el IDE myEclipse, sin embargo y como se pueden dar cuenta pues aun no jala :S.

Bueno pues resulta que soy algo insistente, no me quedo tranquilo hasta que las cosas se resuelvan, he dejado pasar un tiempo para despejar la mente y no he logrado mucho más que lo siguiente:

Encontré un ejemplo simple en este post el cual corre sin problemas, pero me di cuenta que el ejemplo no tiene interfaces ni implementaciones además tampoco tiene una clase para manejar el transacción y el factory, como el que te crea myEclipse (HibernateSessionFactory), lo único que ocupa es una variable  private HibernateTemplate hibernateTemplate; y un setter

   
public void setSessionFactory(SessionFactory sessionFactory) {
        this.hibernateTemplate = new HibernateTemplate(sessionFactory);
    }

para poder guardar un objeto  hibernateTemplate.save(usuario);
Sin embargo esta solución no se me hace satisfactoria, por lo que integre la clase HibernateSessionFactory y eso me provoca el siguiente error:

GRAVE: Excepción enviando evento inicializado de contexto a instancia de escuchador de clase org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'miSessionFactory' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Invocation of init method failed; nested exception is org.hibernate.HibernateException: Unable to instantiate default tuplizer [org.hibernate.tuple.entity.PojoEntityTuplizer]

Caused by: org.hibernate.HibernateException: Unable to instantiate default tuplizer [org.hibernate.tuple.entity.PojoEntityTuplizer]

Caused by: java.lang.NoSuchMethodError: org.objectweb.asm.ClassVisitor.visit(IILjava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V

Espero encontrar una solución con ayuda de ud y poder resolver este problema que seguramente le podrá servir a alguien en un futuro.

Imagen de ezamudio

conflicto de versiones

Me suena a conflicto de versiones con algunas de las librerías que estás usando. Y eso no está nada fácil de resolver. Revisa las librerías que venían incluidas con la versión de Hibernate que estás usando; deben coincidir la mayor y menor.

Imagen de AlexSnake

Problema resuelto.

Hola ezamudio gracias por tus comentarios, efectivamente los Jars no eran compatibles, tuve que buscar y descargar uno a uno, suponía que el IDE de MyEclipse al integrar "Hibernate & Spring capabilities" traía los Jars con la compatibilidad correcta entre ambos, pero veo que no.

Solo una última pregunta, tengo tres proyectos. Dos de ellos se empezaron a desarrollar con mvc y el otro con simples jsp's, cuando hago la inyección de dependencias en los primeros me trae la referencia correctamente pero en el otro viene null, estuve revisando con putos de ruptura en los getter y setter y cuando entra al setter se muestra la referencia, pero cuando la ocupo en otro método es como si nunca se hubiera inyectado, ¿crees que ocurra esto porque me falta integrar algún mvc?

Imagen de ezamudio

instancias

No tengo suficiente info para contestarte bien pero pues me suena a que se usan distintas instancias de tus objetos.

Es decir, en el punto de ruptura del setter ves que se asigna un valor a tu variable de instancia... pero de qué instancia? Si se le pasan valores a un objeto y luego usas otro igual (otra instancia de la misma clase) que no recibió esos valores (no se le inyectaron sus dependencias), se puede arrojar NPE (o la ves en null porque nunca se inyectaron dependencias a ese objeto en particular).

Imagen de AlexSnake

Mas detalles

Estas en lo cierto, el problema es que me arroja un NPE. Tal vez tengo que revisar bien que estoy haciendo ya que tengo un bean de cada DAO y un bean por cada modulo del proyecto donde hago referencia a los beans de los DAO que ocupo. En las clases de los módulos estan las instancias (sin inicializar) de las claeses DAO con sus getters y setter. Cuando el tomcat esta levantando entra a setter de cada instancia los cuales reciben los valores de la inyección, sin embargo cuando quiero ocupar esa instancia que se supone ya esta con el valor del objeto, esta null, ¿raro, vdd?

Imagen de ezamudio

dos casos

Solamente he visto pasas eso en dos casos: Uno, que estás usando un objeto distinto del que manejó el inyector de dependencias. Dos, que estás usando el objeto correcto pero quieres invocar métodos en alguna de sus dependencias antes que ocurra la inyección.

Imagen de AlexSnake

Cuando ocurre una inyección?

Oie tengu una duda, ¿la inyección ocurre cuando levanta el servidor donde esta la aplicacion empieza a correr?.

Imagen de ezamudio

depende

Estás usando Spring verdad? Si configuraste en tu web.xml el ContextLoaderListener, entonces el applicationContext se crea cuando levanta la aplicación en el contenedor. Y en ese momento es cuando puede ocurrir una NPE, si tienes por ejemplo definido un bean A que tiene referencia a un bean B y en el código de algún setter del bean A haces una llamada a un método del bean B; si ese setter (digamos A.setC) se ejecuta antes del A.setB entonces ocurre NPE porque todavía el bean A no tiene la referencia al bean B.

Para esos casos puedes tener un método con la anotación @PostConstruct; ese método se ejecuta después de que se le ponen todas las propiedades a tu bean, de modo que ya puedes hacer referencia a cualquiera. Para cuando se ejecuta ese método, ya se inyectaron todas las referencias que hayas definido en el application context y todas las que estén anotadas con @Autowired o @Resource. Solamente para que funcione @PostConstruct (y @Autowired y @Resource) debes poner en tu application context el <context:annotation-config /> al principio (y definir el XML de tu application context usando esquemas y no el DTD; incluir el esquema xmlns:context="http://www.springframework.org/schema/context").

Imagen de AlexSnake

lo probare

Ok ezamudio gracias por los consejos, voy hacer las pruebas a ver que sale.
Saludos.

Imagen de AlexSnake

Sigue null

Que tal ezamudio, he probado la anotación que me indicaste pero sigo teniendo null y me di cuenta que no es en la referencia de un DAO, sino en el DAO como tal.
Me di cuenta por que mando a llamar directamente la clase DAO y cuando hago un debug el getSession() viene null. Busque en la web sobre este error y algunos declaran una variable HibernateTemplate y generan su getter and setter pero cuando hago eso en mi clase me sale el error Cannot override the final method from HibernateDaoSupport Incluso he probado también con la anotación @Autowired, pero sigue igual... Alguna otra sugerencia??? Gracias.
Mira este es mi codigo:

import java.util.List;

import javax.annotation.PostConstruct;

import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public class BitacoraHibernateDAO extends HibernateDaoSupport implements BitacoraDAO {

        @PostConstruct
        public List findAll() {
                try {
                        Session sesion = getSession();
                        String queryString = "from Bitacora";
                        Query queryObject = sesion.createQuery(queryString);
                        return queryObject.list();
                } catch (RuntimeException re) {
                        throw re;
                }
        }
}

Y esta es mi configuración:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
 "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>        
        <bean id="dataSource"  class= "org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="org.gjt.mm.mysql.Driver"/>
                <property name="url"             value="jdbc:mysql://localhost/syscom"/>
                <property name="username"        value="user"/>
                <property name="password"        value="pwd"/>
                <property name="initialSize"     value="15"/>
                <property name="maxActive"       value="30"/>
                <property name="maxIdle"         value="10"/>
        </bean>
       
        <bean id="sessionFactory"
                class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mappingResources">
            <list>
                <value>daoHbmPojo/Bitacora.hbm.xml</value>
            </list>
        </property>
    <property name="hibernateProperties">
            <props>
                    <prop key="hibernate.hbm2ddl.auto">update</prop>
                    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                    <prop key="hibernate.query.substitutions">true 'T', false 'F'</prop>
                    <prop key="hibernate.show_sql">false</prop>
                    <prop key="hibernate.c3p0.minPoolSize">5</prop>
                    <prop key="hibernate.c3p0.maxPoolSize">20</prop>
                    <prop key="hibernate.c3p0.timeout">600</prop>
                    <prop key="hibernate.c3p0.max_statement">50</prop>
                    <prop key="hibernate.c3p0.testConnectionOnCheckout">false</prop>
            </props>
    </property>

        </bean>
       
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
   
    <bean id="BitacoraDAO" class="daoHbmPojo.BitacoraHibernateDAO">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
   
</beans>

Imagen de ezamudio

Revisa javaMexico 2.0

Ahí verás cómo definimos los DAOs de Hibernate con Spring. Son POJOs que implementan una interfaz y tienen una propiedad SessionFactory. En la config lo que hacemos es definir el DAO, conectarle la sessionFactory (o le puedes poner @Autowired) y luego envolver ese DAO en un org.springframework.aop.framework.ProxyFactoryBean. Ah y necesitas un org.springframework.orm.hibernate3.HibernateInterceptor. Algo así:

<bean id="hibInterceptor" class="org.springframework.orm.hibernate3.HibernateInterceptor">
        <property name="sessionFactory" ref="hibFactory" />
</bean>

<bean id="hibFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan" value="org.javamexico.entity" />
        <property name="hibernateProperties"><value>
                hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
                hibernate.cache.provider_class=org.hibernate.cache.OSCacheProvider
        </value></property>
</bean>

<bean id="miDAO" class="org.springframework.aop.framework.ProxyFactoryBean">
  <property name="frozen" value="true" />
  <property name="proxyInterfaces" value="mi.proyecto.InterfazParaMiDao" />
  <property name="target">
    <bean class="mi.proyecto.MiDaoImpl">
      <property name="sessionFactory" ref="hibFactory" />
    </bean>
  </property>
  <property name="interceptorNames"><list>
    <value>hibInterceptor</value>
  </list></property>
</bean>

Para poder envolver el DAO en el ProxyFactoryBean es necesario que implemente una interfaz. Tus referencias al DAO deberían via la interfaz, no la clase que implementa, porque el objeto que vas a conectar en realidad será el proxy.

Imagen de AlexSnake

Por fin..

Ok parece que ya quedo esto, solo que me quedo una duda y como soy muy curioso estube confgurando el bean de BitacoraDao tal cual me lo pusiste, de hecho ya habia bajado el proyecto de JavaMexico para guiarme en ciertas cosas, pero para ser sincero aun hay muchas cosas que no entiendo.

La duda es por que cuando puse las referencias asi como me inicaste, me marco el siguiente errror:

Cannot create inner bean 'daoHbmPojo.BitacoraDAO#1517a05' of type ['daoHbmPojo.BitacoraDAO] while setting bean property 'target'; nested exception is org.springframework.beans.factory.BeanCreationException: Could not instantiate bean class [daoHbmPojo.UsuarioDao]: Specified class is an interface
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:480)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
        at java.security.AccessController.doPrivileged(Native Method)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
Imagen de ezamudio

está claro

Specified class is an interface

Creo que te confundiste. El proxy debe envolver un bean (lo que le pongas en la propiedad target), y le indicas qué interfaz o interfaces implementa ese bean. Seguramente cuando definiste el bean que es el target del proxy, le pusiste el nombre de la interfaz en vez del nombre de la clase.

La explicación larga es que tienes que definir una interfaz con los métodos de tu DAO y luego tu DAO implementa esa interfaz. El DAO es un POJO o sea cualquier objeto en Java, no necesita heredad de ninguna clase en especial, pero tiene que declarar que implementa la interfaz. La idea de tener el proxy es que se haga pasar por tu DAO, por lo cual debe envolverlo y debe implementar la interfaz del DAO (por eso necesitas definir una interfaz). Para qué? pues si te fijas, el proxy tiene un interceptor (puede tener varios); eso es un concepto de AOP (Aspect Oriented Programming). La idea es que si por ejemplo llamas un método de tu DAO que abre una sesión de hibernate, obtiene objetos y los devuelve, ya no tengas que estar lidiando con bloques de try-catch-finally para manejar la sesión; tu código ni siquiera invoca el session.close(). El proxy se encarga de cerrar la sesión, usando el interceptor.

Suponte que tienes una referencia a tu interfaz del DAO en un componente. Invocas el método cargar() que trae unos datos. Cuando invocas el método, como realmente tienes una referencia al proxy, entonces el proxy recibe la llamada al método cargar() (el cual implementó en tiempo de ejecución, es como una clase dinámicamente creada) y dentro del método cargar() del proxy, se llama el método cargar() de su target, o sea el DAO real. Pero antes de llamarlo, crea una sesión de Hibernate; por eso en tu método debes obtener la sesión con el sessionFactory.getCurrentSession(). Terminando la ejecución de tu método cargar(), el proxy cierra esa sesión, pase lo que pase, no importa si ocurrió una excepción o si el método finalizó de manera normal. Y al final el proxy devuelve el resultado de tu implementación de cargar().

En realidad es todavía un poco más complicado, porque quien hace el trabajo de crear y cerrar la sesión es el interceptor de Hibernate que tiene el proxy. Pero ya no te quiero enredar más con rollos internos de AOP por ahora. Simplemente revisa uno de los métodos de los DAOs de javaMexico 2.0 y verás que nunca se cierra la sesión por código, porque quien la va a cerrar siempre será el proxy. Esto complica un poquito la configuración pero simplifica mucho el código.

Imagen de AlexSnake

Master....

Wow creo que la experiencia y conocimiento que tienes es ilimitado, Efectivamente había puesto el nombre de la interfaz, solo lo cambie y como arte de magia corrió sin bronca!!! Cuando no salen las cosas pienso que no debí enfocarme en esto, pero cuando funcionan correctamente estoy contento de haber incursionado en la programación.
Gracias por los tips Enrique, un saludo!!!

XD

jajaja, ya se! lol,
este brother es dios XD

Pues no funciona.

Pues yo estoy configurando un proyecto con Spring+hibernate y puse lo que dijiste
@javax.annotation.PostConstruct
al principio del Web.xml
y el esquema xmlns:context="http://www.springframework.org/schema/context"

@javax.annotation.PostConstruct
        public void inicialize(){
<web-app        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                        xmlns="http://java.sun.com/xml/ns/javaee"
                        xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
                        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee <a href="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
" title="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
">http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
</a>                    xmlns:context="http://www.springframework.org/schema/context"
                        id="WebApp_ID"
                        version="2.5">
 
  <context:annotation-config />

pero sigue sin funcionar el mendigo postConstruct.