Hibernate 4.2.2 y Spring 4.0.0 (SGCE2013)
Como ya confirmo la persona que nos quito dolores de cabeza de como trabajar con distintas base de datos y nos dio otros dolores de cabeza entonces me di a la tarea de terminar lo que les presentare.
Hablare específicamente de una solución con Hibernate y Spring, no me iré a lo general si no al trabajo cotidiano de todo programador. Como trabajar he integrar una solución a los problemas que se nos presentan a diario.
Hay que tomar en cuenta de que por lo regular se usa Hibernate para las aplicaciones nuevas y/o con bases de datos bien estructuradas.
Antes de entrar de lleno a esto es importante que se echen un clavado en algunos temas si no estan relacionados con ellos: inyeccion de dependencias, Spring, Hibernate, Jpa, y los que se acumulen en el camino.
Estoy armando una aplicación de prototipo y me di a la tarea de usar Hibernate 4 y la nueva versión de Spring Frameworks 4, pero con una arquitectura que me ayudara a realizar las operaciones de la forma mas simple posible.
Con algunas dificultades hice funcionar una arquitectura de cuatro capas persistencia, servicio, controlador y la vista, se puede decir que es MVC y un pilon ya que el acceso a las entities es triangular, tomo en cuenta esta arquitectura por que la aplicación pasara el rango de lo normal así como trabajo en grupo.
En la parte de persistencia la base de todo son las entidades con anotaciones JPA como se muestra en el ejemplo.
Todas las entidades se manejan de la misma forma.
En esta misma capa de persistencia van los DAOs aquí inicia lo interesante de todo, se necesita una clase abstracta donde se realicen todas las operaciones básicas Insert, Delete, Load, Etc.
La clase cuando se extienda recibirá entre sus parámetros La entidad y el tipo de clave primaria de la entidad En los comentarios se describen el contenido de la clase
public abstract class AbstractDao<E, I extends Serializable> {
/*Se recibe la clase entidad*/
private Class<E> entityClass;
/*Constructor*/
protected AbstractDao(Class<E> entityClass) {
this.entityClass = entityClass;
}
/* Se inyecta la sessionFactory de hibernate a la clase */
@Autowired
private SessionFactory sessionFactory;
/*Se obtiene la sesión actual de hibernate*/
public Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
/*Dos ejemplos de métodos comúnmente usados*/
public E buscarPorId(I id) {
return getCurrentSession().get(entityClass, id);
}
/*Actualiza o almacena los datos de una entidad.*/
public void salvarOGuardar(E e) {
getCurrentSession().saveOrUpdate(e);
getCurrentSession().flush();
}
/*...... Mas métodos */
}
De esta clase extenderán todos los DAOs para realizar las operaciones básicas de todas las entidades.Ejemplo de DAO de la entidad Usuario
/*Anotación que indica que será Bean para acceso a base de datos no usen otras anotaciones para la creación de beans @component por ejemplo por que no inyecta de forma adecuada la transacción.*/
@Repository
/*La clase extiende de la clase base*/
public class UserDao extends AbstractDao<Usuario, String>{
/*Se llama el constructor de la clase padre pasándole la entidad con la que trabajara*/
protected UserDao(){
super(Usuario.class);
}
/*Se mandan a llamar métodos de la clase padre quien realizara las operaciones.*/
public void salvar(Usuario usuario){
salvarOGuardar(usuario);
}
public Usuario buscar(String login){
return buscarPorId(login);
}
}
La capa de la persistencia estará formada con la estructura anterior, No puse interfaces para no hacer mas largo el ejemplo pero si recomiendo que se usen en algunos casos, se los dejo a su imaginación :).
En la capa de servicio me ayudara a validar algunas cuestiones, poner lógica de negocio y realizar las transacciones, se formara de la siguiente forma:
/*Anotación que indica que será un Bean servicio .*/
@Service
/*Anotación que indica que todos los métodos de la clase serán de lectura.*/
@Transactional(readOnly = true)
public class UsuarioService {
/*Se inyecta el Dao o DAOs a utilizar*/
@Autowired
private UserDao userDao;
public Usuario buscar(String login) {
return userDao.buscar(login);
}
/*Este método es particular por que hace que se abra un transacción, que nos permitirá da un rollback en caso de tener errores. */
@Transactional(readOnly = false)
public void salvar(Usuario usuario) {
userDao.salvar(usuario);
}
/*... mas métodos*/
}
La idea principal es que desde esta capa se manden a llamar más de un DAO y otros servicios.
La capa de controlador es la que se comunicara con la vista, procesara información del cliente y regresara la respuesta solicitada.
El esta capa podemos definir llamados a URL (@RequestMapping) en la que la respuesta puede ser a una pagina jsp o un servicio Ajax(@ResponseBody) via Get,POST u otra forma de llamado,
También podemos cachar errores con @ExceptionHandler.
/*Anotación que indica que será un controlador*/
@Controller
public class WebUsuario {
/*Inyectamos el Servicio a utilizar*/
@Resource
private UsuarioService usuarioService;
/*Responde a una pagina por Get o post.*/
@RequestMapping(value = "/crea_usuario", method = {RequestMethod.POST,RequestMethod.GET})
public String guardar( Model model, Usuario usuario) {
/*Lógica del controlador*/
Usuario existing = usuarioService.buscar(usuario.getLogin());
if (existing != null) {
model.addAttribute("estatus", "existe");
/*Retorna resultado a un jsp */
return "usuario";
}
usuarioService.salvar(usuario);
model.addAttribute("estatus", "exito");
/*Retorna resultado a un jsp */
return "usuario";
}
/*Si ocurre un error de NullPointerException entonces es lazada la respuesta del error */
@ExceptionHandler(NullPointerException.class)
public ModelAndView handleException (WebRequest req,NullPointerException ex) {
ModelAndView mav = new ModelAndView(new RedirectView(req.getContextPath()+"/errorAccesoPagina.action?error=1") ); /*El error 1 indica el mensaje que al cliente*/
return mav;
}
/*. Mas operaciones.*/
}
La parte de la vista un simple jsp en donde realizara las operaciones el cliente. Solo pondré lo esencial del jsp , el resto se los dejo a su imaginación. Por lo regular para que se vea bien uso Jquery y su gran gama de plugins, bootstrap es buena opción si esperas que tu aplicación se vea bien en navegadores normales y móviles.
<form:label path="login">Usuario</form:label><form:input path="login" />
<form:label path="nombre">Nombre</form:label><form:input path="nombre" />
<button type="submit" id="save">guardar</button>
</form:form>
Hasta aquí lo que tiene que ver con las clases, como vimos solo usamos hibernate y JPA en la capa de persistencia el resto de la aplicación es el uso de Spring y sus diferentes comportamientos.
Veamos la parte de configuración de los xml(También se puede hacer vía Clases pero eso es otra historia) que es donde entra lo bueno y la Unión de Spring con Hibernate de forma adecuada.
Primero el web.xml
<!--Se levanta el contexto de Spring(applicationContext.xml) e hibernate(hibernate.xml) -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml, /WEB-INF/hibernate.xml</param-value>
</context-param>
<!--Se leventa el contexto de Web -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--Un filtro que no se esperaba pero que nos ayudara a que no nos ocurra el error (No Session found for current thread), la clase se definirá mas a detalle -->
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>mx.com.solucionesTiMexico.cloudVerne.service.CustomHibernateSessionFilter</filter-class>
<init-param>
<param-name>sessionFactoryBeanName</param-name>
<param-value>sessionFactory</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
Este es el código del filtro que nos permite que las sesiones lleguen de forma correcta a la clase abstracta. Y que no mueran antes de nacer.
public class CustomHibernateSessionFilter extends OpenSessionInViewFilter {
protected Session getSession(SessionFactory sessionFactory)throws DataAccessResourceFailureException {
Session session = super.openSession(sessionFactory);
/*Nos aseguramos de que nuestras consultas no sean obsoletas*/
session.setFlushMode(FlushMode.AUTO);
return session;
}
Veamos el applicationContext que es donde se definiran loas beans que usamos en las diferentes capas.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
<a href="http://www.springframework.org/schema/beans/spring-beans.xsd
" title="http://www.springframework.org/schema/beans/spring-beans.xsd
">http://www.springframework.org/schema/beans/spring-beans.xsd
</a> <a href="http://www.springframework.org/schema/mvc
" title="http://www.springframework.org/schema/mvc
">http://www.springframework.org/schema/mvc
</a> <a href="http://www.springframework.org/schema/mvc/spring-mvc.xsd
" title="http://www.springframework.org/schema/mvc/spring-mvc.xsd
">http://www.springframework.org/schema/mvc/spring-mvc.xsd
</a> <a href="http://www.springframework.org/schema/context
" title="http://www.springframework.org/schema/context
">http://www.springframework.org/schema/context
</a> http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Buscamos en el o los paquetes donde organuizamos nuestras clases anotaciones que serán Beans de acuerdo al tipo de anotación que posean -->
<context:component-scan base-package="com.algo.ejemplo"/>
<!-- Se activa el reconocimiento de anotaciones como las @Controller ver documentación de Spring-->
<mvc:annotation-driven />
<--- declaramos los Beans de vista en este caso para que un controlador se una a un jps, si se dieron cuenta en el método del controlador retornamos una cadena que representara un usuario.jsp en la ruta /WEB-INF/jsp/ -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
El archivo de hibernate.xml, aquí se definirá la conexión a la base de datos y la forma en la que se conectara con Hibernate.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
<a href="http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
" title="http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
">http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
</a> <a href="http://www.springframework.org/schema/tx
" title="http://www.springframework.org/schema/tx
">http://www.springframework.org/schema/tx
</a> http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<!--Un dadasource de una conexión a MySql-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://Localhost:3306/base" />
<property name="username" value="usuario" />
<property name="password" value="contraseña" />
</bean>
<!-- Sesión de Hibernate indicándole a Spring que será con la versión 4-->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="com.algo.ejemplo.model" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<!--Se activan las transacciones y le indicamos a la aplicación de que tome el Bean transactionManager -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
</beans>
Hasta aquí puedo decir que si toman como guía la configuración de Spring con Hibernate le funcionara de forma correcta.
Algo importante hibernate 4 usa entre sus dependencias classmate.jar sugiero que tomen la versión 0.8.0 las versiones anteriores a esta causan problemas al intentar crear las entities.
Voy a poner el proyecto en github para que puedan echarle el un ojo.
Espero aparte de participar en lo de SGCE apoye a algunos.
Saludos.
- Inicie sesión o regístrese para enviar comentarios
Sorry lo puse donde no era..
Saludos..
Lista completa de libreiras
Seria bueno que enlistes todas las librerias que ocupaste, por que como bien mencionas una que no sea compatible con las demas van a causar problemas y dolores de cabeza.
Saludos.
GANADOR
Este post fue uno de los 3 ganadores, felicidades, tienes pase para SGCE2013.
y la liga al github?
Finalmente si lo subiste a Github?
No lo subi..
Lo tengo en la maquina de mi trabajo, y como aki me tienen bloqueado el servicio no puedo hacerlo, y aparte se me rompio mi maquina en mi casa no he hecho nada.. pero lo subo y te aviso.
Saludos.
bloquean github?
En tu trabajo bloquean github? WTF????????????
asi es.... mi estimado...
Puedes ver pero para subir por alguna razon no se puede.
Es una empresa muy grande dedicada a la venta de chacharas... :). pero me toco la mala suerte de no tener internet libre.. Es mas no digas nada por que bloquean javamexico jajajajaja.
Saludos.
Internet Bloqueado
Chale, yo si de plano tengo bloqueado todo en mi chamba de hecho nomas tengo correo (espero no verme tan patético). En fin, en mis ratos libres trato de programar algo de lo que leo o veo (bueno, eso cuando podía compartir internet de mi cel a la compu ). Recuerdo que debido a esta publicación me puse a ver que había cambiado en Hibernate y Spring, pero como que no encontré mayor diferencia a como anteriormente lo había utilizado.
En fin sin mas rollo, les dejo el proyecto que hice en ese tiempo que leí esta publicación.
https://github.com/javadabadoo/hibernate-spring