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:
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.
- Inicie sesión o regístrese para enviar comentarios
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
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:
<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:
<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:
[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.
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.
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:
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.
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 setterpublic 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:
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.
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.
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?
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).
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?
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.
Cuando ocurre una inyección?
Oie tengu una duda, ¿la inyección ocurre cuando levanta el servidor donde esta la aplicacion empieza a correr?.
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 esquemaxmlns:context="http://www.springframework.org/schema/context"
).lo probare
Ok ezamudio gracias por los consejos, voy hacer las pruebas a ver que sale.
Saludos.
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 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:
<!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>
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 unorg.springframework.orm.hibernate3.HibernateInterceptor
. Algo así:<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.
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:
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)
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.
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"
public void inicialize(){
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.
No doy como colocar EmptyInterceptor al SessionFacotry
Si me puedes ayudar a solucionar de como puedo colocarle el emptyInterceptor que implemente a un sessionFactory que inyecto a mi DAO, puesto que el sessionFactory es creado mediante un xml y se inyecta a todos los DAO para poder realizar cualquier CRUD, en los ejemplos o foros que he leido dice que se hace de la siguiente manera:
session = sessionFactory.openSession(interceptor);
pero openSession para la version que uso no recibe ningun parametro, debe ser la version antigua en donde lo implementaron segun el ejemplo, entonces no he encontrado la manera para poder colocarle el emtyInterceptor para mi SessionFactory inyectado
Si quieren ver mas detalles:
http://stackoverflow.com/questions/31417652/how-to-use-emptyinterceptor-...
Hola @HEILEEN , esta pregunta
Hola @HEILEEN , esta pregunta tiene casi 5 años de antigüedad, sería mejor que abrieras una pregunta nueva en vez de revivir esta.