CRUD con Spring MVC Portlet: Creando el proyecto y la acción de alta

20
51761

CRUD con Spring MVC Portlet: Creando el proyecto y la acción de alta

0. Índice de
contenidos.

1. Entorno

Este tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil Mac Book Pro 17″ (2,6 Ghz Intel Core
    i7, 8 GB
    DDR3)
  • Sistema Operativo: Mac OS X Snow Leopard 10.6.4
  • Spring MVC Portlet 3.0.4
  • Maven 2.2.1
  • Eclipse 3.6 (Helios) con M2Eclipse
  • Liferay 6.0.5

2. Introducción

En este tutorial se plantea como continuación del tutorial Ejemplo básico con Spring MVC Portlet. En este ocasión vamos a «enriquecer» el ejemplo realizando un portlet que nos permita realizar todas las operaciones CRUD contra una entidad. Para realizar las operaciones contra la base de datos me voy a apoyar en el proyecto que creamos en este tutorial Librería de acceso a datos con Spring y JPA

3. Creando el proyecto

Para crear el proyecto tenemos que seguir los mismos pasos que encontramos en este otro tutorial: Ejemplo básico con Spring MVC Portlet.

Lo único que va a cambiar (si queréis) es el nombre del artifactId, el cual vamos a llamar spring-mvc-crud-portlet.

4. Integrando el proyecto con el modelo

En el tutorial Librería de acceso a datos con Spring y JPA vimos como crear el modelo de datos que vamos a integrar ahora con nuestro proyecto.

Para integrar el modelo tenemos que añadir la dependencia en el pom.xml de nuestro proyecto, de esta forma:

  
    com.autentia  
    model-tutoriales  
    1.0-SNAPSHOT  
 

Ahora para poder utilizar esta dependencia en nuestro proyecto tenemos que editar el fichero WEB-INF/context/applicationContext.xml de nuestro proyecto para añadir la siguiente línea:

 <import resource="classpath:application-context-model-tutoriales.xml" />

Lo que estamos haciendo es incorporando la configuración de Spring de nuestro modelo en nuestro proyecto, por lo que ya sólo tendremos que declarar los DAOs para poder utilizarlos en nuestras clases.

5. Creando la vista de detalle de la persona

Ahora vamos a crear el fichero detalle_persona.jsp dentro de la carpeta jsp/spring-crud-mvc-portlet con el siguiente contenido:

<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet"%>  
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>  
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>  
  
  
  
  
  
      
  
  
  
      
    

Lo único que estamos haciendo es crear un formulario que nos va a servir para ver el detalle de una persona y poder hacer un alta, una modificación o eliminarla. Por ahora nos vamos a centrar en el alta de la persona.

Destacar que para crear el formulario estamos utilizando tags de spring, form se refiere a los componentes del formulario y spring:message nos permite internacionalizar los mensajes.

Para dar de alta el fichero de mensajes tenemos que añadir lo siguiente al fichero applicationContext.xml de nuestro proyecto:

  
          
          
              
                messages  
              
          
    

Con esta configuración tenemos que crear el fichero messages.properties dentro de la carpeta src/main/resources donde vamos a registrar todas las claves que utilicemos.

Declarando la propiedad «useCodeAsDefaultMessage» a «true» conseguimos que si Spring no encuentra la clave del mensaje pinte el texto de la clave, en vez de dar un error de despliegue.

Además hemos declarado en el atributo commandName del formulario que vamos a utilizar un bean llamado personaForm que representa los campos del formulario y permite a Spring mapear los campos del formulario, va a tener siguiente contenido:

package com.autentia.springcrudmvcportlet.forms;

import java.io.Serializable;

import com.autentia.tutoriales.model.Persona;

public class PersonaForm implements Serializable{
	
	private static final long serialVersionUID = -8425267500435219790L;
	private Long idPersona;
	private String nombre;
	private String apellidos;
	private String direccion;
	
	public PersonaForm(){}
	
	public PersonaForm(Persona persona){
		this.idPersona = persona.getIdPersona();
		this.nombre = persona.getNombre();
		this.apellidos = persona.getApellidos();
		this.direccion = persona.getDireccion();
	}
	
	public Long getIdPersona() {
		return idPersona;
	}
	public void setIdPersona(Long idPersona) {
		this.idPersona = idPersona;
	}
	public String getNombre() {
		return nombre;
	}
	public void setNombre(String nombre) {
		this.nombre = nombre;
	}
	public String getApellidos() {
		return apellidos;
	}
	public void setApellidos(String apellidos) {
		this.apellidos = apellidos;
	}
	public String getDireccion() {
		return direccion;
	}
	public void setDireccion(String direccion) {
		this.direccion = direccion;
	}
	
	public Persona getPersona(){
		final Persona persona = new Persona();
		persona.setIdPersona(idPersona);
		persona.setNombre(nombre);
		persona.setApellidos(apellidos);
		persona.setDireccion(direccion);
		return persona;
	}
}

6. Dar de alta personas

Para el alta de personas lo primero que vamos a hacer es crear el fichero listado_personas.jsp que va a ser la vista principal de nuestro portlet y desde donde vamos a llamar a la acción de añadir una persona, el contenido de este fichero es el siguiente:

<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet"%>  
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>  
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>  
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>  
  
  
  
  

${msgstatus}

Con la etiqueta portlet:renderURL estamos creando la URL necesaria para poder navegar a la ventana de detalle indicando que la acción que se quiere realizar es el alta.

Para visualizar este jsp como pantalla por por defecto de nuestro portlet lo único que tenemos que hacer indicarlo en la función marcada con @RequestMapping de esta manera:

@RequestMapping
protected final String defaultView(Model model) {
	return "listado_personas";
}

De esta forma lo primero que veremos al cargar el portlet será un enlace a la pantalla que nos permite dar de alta una persona, que no es otra que detalle_persona.jsp

Para redireccionar nuestro portlet a esta pantalla, primero tenemos que capturar la acción de render de esta forma:

@RequestMapping(params = "futuraAccion")
protected final String redirectURL(Model model,
	@RequestParam("futuraAccion") String futuraAccion) {

	if ("crud.alta".equals(futuraAccion)) {
		model.addAttribute("personaForm", new PersonaForm());
	} 
	model.addAttribute("futuraAccion", futuraAccion);
	return "detalle_persona";
}

Con la anotación @RequestMapping(params=»futuraAccion») lo que hacemos es declarar que este método de render se va ejecutar cuando en la URL se esté pasando un parámetro llamado «futuraAccion» independientemente de su valor.

Con la anotación @RequestParam(«futuraAccion») estamos recuperando el valor concreto de ese parámetro, el cual vamos a introducir en el modelo ya que nos va a servir para determinar que acción tiene que ejecutar el botón de acción del formulario de detalle de la persona.

Ahora tenemos que crear el método que se va encargar de realizar la persistencia de la persona, el cual se va invocar cuando exista el parámetro «ejecutarAccion» dentro de la URL y el parámetro «accion» coincida con el texto «crud.alta». Con lo que el método quedaría declarado de esta forma:

@Resource 
private PersonaDAO personaDAO;

@Resource
private ResourceBundleMessageSource messages;
---
@RequestMapping(params = {"javax.portlet.action=ejecutarAccion", "accion=crud.alta"})
protected final void insertarPersona(
	@ModelAttribute("personaForm") PersonaForm personaForm,
	@RequestParam("accion") String accion,
	@RequestParam(required=false, value="form.cancelar") String cancelar) {
		
	if (siUsuarioNoCancelaAccion(cancelar)) {
		personaDAO.insert(personaForm.getPersona());
		model.addAttribute("msgstatus", 
		messages.getMessage("status.ok", null, LocaleContextHolder.getLocale()));
	}
}

En caso de que el usuario pulse en el botón de cancelar, no hacemos nada, y si pulsa en el botón de alta, directamente introducimos esa información en la base de datos apoyándonos en nuestro proyecto model-tutoriales.

En un caso «real» seguramente no interese hacer directamente el acceso a la base de datos desde el controlador y habrá que crear una capa de servicios que se encargue de hacer este acceso, para poder reutilizar el código o crear una capa de REST, por ejemplo. El diseño dependerá del alcance de nuestro proyecto.

Como no decimos lo contrario, después de ejecutar este método el portlet mostrará la vista por defecto, mostrando un mensaje proceso realizado correctamente.

7. Vamos a probarlo

Para probar nuestro desarrollo, lo único que tenemos que ejecutar mvn clean package de nuestro proyecto, y desplegar nuestro .war en el servidor de Liferay. En caso de utilizar el plugin de Liferay, lo podemos hacer directamente ejecutando: mvn liferay:deploy, esta acción NO realiza previamente el package del proyecto.

Una vez desplegado correctamente, instanciamos el portlet en cualquier página de nuestro portal y deberíamos ver algo pareciado a esto:

Para acceder a la pantalla de alta, pinchamos en el enlace que se muestra, rellenamos los datos del formulario y pulsamos en el botón de alta, con lo que si vamos a la base de datos configurada podremos ver que efectivamente se ha insertado nuestro registro.


8. Conclusiones

Lo vamos a dejar aquí, en el próximo tutorial vamos a ver como mostrar un listado con todos las personas que vamos dando de alta, utilizando la librería displaytags que nos permite tener tablas paginadas de una forma muy cómoda. Además, en posteriores tutoriales, veremos como modificar y eliminar personas, como añadir validación a nuestro formulario y hacer pruebas unitarias.

Cualquier duda o sugerencia en la zona de comentarios.

Saludos.

20 COMENTARIOS

  1. Hola Ruben estoy siguiendo tus tutoriales pero en este tengo un problema y es en el IMPORT com.autentia.tutoriales.model.jpa.PersonaJPA; que defines en la clase PersonaForm…. esa clase no existe, podrias ayudarme con eso? te lo agradeceria mucho.

  2. Hola limaho,

    Esa clase ha sido creado en el tutorial \\\»Librería de acceso a datos con Spring y JPA\\\». Por lo tanto antes de continuar con este tendrás que hacer el que te indico.

    Muchas gracias por seguir mis tutoriales. Ante la más mínima duda, ya sabes donde están los comentarios.

    Saludos.

  3. Gracias Ruben por contestarme, ps mira el tuto anterior lo realice y me funciono perfectamente. Las clases que estan en ese proyecto son: GenericDAO, GenericDAOJPAImpl, Persona, PersonaDAO, PersonaDAOJPAImpl y PersonaDAOJPAImplTest… la que tu haces referencia en este proyecto PersonaJPA no esta.

  4. Hola Limaho,

    Antes de nada mil perdones, he visto lo de JPA y ya no he comprobado nada más.

    Efectivamente tienes razón en el proyecto anterior no existe la clase PersonaJPA se ha sustituido por Persona.

    Muchas gracias por tu comentario.

    Saludos.

  5. Hola Ruben que pena incomodar tanto. Cuando genero mi .WAR para subirlo a Liferay me bota un error referente al proyecto pasado Libreria de Datos me dice lo siguiente:

    22:23:36,755 ERROR [ContextLoader:220] Context initialization failed
    org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Failed to import bean definitions from URL location [classpath:application-context-modelo-datos.xml]
    Offending resource: ServletContext resource [/WEB-INF/context/applicationContext.xml]; nested exception is org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 13 in XML document…

    El proyecto pasado si me funciono haciendo las pruebas con el JUnit, pero en este no me funciona, y otra cosa, las ultimas lineas de codigo que especificas en este tuto van dentro del Controlador cierto? pero hay algo que no me queda claro y esta parte:

    if (siUsuarioNoCancelaAccion(cancelar)) {
    personaDAO.insert(personaForm.getPersona());
    model.addAttribute(«msgstatus»,
    messages.getMessage(«status.ok», null, LocaleContextHolder.getLocale()));
    }

    Gracias Por la ayuda y que pena estar molestandote…

  6. Hola limaho,

    Parece que tu error viene dado porque Spring no encuentra el import que se le está haciendo. Esto puede ser por dos motivos: uno el nombre del fichero xml que quieres importar (application-context-modelo-datos.xml) no es correcto o está en otra ruta (según lo tienes tú este fichero debería estar en la carpeta resources de la aplicación de librería de datos) o dos no es declarado la dependencia con el proyecto de librería común dentro del pom.xml del proyecto de portlet.

    Espero haber sido de ayuda.

    Saludos.

  7. Ruben noo me funciona, intente haciendo copy and paste del tuyo pero no he tenido exito. Puedes subir tu codigo o enviarlo a mi correo?, te lo agradeceria mucho.

    Gracias!

  8. el servidor tiene mucho que ver en un portlet con acceso a base de datos? utilizo el tomcat, sera que puede generar conflictos al tratar de conectar spring y jpa?

  9. Hola limaho,

    El servidor no tiene que ver. Lo que tienes que comprobar es el pom.xml del proyecto del portlet tenga la dependencia con el proyecto de librería de datos para que introduzca el jar.

    Otra cosa importante es que en la sección de librería de tu servidor tengas el .jar adecuado a la base de datos que estés utilizando.

    Si aún así sigues con problemas, mándame la traza de error.

    Saludos.

  10. Este es el primer error que muestra: Error creating bean with name \\\’messageSource\\\’ defined in ServletContext resource [/WEB-INF/context/applicationContext.xml]

    luego este: Error creating bean with name \\\’org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor#0\\\’: Cannot create inner bean \\\'(inner bean)\\\’ of type [org.springframework.transaction.interceptor.TransactionInterceptor] while setting bean property \\\’transactionInterceptor\\\’; nested exception is org.springframework.beans.factory.BeanCreationException

    Error creating bean with name \\\'(inner bean)\\\’: Cannot resolve reference to bean \\\’transactionManager\\\’ while setting bean property \\\’transactionManager\\\’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name \\\’transactionManager\\\’ defined in class path resource [application-context-model-tutoriales.xml]: Cannot resolve reference to bean \\\’entityManagerFactory\\\’ while setting bean property \\\’entityManagerFactory\\\’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name \\\’entityManagerFactory\\\’ defined in class path resource [application-context-model-tutoriales.xml]: Invocation of init method failed; nested exception is java.lang.RuntimeException: error trying to scan : file:/I:/Software/Programacion/Servidores/Liferay-Portal-Tomcat%206.0.5/tomcat-6.0.26/webapps/spring-crud-mvc-portlet/WEB-INF/lib/model-tutoriales-1.0-SNAPSHOT.jar
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)

  11. Buenas Ruben,

    Antes de nada agradecerte este y otros tutoriales sobre Spring MVC, llevo años visitando vuestra página y es realmente alentador ver a gente que dedica su tiempo a compartir conocimientos con el resto. Por desgracia estoy teniendo problemas por la configuración de los proyectos de los tutoriales de Spring MVC (localización y contenido de los ficheros). Por ejemplo, he tenido problemas ejecutando los tests de la librería de acceso a datos. Agradecería que me enviases el código de al menos este tutorial y el de la librería a la siguiente dirección: mi-mail-1 (arroba) hotmail.com

    Como habéis observado muchos tenemos problemas con la configuración de los proyectos, creo que en general estaría bien que en los tutoriales añadiéseis una captura de pantalla de la estructura de los ficheros en la IDE (Eclipse, Netbeans o la que sea) y/o adjuntáseis un enlace para descargar el código en un zip. Con eso vuestros tutoriales ya serían completamente perfectos. En cualquier caso se agradece el ingente esfuerzo que ya realizáis por compartir conocimiento.

    Muchas gracias.

  12. Hola Ruben, en el paso del enlace a la pantalla del alta me salta este error:

    Caused by: java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name \\\’command\\\’ available as request attribute

    cambiando:
    model.addAttribute(\\\»personaForm\\\», new PersonaForm());
    por:
    model.addAttribute(\\\»command\\\», new PersonaForm());

    funciona correctamente, asi que me imagino que el error esta en que hay que definir personaForm en algun sitio, pero donde?????

  13. Que desesperacion lo de crear un portlet y usar spring!!!
    ¿existe alguna incompatibilidad con glassfish? Estoy haciendo un proyecto spring mvc con acceso a datos con JPA/Hibernate. Lo estoy intentando probar sobre openportal portlet container con glassfish 3 y me da muchos problemas. No me funciona la inicialización de spring. Te paso mi email por si me pudieras mandar el ejemplo de este tutorial completo (incluyendo la librería de acceso a datos). guaose@gmail.com es mi mail. Gracias.

  14. Hola Rubén Aguilera Díaz-Heredero, lo felicito por su buen tutorial. Quisiera trabajar en su proyecto le dejo mi correo, muchas gracias.

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad