Está en la página 1de 216

Autor

Julio Bellón Aguilera (jbafiddle@hotmail.com)

E ste curso pretende demostrar paso a paso, la tarea de desarrollar una aplicación corporativa, desde su
inicio hasta su puesta en producción. Obviamos todo lo relativo a MÉTRICA y a la configuración de
servidores, diseño, desarrollos UML, etcétera, centrándonos en lo que realmente importa y para lo que está
concebido el presente documento. Cómo se programa una aplicación para una empresa , qué tecnologías
uso, por qué y cómo empiezo!

Supongo que muchos os habréis hecho ésta pregunta, una vez que hayáis terminado la reunión con un
cliente en la que os ha consultado acerca de la posibilidad de desarrollarle un programa para su negocio.
Obviamente, sabemos que sabemos programar y nos dedicamos a eso, por tanto, el cómo se programa una
aplicación para una empresa, que no sea malinterpretado, por favor; Se refiere más al momento en que por
fin, después de todo el proceso (calentamiento de cabeza, mejor dicho para algunos), acerca del estudio de
implantación del software, estudio de los casos de uso, estudio de la lógica de negocio, y un largo etcétera....
SI ES QUE LO HACES (si estás en una empresa de desarrollo de software medianamente aceptable, lo harás, o
tendrás que regirte por el que los compañeros de ése equipo hayan hecho), repito; Momento en el que por
fin te sientas delante de tu máquina y es cuando pasa lo siguiente:

¡¡¡¡¡¡¿Por dónde empiezo?!!!!!!


Vamos a ir muy al grano, puesto que el curso ha sido diseñado para usarlo como guía de aprendizaje. Y no
nos vamos a entretener en conceptos como integración a través de Test o cosas de ésas que hacen
realmente tedioso y duplicable el trabajo. No vamos a ser tan puristas. Vamos a trabajar Paso a Paso,
deteniéndonos en lo esencial y profundizando en lo que realmente haya que profundizar. Sin más ni más.

Dicho esto, espero que os ayude y os lo paséis tan bien realizándolo como yo lo he pasado escribiéndolo.

1
Tabla de Contenidos
1) Software requerido

2) Lo que vamos a aprender

3) La arquitectura de Spring MVC

4) La aplicación que vamos a desarrollar

5) Diseñando la Base de Datos

6) Dando de alta el proyecto en eclipse

7) Configurando las dependencias del proyecto

8) Creando la Base de Datos

9) Implementando Spring Security

10) Autenticando contra la Base de Datos

11) Desarrollando la capa de Persistencia

12) Desarrollando la capa de Aplicación

13) Desarrollando la capa de Presentación

En ésta primera parte nos vamos a centrar en el desarrollo de la capa de persistencia y gran
parte de las capas de aplicación y presentación, puesto que vamos a dejar totalmente
finalizada la parte del programa que gestiona los usuarios del mismo, sus roles y contraseñas.

2
Software requerido

E
n el momento de confeccionar el presente documento, se han utilizado las siguientes herramientas.
En realidad no tienes por qué usarlas, pero sí que sería recomendable hacerlo puesto que las capturas
de las imágenes y los pasos de éste proyecto se han realizado con ellas, lo cual, te facilitará mucho
más el trabajo. Las mismas te las listo a continuación:

 jdk1.7.0_45
 Apache Tomcat Server v7.0.41
 Eclipse Kepler Java EE IDE Developers con el plugin de Spring IDE STS Versión 3.4.0

(Bueno, y algunos más, pero que no hacen falta para éste tutorial... jejeje). Yo soy uno de ésos que lo
queremos tener TODO en nuestro IDE! Aunque no lo utilicemos nunca!! ;)

 MySQL server versión 5.5.8 Comunity Server (GPL).

Vamos a dar por supuesto que posees unos conocimientos aunque sean muy básicos sobre programación
orientada a objetos, concretamente con Java, que te has iniciado en la programación de aplicaciones web y
que sabes instalarte el jdk, el eclipse, el servidor tomcat y el servidor mysql y configurártelos. Esto es lo
mínimo que te voy a pedir, aparte de las ganas y la paciencia necesarias para aguantarme y hacer éste
tutorial.
No obstante, también te voy a dejar los enlaces para que te descargues el software mencionado, porque
como todos sabemos, "ésto", va cada vez más rápido y de aquí a que yo termine y/o que tú llegues a leértelo
y a desarrollar entero éste documento, lo más seguro es que éstas "tecnologías" se hayan quedado un poco
obsoletas o pasadas de moda. Lo cual no significa que no se pueda desarrollar con las que existan en ése
momento, sino que, probablemente tendrás que cambiar y reconfigurar un montón de cosas y como
resultado de éstos cambios, los pasos no se parecerán ni por el forro a los que vamos a ver aquí, por lo que
te vuelvo a recomendar; Aunque haga falta ejecutar de nuevo "El viejo Windows 7 x64", en una máquina
virtual, o sacar del trastero "el ordenador del abuelo...", BÁJATELOS e INSTÁLATELOS, será lo más fácil...

3
Lo que vamos a aprender

A
continuación te voy a mostrar una lista de las partes del maravilloso mundo de la programación que
se van a cubrir en éste tutorial. Esto no significa que nos vayamos a convertir en unos expertos en
estos temas, sino, simplemente, que son facetas o perfiles relacionados con el programador que es
capaz de programar (¡qué bien suena!), una aplicación como la que vamos a construir, que se supone, que al
menos, conoce o sabe usar...

 Inversión de Control (IoC).


 El framework (o marco/perspectiva de trabajo) , Spring MVC v3.2.6.
 El framework Spring Security v3.2.0.
 Acceso a datos mediante JDBC e Hibernate.
 El patrón DAO.
 Manejo de transacciones.
 Encriptación mediante el algoritmo SHA1.
 Configuración personalizada de una app-web. el WEB.xml.
 Manejo de Maven. Artefactos, dependencias, el POM.xml.
 Desarrollo de Bases de datos. MySQL 5.5
 Validación de datos con anotaciones y con clases Validators.
 Validación de formularios normal y restringida.
 Manejo de solicitudes GET y POST.
 Paso de parámetros por vistas al modelo.
 Creación de atributos de modelo.
 Configuración de Spring con anotaciones.
 Inyección de dependencias. JSR-303.
 Configuración de Hibernate con anotaciones.
 Rotura del antipatrón DAO.
 Creación e implementación de interfaces.
 Uso de generics en java.
 Ventanas modales de confirmación con Javascript.
 Reglas de navegación.
 Securización de accesos a páginas web.

4
La arquitectura de Spring MVC
Os dejo una imagen que de la arquitectura de éste framework. Después os cuento cosas:

Básicamente, lo que necesitamos saber es que este framework está basado en el famoso patrón de diseño,
Modelo-Vista-Controlador (MVC), el cual, separa la lógica de la aplicación en tres capas que tienen por
nombre... ¿a que no lo adivináis? ;)

Ahora, os explico brevemente cómo funciona


el tema:

E l dispatcher-servlet, (servlet
despachador) actúa como el controlador
frontal entre la aplicación spring y sus
clientes. Intercepta todos los "requests"
(solicitudes) y consulta al handler mapping
(mapa de manejeo...), qué controlador tiene
que ser invocado para atender a ése
"request". A continuación, el Controller se
encarga de procesar los requests llamando a
las otras clases de servicio o de la lógica de
negocio. La salida se adjunta a objetos de
modelo que se envían a una vista
determinada. El encargado de resolver y
construir ésa vista es el View Resolver, que las
localiza físicamente, a través de su nombre lógico. Por último, las View o vistas, son las páginas en formato
JSP, HTML, etc...

5
La aplicación que vamos a desarrollar
V amos a desarrollar una aplicación para una franquicia de guarderías. La gestión de los alumnos se
realizará desde la guardería "madre", de una forma centralizada. Los demás centros se conectarán al
centro principal donde estará nuestra aplicación instalada y ejecutándose, para la administración y gestión
de los datos referentes a los alumnos. A que mola! Pues ahora empezamos de principio a fin, paso a paso.

Vamos a pasar muy por encima de lo que concierne a la venta de los equipos en caso necesario, obviando el
hecho, de que les tendrás que aconsejar y vender al menos, el equipo que haga de servidor sino también, a
lo mejor, los terminales desde los que se van a conectar. Les asesorarás acerca de las soluciones de
conectividad que les ofrecerá su proveedor de servicios de telecomunicaciones, en cuanto a esto, la cosa se
suele hacer, configurándoles una red privada virtual (VPN) a través de sus líneas ADSL, para las que les has
aconsejado que contraten direcciones IP fijas, etcétera... (a mí me parece una de las soluciones más baratas,
sobre todo cuando se trata de pymes muy pequeñitas, que a lo mejor, lo que han hecho ha sido abrir su
segunda o tercera sucursal, vamos, al nivel en el que tú, como "lobo solitario" te vas a mover...)

Pero no me enrollo! el caso es que ahora toca empezar a pensar en la aplicación. ¿Qué nos han pedido?

¿Qué quieren exactamente? Un sistema de gestión de la información centralizado.

¿Sobre qué información? Sobre la información relativa a sus alumnos (Los pitufos;).

¿Y exactamente, qué? Pues ahí va!

 Una ficha que contenga los datos personales de cada alumno, y padre, madre o tutor del mismo, el
curso al que va, las actividades que realiza, sus calificaciones, y su fecha de ejecución; (Comer,
lavarse las manos, deposiciones, psicomotricidad, estimúsica, etc...).
 Las relaciones entre los alumnos, curso y el centro al que pertenecen.
 Los usuarios y sus roles en la aplicación y su relación con los profesores.
 Las actividades que imparten los profesores y el centro al que pertenecen.

Bueno, con esto es suficiente, a modo de ejemplo, pienso yo, ¿no? es evidente, que, a la larga, si lo haces
bien con tu cliente, te pida más funcionalidades para la aplicación, claro está, no sé si lo veis, que una de las
cosas que le podríais ofrecer, sería la posibilidad de ofrecer una especie de interfaz de comunicación, no sólo
entre los centros, sino también "con las familias", programable a través de android o de objective-c,
etcétera... pero eso, es otra historia.

También espero que perdonéis la simplicidad con la que he querido desarrollar ésta bd; es verdad, que no he
completado todos los datos, no se ha tenido en cuenta la parte de los proveedores de la guardería (comida,
inventario, material de oficina), tampoco se ha tenido en cuenta lo referente a aspectos como el de
facturación, control de horarios, control del personal laboral, etc... Recuerdo que es a modo de ejemplo.
Manos a la obra cógete papel y lápiz y empieza a diseñar las tablas de la base de datos...

6
Diseñando la base de datos

A continuación, os propongo la imagen con la estructura de la BD. Después, os descifro el jeroglífico:

A través de 14 tablas, vamos a intentar ofrecer unas interfaces que nos cubran lo más básico. El primer
campo de cada tabla es el índice, que será auto-numérico y va precedido por las letra id (de índice). En
alunas tablas comunes, veréis dos llaves; Esto significa que los dos campos hacen de clave primaria de la
tabla. Pasa en las tablas que realizan las relaciones de tipo muchos a muchos, como por ejemplo la relación
que puede haber entre profesores (que imparten muchas actividades) y actividades (que pueden ser
impartidas por más de un profesor) o entre alumnos (que realizan muchas actividades) y actividades (que
son realizadas por muchos alumnos).

Un detalle del que quería dejar constancia es que aquí no hablamos de actividades en plan asignaturas. No,
los peques todavía no hacen eso...

Los peques, los pitufos, hacen cosas como comerse todo hacer caca durante el día una o varias veces,
hacérsela encima o no, etc. En definitiva son actividades, que se califican en base a si se han realizado o no y
cómo se han realizado. Lo que nos da una idea de la política de tratamiento de datos que tenemos que
afrontar o abstraer a la hora del desarrollo.

También, habréis advertido que hay 3 tablas (profesores, alumnos y pmt), que en realidad, si abstraemos un
poco, son personas, por lo que a la hora de implementar nuestro modelo entidad-relación podríamos usar la
herencia y tratarla con las anotaciones @DiscriminatorColumn, @DiscriminatorValue y
@Inheritance(strategy=InheritanceType.SINGLE_TABLE) y así ganaríamos en rendimiento y eficiencia en
nuestra aplicación. Pero repito, estamos a nivel de ejemplo y no os voy a complicar mucho la cosa.

7
L as relaciones, se pueden actualizar en cascada y borrar en cascada. Así, dejamos la gestión de la bd semi-automatizada en parte, con lo que nos
ahorraremos muchas consultas sql a la hora de programar nuestro código de gestión (Aunque sabemos también que puede ser peligroso...). Ahora, sólo
falta transcribir el código para la creación de las tablas y sus relaciones. Lo vamos a guardar en un archivo al que llamaremos "Pitufos.sql", que por ahora
situaremos en el escritorio de nuestra máquina. más tarde cuando hayamos creado el proyecto, lo moveremos dentro del mismo, en su carpeta
correspondiente.
'Pitufos.sql':
CREATE DATABASE pitufosbd;
GRANT ALL ON pitufosbd.* TO pitufosappuser@'%' IDENTIFIED BY 'pitufosappuserpss';
GRANT ALL ON pitufosbd.* TO pitufosappuser@localhost IDENTIFIED BY 'pitufosappuserpss';
GRANT ALL ON pitufosbd.* TO pitufosappuser@127.0.0.1 IDENTIFIED BY 'pitufosappuserpss';
USE pitufosbd;
CREATE TABLE usuarios (
idusuario INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
usuario varchar(50),
password varchar(100),
enabled tinyint(1)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE roles (
idrol INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
idusuario INTEGER NOT NULL,
rol varchar(100),
KEY FK_US_RO (idusuario),
CONSTRAINT FK_US_RO FOREIGN KEY (idusuario) REFERENCES usuarios (idusuario) ON DELETE CASCADE ON UPDATE CASCADE
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE pmt (
idpmt INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
nombre varchar(100),
apellidos varchar(150)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE centros (
idcentro INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
nombre varchar(150)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE cursos (
idcurso INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
curso varchar(250)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE observaciones (
idobservacion INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,

8
observacion varchar(250)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE calificaciones (
idcalificacion INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
calificacion varchar(150)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE fechas (
idfecha INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
fecha date
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE profesores (
idprofesor INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
nombre varchar(100),
apellidos varchar(150),
idcentro INTEGER NOT NULL,
idcurso INTEGER NOT NULL,
KEY FK_PR_CE (idcentro),
CONSTRAINT FK_PR_CE FOREIGN KEY (idcentro) REFERENCES centros (idcentro) ON DELETE CASCADE ON UPDATE CASCADE,
KEY FK_PR_CU (idcurso),
CONSTRAINT FK_PR_CU FOREIGN KEY (idcurso) REFERENCES cursos (idcurso) ON DELETE CASCADE ON UPDATE CASCADE
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE roles_profesores (
idrol INTEGER NOT NULL,
idprofesor INTEGER NOT NULL,
PRIMARY KEY(idrol, idprofesor),
CONSTRAINT FK_RO_RO FOREIGN KEY (idrol) REFERENCES roles (idrol) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT FK_PR_RO FOREIGN KEY (idprofesor) REFERENCES profesores (idprofesor) ON DELETE CASCADE ON UPDATE CASCADE
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE actividades (
idactividad INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
denominacion varchar(150),
idcalificacion INTEGER NOT NULL,
idfechaejecucion INTEGER NOT NULL,
idobservacion INTEGER NOT NULL,
KEY FK_CA_AC (idcalificacion),
CONSTRAINT FK_CA_AC FOREIGN KEY (idcalificacion) REFERENCES calificaciones (idcalificacion) ON DELETE CASCADE ON UPDATE
CASCADE,
KEY FK_FE_AC (idfechaejecucion),
CONSTRAINT FK_FE_AC FOREIGN KEY (idfechaejecucion) REFERENCES fechas (idfecha) ON DELETE CASCADE ON UPDATE CASCADE,
KEY FK_OB_AC (idobservacion),
CONSTRAINT FK_OB_AC FOREIGN KEY (idobservacion) REFERENCES observaciones (idobservacion) ON DELETE CASCADE ON UPDATE CASCADE

9
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE actividades_profesores (
idactividad INTEGER NOT NULL,
idprofesor INTEGER NOT NULL,
PRIMARY KEY(idactividad, idprofesor),
CONSTRAINT FK_AC_PR FOREIGN KEY (idactividad) REFERENCES actividades (idactividad) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT FK_PR_AC FOREIGN KEY (idprofesor) REFERENCES profesores (idprofesor) ON DELETE CASCADE ON UPDATE CASCADE
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE alumnos (
idalumno INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
nombre varchar(100),
apellidos varchar(150),
idcentro INTEGER NOT NULL,
idcurso INTEGER NOT NULL,
idpmt INTEGER NOT NULL,
KEY FK_AL_CE (idcentro),
CONSTRAINT FK_AL_CE FOREIGN KEY (idcentro) REFERENCES centros (idcentro) ON DELETE CASCADE ON UPDATE CASCADE,
KEY FK_AL_CU (idcurso),
CONSTRAINT FK_AL_CU FOREIGN KEY (idcurso) REFERENCES cursos (idcurso) ON DELETE CASCADE ON UPDATE CASCADE,
KEY FK_AL_PMT (idpmt),
CONSTRAINT FK_AL_PMT FOREIGN KEY (idpmt) REFERENCES pmt (idpmt) ON DELETE CASCADE ON UPDATE CASCADE
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE actividades_alumnos (
idactividad INTEGER NOT NULL,
idalumno INTEGER NOT NULL,
PRIMARY KEY(idactividad, idalumno),
CONSTRAINT FK_AC_AL FOREIGN KEY (idactividad) REFERENCES actividades (idactividad) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT FK_AL_AC FOREIGN KEY (idalumno) REFERENCES alumnos (idalumno) ON DELETE CASCADE ON UPDATE CASCADE
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

Como habréis podido observar, el usuario de la aplicación, o sea, el usuario que se va a conectar a la base de datos, no coincide con el usuario que va a usar
la aplicación Esos, los crearemos nosotros más adelante. Esto tiene un motivo, pero el mismo, es subjetivo. Me explico; Yo, creo que para éste tipo de
implementaciones es mejor separar los usuarios del servidor de bases de datos de los usuarios que van a usar la base de datos. Así, si se produce cualquier
error o manipulación maliciosa, siempre vamos a tener el control de la bd a través del propio usuario del servidor. No obstante, repito, es una opinión
subjetiva.
Una vez terminado el diseño de la base de datos, va siendo hora de dar de alta nuestro proyecto en eclipse, y mover nuestro archivo sql dentro del mismo,
por lo que pudiera pasar. Después nos conectaremos al servidor mysql y lo ejecutaremos.

10
Dando de alta el proyecto en eclipse
Clicamos en new/Spring Proyect:

11
A continuación, nos saldrá un
cuadro de diálogo como el que
vemos a la derecha:

1 En el campo "Nombre de
proyecto" escribimos pitufosapp

2 En el apartado "Templates"
(plantillas) seleccionamos Spring
MVC Project

3 clicamos en el botón Siguiente

Al pulsar en Siguiente nos saldrá el siguiente diálogo de confirmación, al que decimos que si:

Lo que hemos hecho, ha sido darle permiso a eclipse para que se descargue la plantilla correspondiente a
nuestro proyecto, desde la que se generará la estructura de directorios y la configuración inicial.

12
A continuación, especificamos com.guarderias.pitufos como paquete principal y pulsamos finish

Ahora observad cómo se ha configurado el proyecto primeramente: No toqueis nada! Todavía no hemos
terminado de configurarlo!!

13
Podemos observar tres partes claramente diferenciadas:

1) La estructura de archivos del proyecto. Como podéis ver, en la carpeta del proyecto nos aparece el
símbolo avisándonos de un "pequeño problema" en la parte 2.
2) la pestaña "marcadores" de eclipse. Allí se nos avisará de todos los problemas, alarmas, errores y
marcadores que vayan sucediendo o que establezcamos durante el desarrollo de nuestra aplicación.
Ahora, lo que nos está diciendo es que la versión configurada para el proyecto del jdk que la plantilla
descargó y auto-configuró, no la encuentra, pero no os preocupéis por eso ahora.
3) En ésta parte, aparecen los servidores de aplicaciones que nosotros tengamos instalados y
configurados en nuestro equipo y en eclipse. Más adelante pararemos sobre éste apartado.

Ahora clicamos con el botón derecho del ratón sobre la carpeta del proyecto, pitufosapp en la parte 1 y en el
menú contextual, seleccionamos maven/update project.

14
Nos aparecerá el siguiente formulario:

Ahora, nos aseguraremos que nuestro proyecto pitufosapp está seleccionado y que también está
seleccionada la casilla Force Updates of Snapshots/Releases. Pulsamos Aceptar.
Maven se encargará de descargar todas las dependencias necesarias para nuestro proyecto y actualizar el
mismo. Pero ahora, vamos a echarle un pequeño vistazo a nuestra estructura de directorios.

Podemos observar 5 partes:


1) La clase controlador HomeController.java que se encargará
de procesar los "request" procedentes de la página/vista,
home.jsp
2) El archivo de configuración para el loggin de nuestra
aplicación. Más adelante hablaremos sobre esto.
3) Los archivos de configuración de spring. Los archivos
servlet-context.xml y root-context.xml
4) La página de bienvenida y punto de entrada a nuestra
aplicación. La vista, home.jsp
5) El archivo descriptor de despliegue web.xml de nuestra
aplicación.
6) El
archivo de configuración de nuestro proyecto Maven
pom.xml

15
Vamos a resolver el problema de la versión del jdk:

Nos vamos a la carpeta del proyecto y clicamos con el botón derecho del ratón, seleccionando del menú
contextual emergente propiedades. Así, accedemos al cuadro de diálogo de propiedades del proyecto:

16
En el cuadro de diálogo de propiedades del proyecto, seleccionamos el apartado Project Facets y nos
desplazamos hasta la casilla de verificación de Java. Allí podremos observar que en la columna
correspondiente a la versión de java utilizada, se encuentra marcada la versión 1.6. Vamos a cambiar eso.
Seleccionamos la flecha que hay a la derecha de la versión de Java (3) y del menú desplegado clicamos
sobre la versión 1.7. Clicamos nuevamente en Aplicar. Eclipse y Maven, actualizarán las bibliotecas del
proyecto. Pulsamos Aceptar y listo! nuestra warning desapareció! Os dejo las capturas de los pasos
y la del resultado, para que no nos perdamos:

Pasos a realizar para suprimir la warning referente a la versión de java utilizada.

17
Et voilá! ya nos aparece todo como debería ser!

Configurando las dependencias del


proyecto

V amos a echarle un vistazo a nuestro archivo de configuración de proyecto Maven. El archivo pom.xml.
Para ello, lo abrimos, con su editor personalizado. Navegamos por la estructura de directorios del
proyecto hasta el archivo, clic con botón derecho, abrir con, Maven POM Editor:

18
Ahora se nos abre el editor propio del archivo, donde podemos configurar diversos aspectos de nuestro
proyecto como el artefacto, el nombre de proyecto, si tiene módulos, las dependencias, etcétera... Nosotros
nos vamos a centrar por ahora, en el apartado Properties (propiedades).

Declaramos varias propiedades para facilitar las dependencias y sus relaciones. También, porque a la hora de
actualizar una dependencia, con sólo cambiar el valor de la propiedad que definamos, se cambia
automáticamente en todos los valores de dependencias asociadas a ésa propiedad. Nos simplificamos la
vida. Así de simple. No os preguntéis más. Ahora, lo que vamos a hacer es quitar una propiedad que no nos
va a servir para nada, dejaremos otras, modificaremos otras y estableceremos otra. Al lío:

1º Borramos la propiedad java-version:

Clic en la propiedad (2)y clic en remove. Después, guardamos el archivo.

19
2º Cambiamos el valor de la propiedad org.springframework-version: Ahora mismo está establecida en el
valor 3.1.1.RELEASE, pero nosotros en realidad vamos a utilizar la 3.2.6.RELEASE. Así que la seleccionamos
haciendo doble clic y en el cuadro de diálogo que aparece, cambiamos el valor existente por el nuestro.

Al guardar el archivo, maven actualiza automáticamente todas las dependencias del proyecto.

3º Cambiamos el valor de la propiedad org.slf4j-version: Ahora mismo está establecida en el valor 1.6.6,
pero nosotros en realidad vamos a utilizar la 1.7.5. Así que la seleccionamos haciendo doble clic y en el
cuadro de diálogo que aparece, cambiamos el valor existente por el nuestro.

Al guardar el archivo, de nuevo, maven actualiza automáticamente todas las dependencias del proyecto.

20
4º Creamos el valor de la propiedad org.springframework.security-version: Vamos a utilizar la versión del
spring framework security 3.2.0.RELEASE, la cual nos va a cuadrar muy bien con la versión del spring
framework con la que vamos a trabajar y con el proveedor del servlet, que vamos a usar; Apache gerónimo.

¿El por qué? después de la captura con los pasos.

Guardamos, y... No se actualiza nada, porque todavía no hemos definido las dependencias correspondientes
a org.springframework.security. Pero eso lo haremos más adelante. Ahora, la parrafada del por qué de la
versión del security y del por qué de usar apache gerónimo.

Realmente, podríamos usar tanto la api javax.servlet, servlet-api, en su versión 2.5 como la
org.apache.geronimo.specs, geronimo-servlet_3.0_spec , en su versión 1.0. Aquí he escogido ésta, por una
cuestión de compatibilidad con el servidor de aplicaciones apache, ya que el servlet que usamos, pertenece
también a apache. Digamos que es más bien, una cuestión de "semántica". En cuanto a por qué usamos la
versión 3.2.0.RELEASE con la versión 3.2.6.RELEASE en lo referente a los frameworks de spring security y
spring respectivamente, se debe a que el uso de la versión anterior de spring security y/o la versión posterior
del spring framework, provocan errores de compilación, "bugs" que por el momento siguen abiertos en los
foros de soporte técnico de spring, así que nos vamos a ahorrar éstos problemas, usando las dos versiones
que por ahora cuadran sin dar problemas.

También existen opciones de configuración de repositorios y dependencias más generalistas, que incluyen
toda una serie de dependencias pre-configuradas y totalmente compatibles.

21
Por ejemplo, existen perfiles de dependencias para aplicaciones java, en repositorios que puedes configurar
tú. Voy a nombrar uno de ellos para que el tema no se quede sin ver, puesto que puede ser interesante.

<repositories>
<repository>
<id>java.net2</id>
<name>Repository hosting the jee6 artifacts</name>
<url>http://download.java.net/maven/2</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>

Como podéis ver, se define un repositorio en la url especificada y una dependencia para javaee-web-api,
versión 6.0. Bueno. Si configurásemos éstas líneas en nuestro pom.xml, el resultado sería que se nos
incluirían las siguientes bibliotecas: Servlet 3.0, EJB Lite 3.1, JPA 2.0, JSP 2.2, EL 1.2, JSTL 1.2, JSF 2.0, JTA 1.1,
JSR-45, JSR-250. Cómodo, ¿no? Pero nosotros nos lo vamos a currar más. Las vamos a definir "a mano", así
podremos elegir las que queramos, acorde a nuestras propias preferencias a la hora de programar.

Veamos cómo queda por ahora nuestro archivo pom.xml y después pasamos a configurar las dependencias
que vamos a usar para nuestra aplicación:

22
Ahora, os paso la lista de dependencias que vamos a usar, en una tabla. Podréis comprobar que muchas de
ellas ya se encuentran en el listado existente que está clicando en la pestaña Dependencies de nuestro
archivo pom.xml. Esto es debido a la configuración inicial de la plantilla que se usó para dar de alta el
proyecto y a las posteriores modificaciones que hemos realizado en el archivo pom.xml, nosotros mismos.

Las dependencias existentes van en verde, las que vamos a hacer a modo de ejemplo en azul y las que
tendréis que introducir vosotros en negro. Por último, la que vamos a sustituir (opcionalmente), en naranja.

Group Id Artifact Id Version Scope


org.aspectj aspectjrt ${org.aspectj-version} compile
org.slf4j slf4j-api ${org.slf4j-version} compile
org.slf4j jcl-over-slf4j ${org.slf4j-version} runtime
org.slf4j slf4j-log4j12 ${org.slf4j-version} runtime
log4j log4j 1.2.15 runtime
javax.inject javax.inject 1 compile
javax.servlet servlet-api 2.5 provided
javax.servlet.jsp jsp-api 2.1 provided
javax.servlet jstl 1.2 compile
junit junit 4.7 test
taglibs standard 1.1.2 compile
org.springframework spring-core ${org.springframework-version} compile
org.springframework spring-webmvc ${org.springframework-version} compile
org.springframework spring-context ${org.springframework-version} compile
javax.validation validation-api 1.1.0.Final compile
javax.transaction jta 1.1 compile
mysql mysql-connector-java 5.1.28 compile
org.hibernate hibernate-validator 4.3.1.Final compile
org.hibernate.java-persistence jpa-api 2.0-cr-1 compile
org.hibernate hibernate-entitymanager 3.6.10.Final compile
org.hibernate hibernate-core 3.6.10.Final compile
org.springframework spring-orm ${org.springframework-version} compile
org.springframework.security spring-security-web ${org.springframework.security-version} compile
org.springframework.security spring-security-config ${org.springframework.security-version} compile
org.springframework.security spring-security-core ${org.springframework.security-version} compile

Sustituiremos la dependencia marcada en naranja, que quedará configurada de la siguiente forma:

Group Id Artifact Id Version Scope


org.apache.geronimo.specs geronimo-servlet_3.0_spec 1.0 provided

Manos a la obra! primero sustituiremos la dependencia referente al servlet-api y después nos encargaremos
de introducir la referente a las taglibs standard, introduciendo sus propiedades directamente. Después y por
último, introduciremos la de spring-core, cuya propiedad version, apunta a la propiedad que definimos
anteriormente en la pestaña overview.

23
1º Sustituímos la dependencia
servlet-api: Primero Clicamos la
dependencia (1), después al
botón properties... (2), y en el
cuadro de diálogo que nos
aparece, introducimos los
campos correspondientes a los
pasos (3 al 5). Pulsamos el
botón Aceptar (6) y guardamos
el archivo. Maven nos actualizará
automáticamente todas las
librerías necesarias.

2º Introducimos la dependencia
taglibs standard: Clicamos el
botón add... (1), después en el
cuadro de diálogo que nos
aparece, introducimos los campos
correspondientes a los pasos del
(2 al 5). Pulsamos Aceptar (6) y
guardamos el archivo. Maven nos
actualizará automáticamente todo
lo necesario, otra vez.

24
3º Introducimos la dependencia
spring-core: Clicamos el botón
add... (1), después en el cuadro
de diálogo que nos aparece,
introducimos los campos
correspondientes a los pasos del
(2 al 5). Pulsamos Aceptar (6) y
guardamos el archivo. Maven
nos actualizará automáticamente
todo lo necesario, otra vez. Diréis
que guardo muchas veces el
archivo, lo sé, pero es por
seguridad y de paso, al
actualizarse las dependencias
compruebo si se genera algún
error de compilación o
incompatibilidad...

Ahora solamente queda que vosotros configuréis las restantes dependencias mostradas en la tabla de
dependencias anterior. Las mismas, repito, van en negro. La operación, como podéis comprobar es sencilla y
repetitiva. Aunque también podéis hacerlo desde el código fuente directamente del archivo, pulsando en la
pestaña pom.xml y editando manualmente las dependencias. Aunque yo no lo recomiendo, puesto que a
veces, sin causa aparente, da errores en el descriptor del archivo. Una simple coma, o una espacio sin tener
en cuenta en un "corta-pega" y se fue todo al traste... Una pequeña visión del xml sería así:

........
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
........ </dependencies>

25
Os dejo una captura de cómo deberían quedar configuradas nuestras dependencias. Si notáis que están
"ordenaditas" mis dependencias y las vuestras no, no os preocupéis, es que yo las he ordenado desde la
pestaña pom.xml:

26
Vamos a terminar de configurar nuestro proyecto. Vamos a situar el
archivo de creación de la base de datos en su directorio
correspondiente dentro de la estructura de directorios del proyecto.
Para ello, haremos clic con el botón derecho del ratón en el directorio
raíz del proyecto y seleccionaremos nueva/carpeta. En el cuadro de
diálogo que aparece, especificamos la carpeta padre, que va a ser la
carpeta raíz del proyecto y en el apartado nombre rellenamos el
campo con db. Clicamos Aceptar. Después, navegamos por nuestro
sistema de archivos de nuestro ordenador hasta donde tengamos
almacenado el archivo pitufos.sql que creamos anteriormente y lo
movemos hasta la ubicación de nuestra carpeta db dentro de nuestro
proyecto. Después de realizar todos los pasos, la estructura de
directorios del proyecto debería de quedaros así:

Ejecutamos la aplicación para


ver que todo funciona
correctamente. Aunque
realmente, lo único que está
configurado para ser
desplegado en nuestro
servidor tomcat, va a ser la
configuración inicial de la
plantilla de nuestro proyecto.
Más adelante, iremos
añadiendo cosas.

Primeramente hacemos clic


con botón derecho del ratón
en la carpeta raíz del
proyecto, seleccionamos
Ejecutar como / Run on
Server.

27
En el cuadro de diálogo que aparece,
aseguraros que tenéis marcada la opción
"Choose an existing server", en caso de que
tengáis configurados algún que otro servidor
de aplicaciones en vuestro espacio de trabajo.
Si no, tendréis que crear uno. Seleccionad un
servidor, clicad en Siguiente.

En el siguiente vuadro de diálogo, nos


aseguramos que nuestra aplicación esté
configurada dentro de los recursos de nuestro
servidor de aplicaciones. Podemos administrar
si queremos que haya otras aplicaciones o no,
dentro del servidor. Clicamos en Finalizar.

28
Por último, la salida de vuestra aplicación, será como la siguiente, en caso que tengáis configurado eclipse
para que ejecute las aplicaciones web dentro del propio navegador que ofrece el IDE. En caso contrario, os
ejecutará el navegador web que tengáis instalado en vuestro equipo.

Podemos ver tres zonas claramente diferenciadas:

 La zona 1, que contiene nuestro navegador incrustado en eclipse, donde visualizamos nuestra
aplicación. La vista (La página web), y su Controlador (La clase java que la controla), han cargado
unos elementos por defecto, provenientes de la plantilla que usamos para dar de alta nuestro
proyecto. Después los cambiaremos y personalizaremos a nuestro gusto... (el del cliente, perdón);
 La zona 2, donde se encuentra nuestro servidor de aplicaciones, con el recurso desplegado (nuestra
aplicación), y el estado de ejecución.
 La zona 3, las pestañas de Marcadores, Progreso y Consola, donde tenemos habilitada en primera
capa la Consola, para ver los mensajes que nos manda nuestra aplicación y nuestro servidor de
aplicaciones. Vamos a pararnos un momento en éste punto.

29
Después de arrancar el servidor, cuyos mensajes vemos en rojo, el mismo, nos ofrece la salida del log
referente al contexto de aplicación. (1).
Vemos cómo spring nos ha inicializado primero, el archivo root-context.xml y en segundo lugar el archivo
servlet-context.xml. (2 y 3).
Por último nos manda un loggin de los elementos que cargó la plantilla por defecto de nuestro proyecto (4).

Por ahora es suficiente con saber esto. Seguimos con nuestro proyecto:

Más respuestas... después. Cerramos la página del navegador (1), y paramos el servidor Tomcat (2), puesto
que es recomendable parar el servidor para evitar que se recargue automáticamente mientras seguimos
desarrollando el proyecto. Puede generarnos fallos.

30
Creando la Base de Datos
V amos a crear nuestra base de datos enteramente desde eclipse. Lo primero que vamos a hacer, es
configurar un nuevo perfil de conexión a nuestro servidor MySQL 5.5 Local. Lo llamaremos así. Debemos
abrir primero, si no la tenéis abierta ya, la ventana Data Source Explorer. Aquí, los pasos:

Clicamos en Ventana/Mostrar vista/ Otras...

En el cuadro de diálogo emergente, navegamos hasta el nodo


Data Management y seleccionamos la vista Data Source
Explorer. Clicamos en Aceptar y listo.

31
Ahora nos aparecerá la ventana/perspectiva que hemos habilitado. Desde allí, vamos a crear nuestro perfil.
Clicamos en Database Connection y en el menú contextual que sale, seleccionamos New...

En el diálogo, para crear el perfil de conexión a la base de datos, seleccionaremos el tipo de perfil de la lista,
que será MySQL, escribiendo después en los campos Name MySQL 5.5 Local y una descripción (Opcional). El
nombre que le pongáis también es opcional. Pulsamos Siguiente.

32
Del siguiente cuadro de diálogo, nos fijaremos en el siguiente botón, que pulsaremos, para crear una nueva
definición de driver:

En la ventana emergente, seleccionamos el JDBC driver correspondiente a MySQL 5.1:

33
Cambiamos a la pestaña Jar List y clicamos en Clear All, para limpiar los drivers existentes (que en realidad
no estaban localizados en vuestro sistema). Añadimos uno nuevo, clicando en Add JAR/Zip...

y lo buscamos en nuestro sistema de archivos:

(Si no lo tenéis, buscarlo aquí: http://dev.mysql.com/downloads/connector/j/ seleccionad el


correspondiente a platform independent, (independiente de la plataforma), os descargaréis un zip que
tendréis que descomprimir en donde vosotros queráis. Ojo! allí donde lo ubiquéis, tendréis que dejarlo
siempre, ya que después eclipse lo tendrá registrado en ésa localización.

34
Volvemos a movernos a la pestaña Properties y rellenamos los datos correspondientes a nuestra conexión
(2). Clicamos Aceptar. En mi caso, la contraseña de mi usuario root del servidor mySQL instalado en mi
equipo es admin.

Volveremos al cuadro de diálogo para la creación del nuevo perfil, en el que nos vamos a asegurar de que la
casilla Save password esté marcada. Si queremos, podemos testear la conexión al servidor. También
podríamos antes de clicar en Finalizar, clicar en Siguiente, donde se nos mostraría un resumen general de la
configuración que acabamos de hacer... Bueno, lo dicho. Clic en Finalizar y Listo!

35
Vemos, que nos aparecerá nuestro nuevo perfil de conexión en el explorador de fuentes de datos Data
Source Explorer:

Ahora abrimos nuestro archivo pitufos.sql y en la parte superior del mismo, en la sección Connection Profile,
seleccionamos los datos correspondientes al perfil que acabamos de crear:

36
Guardamos el archivo, y si todo está bien, lo podemos ejecutar para crear nuestra bd. Para ejecutarlo,
hacemos clic con botón derecho del ratón sobre el archivo y seleccionamos Execute SQL Files:

Si todo está bien y no nos hemos dejado ninguna coma ni nada raro... debería aparecer una ventana, en la
que si clicáis en Detalles, veréis el progreso de la operación y también otra ventana, titulada SQL Results, al
lado del explorador de data-sources mostrando también el progreso de la operación.

37
Aviso!! Cuando hemos creado el perfil de conexión, en la configuración, habréis observado que hemos
rellenado los datos así:

 Connection URL - jdbc:mysql://localhost:3306/


 Database Name - pitufos
Esto es un error que ha sido creado a propósito, para evitar problemas de conexión al servidor desde eclipse,
puesto que si hubiéramos configurado la URL correctamente, al no existir la bd, nos habría fallado la primera
vez que se conecta, al darle a Aceptar/Finalizar. Ahora lo corregimos. Os muestro la configuración inicial (la
que creamos anteriormente):

Desde el explorador de data-sources, clicamos con botón derecho sobre el perfil y seleccionamos Properties.

38
En el cuadro de diálogo emergente, rellenamos los datos correctamente, como en la imagen de abajo:

Al clicar Aceptar, nos pedirá reconectar al servidor.


Aceptamos y al acabar, la estructura final de nuestro perfil
de conexión a nuestra base de datos, pitufosbd, se nos
mostrará como en la imagen de la izquierda y además
tendremos creado y configurado un perfil de conexión a
nuestra base de datos totalmente funcional.

39
Implementando la Seguridad
A hora que tenemos la base de datos configurada y la aplicación funcionando, aunque de una forma muy
básica, es hora de realizar las implementaciones de seguridad. Par ello, vamos a utilizar el framework
spring security, que nos proporciona dicha integración de una forma simple y efectiva, a través de filtros e
implementación de chequeos de seguridad, basados en roles definidos para usuarios, etcétera...

Vamos a proporcionar un acceso de forma segura a nuestra aplicación. Después de ser implementado, los
usuarios tendrán que autenticarse contra nuestra base de datos, para poder acceder al contenido de la
página que hará de punto de entrada a nuestra aplicación. Spring Security, en la versión que vamos a usar
incorpora la posibilidad de realizar la configuración de la securización, a través de código java. Nosotros no
vamos a utilizar ésa característica todavía. Vamos a configurarlo a la manera tradicional, o sea, a través de
archivos de configuración con formato xml. Comenzamos!!

Como ya tenemos configuradas nuestras dependencias, pasamos directamente a crear nuestra página de
login personalizada. Si bien es cierto que spring security proporciona una página de login por defecto, si no se
le proporciona una, nosotros la vamos a declarar, porque más adelante la vamos a personalizar. nos
movemos al directorio pitufosapp/src/main/webapp/WEB-INF/views y clicamos con el botón derecho del
ratón en el icono de la carpeta. Seleccionamos a continuación Nueva/Otras...

40
Aparece el cuadro de diálogo para la selección del
asistente para la creación del nuevo archivo. Allí
navegamos por la lista hasta el nodo web, donde
seleccionamos JSP File. Clicamos en Siguiente.

En el asistente, nos aseguramos que la ruta de nuestro


nuevo archivo sea pitufosapp/src/main/webapp/WEB-
INF/views, bien tecleándola nosotros mismos, bien,
seleccionando la carpeta en el navegador inserto en el
formulario (1).

Nombramos el archivo como login.jsp, en su apartado


correspondiente (2).

Pulsamos Siguiente (3).

41
Ahora, nos aparecerá el cuadro de diálogo de selección de
plantilla:

Vamos a seleccionar la más normalita, ya la iremos


complicando... New JSP File (html)

Tenemos que asegurarnos primero que la opción Use JSP


Template está habilitada.Podemos observar, como en la
zona (3), nos aparece el código de la plantilla. Esto será lo
que nos aparezca en el editor cuando cliquemos en
Finalizar, que modificaremos después a nuestro gusto.

Una vez en el editor, con el archivo abierto, sustituimos el código insertado por la plantilla por el siguiente:
'pitufosapp/src/main/webapp/WEB-INF/views/login.jsp':
<%@ include file="/WEB-INF/views/include.jsp" %>
<html>
<head>
<title>Autenticación</title>
<style>
.errorblock {
color: #ff0000;
background-color: #ffEEEE;
border: 3px solid #ff0000;
padding: 8px;
margin: 16px;
}
</style>
</head>
<body onload='document.f.j_username.focus();'>
<h3>Autenticación con Usuario y Contraseña (Página Personalizada)</h3>
<c:if test="${not empty error}">
<div class="errorblock">
Su autenticación no ha sido correcta, inténtelo de nuevo.<br /> Causa :
${model.causa}
</div>
</c:if>
<form name='f' action="<c:url value='j_spring_security_check' />"
method='POST'>
<table>
<tr>
<td>Usuario:</td>
<td><input type='text' name='j_username' value=''></td>
</tr>
<tr>
<td>Contraseña:</td>
<td><input type='password' name='j_password' /></td>
</tr>
<tr>
<td colspan='2'><input name="submit" type="submit"
value="Enviar" /></td>
</tr>
<tr>
<td colspan='2'><input name="reset" type="reset" /></td>
</tr>
</table>
</form>
</body>
</html>

42
Veremos que al guardar el archivo nos dará un error:

Esto se debe a la primera línea, donde se declara una directiva para que se incluya un fichero de cabecera,
llamado include.jsp que todavía no hemos declarado. Pues al lío. Realizamos los mismos pasos para la
creación de éste archivo, que hemos realizado para la creación de nuestra página login.jsp, sólo que ésta vez
lo nombraremos include.jsp y le sustituiremos el código por el siguiente:
'pitufosapp/src/main/webapp/WEB-INF/views/include.jsp':
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ page session="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="core" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

Como podemos observar, el código de nuestro archivo de cabecera incorpora las tags jstl que hemos
declarado en nuestras dependencias. Al ser un fichero que vamos a incluir en todas nuestras vistas, quiere
decir que toda nuestra aplicación va a usar jstl. Pero pasemos a observar un momento el código de nuestra
página de login.jsp:

En la línea ${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message} hemos introducido una forma de notificar


un mensaje cuando el usuario se autentica incorrectamente. Aunque esto se puede personalizar. Después
personalizaremos el mensaje, pero lo vamos a dejar así, para que veáis cómo funciona éste la primera vez
por si os gusta, que es bueno que tengáis siempre las dos opciones...

Los valores 'j_spring_security_check' 'j_username' y 'j_password', son nombres estándar de spring security,
que tenemos que seguir, a la hora de implementar nuestra página de login personalizada. Estos son:

1. j_spring_security_check – Login service


2. j_spring_security_logout – Logout service
3. j_username – Username
4. j_password – Password

43
Vemos que el servicio de logout no aparece en nuestro código. Esto es porque el mismo lo tenemos que
incluir en las páginas o vistas de nuestra aplicación. Con lo cual, nos vamos a la página home.jsp e
introducimos el siguiente código: (De paso, incluimos nuestro fichero cabecera).
'pitufosapp/src/main/webapp/WEB-INF/views/home.jsp':
<%@ include file="/WEB-INF/views/include.jsp" %>
<html>
<head>
<title>Home</title>
</head>
<body>
<h1>Hola Mundo!</h1>
<P>La hora en el Servidor es :${model.now}.</P>
<br>
<h3>Message : ${model.message}</h3>
<h3>Username : ${model.username}</h3>
<a href="<c:url value="/j_spring_security_logout" />"> Logout</a>
</body>
</html>

Así, cuando el usuario de la aplicación, cliquee en el link Logout, que le va a aparecer abajo del todo, se le
redirigirá a la página de login de la aplicación, recargando de nuevo toda la sesión y lo relativo a la política de
seguridad de spring. (También se podría incluir en el fichero de cabecera, para evitar la duplicidad de código,
o en un fichero auxiliar que luego incluiríamos en nuestras páginas, en la posición que deseemos).
A continuación vamos a modificar el controlador existente y a crear un nuevo controlador para la página de
login.jsp. Modificamos el controlador existente, HomeController.java, sustituyendo el código existente en la
clase por éste otro:
'pitufosapp/src/main/java/com/guarderias/pitufos/HomeController.java':
package com.guarderias.pitufos;
import java.io.IOException;
import java.security.Principal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
/**
* Maneja los requests de la pagina home.jsp de la app.
*/
@Controller
public class HomeController {
protected final Log logger = LogFactory.getLog(getClass());
/**
* Selecciona la vista home para construirla devolviendo su nombre.
*/
@RequestMapping(value = "/home", method = RequestMethod.GET)
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response,
Principal principal ) throws ServletException, IOException {
String now = (new Date()).toString();
logger.info("Devolviendo la vista home con " + now);
String name = principal.getName();
Map<String, Object> myModel = new HashMap<String, Object>();
myModel.put("now", now);
myModel.put("username", name);
myModel.put("message", "Mensaje de Spring Security");
return new ModelAndView("home", "model", myModel);
}
}

44
La anotación @Controller se usa para especificar que la clase va a ser un controlador de spring.
La anotación @RequestMapping especifica que el método handleRequest(), manejará el GET request, que
provenga de la URL mapeada por el patrón "/home".
A destacar que dicho método handleRequest(), recogerá el parámetro principal de la interfaz
java.security.Principal, que será el objeto que en cierta manera, almacenará al usuario que se va a
autenticar, del que vamos a obtener su nombre de usuario.
En dicho método se nos devuelve un objeto ModelAndView. Este tipo de objetos nos devuelven en un sólo
return el modelo y la vista a la vez, lo que nos facilita el trabajo en gran medida.
La vista será resuelta por el DispatcherServlet y se pasa como un String que será resuelto a su vez por un
ViewResolver (Que ya lo definiremos). El modelo es un objeto de la clase Map (de clave-valor), en el que
ponemos la fecha actual y el mensaje que configuramos anteriormente en login.jsp con el usuario que se
autentica. (En realidad sería al revés; éste controlador, va a ser el que cuando se solicite la vista home, le
proporcione la información obtenida anteriormente en el formulario de login, a través del modelo que
hemos configurado como mapa de objetos).
Para finalizar, me gustaría resaltar que en una clase que actúa como controlador, podemos escribir muchos
métodos para manejar diferentes URLs.

Hemos modificado nuestra clase controladora de a vista home.jsp, a la que vamos a ser redirigidos después
de habernos autenticado. Pero ahora tendremos que escribir el código para la clase controladora de nuestro
formulario de login personalizado, login.jsp.

No nos vamos a complicar mucho. Lo único que queremos es que cuando se solicite a la vista /login ésta sea
devuelta parece una perogrullada, ¿no? pues si no lo especificas no te lo va a hacer! Además, está bien
tenerlo así definido, puesto que te abres la posibilidad de definir tú un modelo propio, políticas
complementarias de seguridad, etc...
También que cuando el usuario cometa un error de autenticación, que es cuando se solicitará la vista
/loginfailed, se nos devuelva otra vez la vista login, pero con la variable "error" puesta a true, la cual, la
pasaremos a través del modelo de ésa vista que se nos ha pasado como argumento.
Además, que cuando el usuario de la aplicación haga el logout, o sea, se solicite la vista /logout, se nos
devuelva a la vista/página login.
Bueno, vamos a aprovechar para ir separando por paquetes las clases que usaremos en el código. Crearemos
cuatro paquetes bajo com.guarderias.pitufos, que se llamarán dominio, repositorio, servicio y web,
respectivamente. Moveremos la clase HomeController.java a su paquete correspondiente y crearemos la
clase LoginController.java. Empezamos!!

Clicamos con botón derecho del ratón sobre el paquete com.guarderias.pitufos y seleccionamos
Nueva/Paquete:

45
En el cuadro de diálogo de creación de
Paquete Java, introducimos como nombre
com.guarderias.pitufos.dominio y clicamos
Finalizar. Repetimos ésta acción para los
paquetes servicio, repositorio y web.

Movemos la clase HomeController.java; Para ello vamos a clicarla con el botón derecho del ratón y
seleccionamos en el menú emergente la opción Refactorizar/Mover...:

46
En el cuadro de diálogo Mover,
seleccionamos el paquete donde
vamos a depositar nuestras clases
controladoras, el paquete web y
clicamos Aceptar. Eclipse, actualizará
todas las referencias a la nueva
ubicación de la clase por nosotros.

Una vez hecho, vamos a crear la clase LoginControler.java. Recordemos que será la que manejará a la vista
login.jsp y a /loginfailed y a /logout. Esta notación la hago así, porque son valores por defecto de spring
security, aunque podríamos personalizarlos también. Nos situamos sobre el paquete
com.guarderias.pitufos.web y como casi siempre, pulsamos el botón derecho del ratón y del menú
contextual seleccionamos Nueva/Clase:

47
Nos va a aparecer un cuadro
de diálogo de creación de
clases Java como el que
aparece a la derecha: >>>>>>
En el apartado Nombre
escribimos el nombre de la
clase LoginController y
clicamos en Finalizar. A
continuación nos aparecerá
en el editor, la clase recién
creada, a la que vamos añadir
el código necesario para
manejar los requisitos
comentados anteriormente.
Sustituiremos el código
generado por el código que
se encuentra más abajo. v
v
v
v
v
v
v
v
v
v
v
'pitufosapp/src/main/java/com/guarderias/pitufos/LoginController.java':
package com.guarderias.pitufos.web;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class LoginController {

@RequestMapping(value="/login", method = RequestMethod.GET)


public String login(ModelMap model) {
return "login";
}

@RequestMapping(value="/loginfailed", method = RequestMethod.GET)


public String loginerror(ModelMap model) {

model.addAttribute("error", "true");
return "login";
}

@RequestMapping(value="/logout", method = RequestMethod.GET)


public String logout(ModelMap model) {
return "login";
}
}

48
Con ésta clase, hemos conseguido realizar las especificaciones comentadas anteriormente. Ya sólo nos queda
crear el archivo spring-security.xml que será el archivo donde se gestionará la configuración de spring
security para nuestra aplicación e implementar la autenticación con xml primero, como ejemplo, después
contra la bd. Después modificaremos los xml de spring para introducir la seguridad, a través de los filtros
necesarios, configuraremos un ViewResolver, que nos resolverá las vistas devueltas como String por los
métodos manejadores de nuestras clases controladoras y le diremos al spring framework dónde se ubica el
archivo spring-security.xml.

Lo primero que vamos a hacer va a ser, modificar el fichero servlet-context.xml. Lo abrimos y completamos
el código existente con el siguiente, o, para facilitaros el trabajo, sustituid el código del archivo por éste otro:

'pitufosapp/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml':
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<!-- DispatcherServlet Context: defines this servlet's request-processing


infrastructure -->

<!-- Activa el modelo de programación para el Spring MVC @Controller -->


<mvc:annotation-driven />

<!-- Maneja los HTTP GET requests para los recursos /resources/** sirviendo
estática y eficientemente los recursos sitos en el directorio
${webappRoot}/resources -->
<mvc:resources mapping="/resources/**" location="/resources/" />

<!-- Resuelve las vistas seleccionadas para ser construídas por los @Controllers
a los recursos .jsp sitos en el directorio /WEB-INF/views -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>

<!-- Escanea el classpath de la aplicación, en busca de @Components para


desplegarlos como beans -->
<context:component-scan base-package="com.guarderias.pitufos.web" />

</beans>

El código va comentado, así que no me pararé más en éste archivo.

49
Vamos a modificar el fichero root-context.xml con el siguiente código. No me pararé a comentarlo puesto
que ya lo está en los comentarios del mismo fuente:

'pitufosapp/src/main/webapp/WEB-INF/spring/root-context.xml':
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<!-- Root Context: Define recursos compartidos visibles para todos los otros
componentes web -->

<!-- activando la configuración para la aplicación conducida por anotaciones


"annotation driven" -->
<context:annotation-config />

</beans>

A continuación creamos el fichero spring-security.xml, clicando con botón dcho. del ratón sobre alguna de
las carpetas bajo WEB-INF/spring, y seleccionando Nueva/Spring Bean Configuration File:

50
En el cuadro de diálogo de creación del archivo,
vamos a localizar al mismo en la carpeta spring,
bien seleccionándola, bien, mecanografiando
directamente la localización (1).

Nombramos al archivo spring-security.xml (2).

Clicamos en Siguiente (3).

Ahora vamos a seleccionar los


namespaces que se usarán en el
archivo que estamos creando. Primero
la versión de los beans:
Marcamos la casilla beans (1).
Seleccionamos el esquema para la
versión 3.0 (2).

51
Seguidamente, el esquema para spring
security:
Marcamos la casilla security (3).
Seleccionamos el esquema para la
versión genérica (4).
Clicamos el botón Siguiente (5).

La última pantalla, es opcional y sirve para


establecer el conjunto de beans donde
queréis que se almacene la configuración
actual. No es necesario, por lo que
anteriormente podéis clicar en Finalizar,
pero es mejor asegurarse que estará en
nuestro proyecto... así que, Marcamos la
casilla de nuestro proyecto (6) y Clicamos
en Finalizar (7).

52
Hemos visto cómo crear un archivo de configuración de spring security desde el asistente. El mismo nos
generará un esquema para codificar nuestros beans y configuraciones dentro del archivo.
Nosotros, por ahora vamos a sustituir el código generado por otro, que ya viene explicado en los
comentarios, por lo que no me pararé tampoco a explicarlo. Sólo diré que es importante sustituir las líneas
de código generadas de forma automática, referentes al esquema seleccionado con las que se proporcionan
a continuación. Aparte, al guardar el archivo nos avisará de dos warnings de uso de métodos depreciados.
Son bugs que aún hoy siguen abiertos en los foros de spring security, así que no hay que hacerle mucho
caso. Por supuesto, queda por añadir, que si editamos el archivo desde el Spring Config Editor, tendremos
muchísimas más posibilidades de configuración y edición visual, pero eso, es otra historia y no compete a los
objetivos de éste curso. Os dejo una captura para abrir el archivo con el Spring Config Editor:

53
A continuación, sustituimos el código generado con el siguiente:
'pitufosapp/src/main/webapp/WEB-INF/spring/spring-security.xml':
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd">

<!-- Configuración de la interceptación http -->


<http auto-config="true" use-expressions="true">
<!-- Establecemos el mapeo para las páginas de login, logout,
redireccionamiento y los roles de acceso -->
<!-- Nota: Se puede sustituir por "hasRole('ROLE_USER')" ó "hasRole('ROLE_ADMIN')"
si se quiere que sólo un usuario que tenga ése rol tenga acceso a la página. -->
<intercept-url pattern="/home.htm" access="hasAnyRole('ROLE_USER', 'ROLE_ADMIN')" />
<intercept-url pattern="/login*" access="permitAll" />
<form-login login-page="/login.htm" always-use-default-target="true"
default-target-url="/home.htm" authentication-failure-url="/loginfailed.htm" />
<logout invalidate-session="true" logout-success-url="/logout.htm"/>
<remember-me/>
</http>
<!-- Proveemos un gestor de autenticación donde los usuarios y contraseñas
se almacenan en el mismo archivo de configuración de seguridad XML, que también
cifra la contraseña, usando el algoritmo de encriptación SHA, tanto para
el almacenaje como para la validación de la clave introducida por el usuario
del formulario login -->
<!-- La encriptación se ha realizado con la librería jacksum.jar y la siguiente
instrucción desde la línea de comandos en cualquier directorio
que contenga dicha librería: java -jar jacksum.jar -a sha -q "txt:juliopass" -->
<authentication-manager>
<authentication-provider>
<password-encoder hash="sha" />
<user-service>
<user name="julio" password="55c95897e527b6925ad98cfcd0b7bd9e5bf78a29"
authorities="ROLE_USER, ROLE_ADMIN" />
<user name="ainhoa" password="cdd73be39a3dc11296e4dfce94738d6d7f2db4fa"
authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>

Ahora os explico; Este ejemplo de código, sirve para la validación de dos usuarios, los cuales son
almacenados NO EN LA BASE DE DATOS, sino en el mismo archivo de configuración de seguridad, como ya
viene explicado en los comentarios del mismo código fuente. Mi intención ha sido realizarlo así para que
conozcáis al menos, dos de las posibilidades que existen (existen más tecnologías de autenticación que son
soportadas, como por ejemplo la autenticación contra LDAP, JOSSO, ATLASSIAN, X.509, JA-SIG, y un largo
etc... pero eso no es objetivo de éste curso). Lo que vamos a hacer es probar que efectivamente funciona
nuestra configuración de seguridad y cuando hayamos confirmado éste respecto, volveremos a éste archivo
para modificarlo y que la autenticación se realice CONTRA LA BASE DE DATOS, con lo que primero
tendremos que crear los usuarios en la base de datos y asignarles sus roles respectivos, aparte de proveer a

54
nuestra aplicación de un archivo de configuración de jdbc, vamos, un datasource, con la información de
acceso a nuestra bd, el cual, deberá ser informado posteriormente en el archivo root-context.xml.
Habréis observado también, que hemos usado una librería, jacksum.jar, la cual os la podéis descargar de
aquí: http://www.jonelo.de/java/jacksum/ Os recomiendo que consultéis la documentación, merece la pena.
Si la descargáis y la guardáis por ejemplo en c: a continuación os dejo una captura de cómo la usé yo para la
encriptación de la contraseña de uno de los usuarios ficticios que inventé para nuestra app:

Con lo cual, podéis sustituir el código correspondiente en el archivo spring-security.xml por el que vosotros
decidáis, para vuestros usuarios y contraseñas. Por supuesto, me dejo la posibilidad de implementación de
ésta utilidad en la misma aplicación, porque es una manera "de no perder el contacto con el cliente", ya que
de ésta forma, seremos nosotros quienes gestionaremos la creación de usuarios, contraseñas y sus roles
correspondientes y su habilitación, en la aplicación. Aunque no debemos descartar la posibilidad de que
nuestro cliente, específicamente, nos pida delegarle ésos atributos en un apartado de gestión de la
aplicación, que habría entonces que programar también... en fin, no me enrollo. Seguimos.

Lo último que nos queda, es la modificación del archivo descriptor de despliegue de nuestra aplicación, al
que debemos informarle de la localización del archivo spring-security.xml y de los filtros de spring security
que mapearán las url definidas por nosotros y que serán todas (/*).

55
Hacemos doble clic sobre el archivo web.xml de nuestra aplicación, y completamos el código existente con el
siguiente, o bien, os dejo todo el código del archivo para que realicéis un corta-pega. (más fácil);

'pitufosapp/src/main/webapp/WEB-INF/web.xml':
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

<!-- Definimos el contenedor raíz de Spring compartido por todos los Servlets y
Filtros y la configuración de Spring Security -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/root-context.xml
/WEB-INF/spring/spring-security.xml
</param-value>
</context-param>

<!-- Definimos los filtros para spring security -->


<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Creamos el contenedor de Spring compartido por todos los Servlets y Filtros -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- Definimos el Servlet que procesará los requests de nuestra app-->


<servlet>
<servlet-name>pitufosapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>pitufosapp</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>

</web-app>

56
Antes de ejecutar la aplicación, debemos añadir un archivo que hará de punto de entrada a nuestra
aplicación. Esto es debido a otro bug de mapeo de patrones de URLs en spring mvc. Mientras se resuelve, si
no añadís el archivo que vamos a crear a continuación, al ejecutar la aplicación, el navegador os re-
direccionará a una página de error 404 y veréis el siguiente mensaje en la salida de la Consola:

WARNING: No mapping found for HTTP request with URI [/pitufos/] in DispatcherServlet with
name 'pitufosapp'

No hay que darle más vueltas ni calentarse la cabeza con eso. Es eso, un Bug o error que cometieron los
programadores de spring cuando lo desarrollaron y que a fecha de hoy, 21-01-2014, no está resuelto. Ahora
lo vamos a salvar nosotros, creando un archivo que llamaremos index.jsp bajo el directorio webapp e
insertándole el siguiente código: (No incluyo capturas, porque ya hemos creado archivos jsp antes...)

'pitufosapp/src/main/webapp/index.jsp':
<%@ include file="/WEB-INF/views/include.jsp" %>

<%-- Redireccionamos, porque no queremos establecer la página de bienvenida a una URL


virtual. --%>
<c:redirect url="/home.htm"/>

Bien! Si ahora ejecutamos nuestra aplicación, la misma nos deberá re-direccionar desde index a home y
como home está interceptada por spring security a login:

57
Si la autenticación falla, os aparecerá la siguiente página:

Y finalmente, autenticándose correctamente, aparecéis en la página principal de nuestra aplicación, donde si


clicáis en el Link logout, retornaréis a la página de login:

58
Para acabar éste apartado, os dejo una captura de cómo deberíais de llevar la estructura de directorios y
archivos del proyecto llegados a éste punto:

59
Autenticando contra la Base de Datos
A modo de ejemplo, lo visto anteriormente dejaría mucho que desear en materia de seguridad. ¿Almacenar
los usuarios y las contraseñas en el mismo archivo? No parece la mejor solución... así que lo que vamos a
hacer es realizar la implementación necesaria para que el almacenamiento de los usuarios y contraseñas sea
llevado a cabo en la base de datos de la aplicación. Para ello, primero tendremos que modificar nuestros
archivos de configuración de Spring e informar de la persistencia que vamos a utilizar.

Vamos a crear un archivo al que denominaremos hibernate.properties y que situaremos en el directorio


classes. Allí vamos a informar de los datos necesarios para la conexión a la base de datos:

En el cuadro de diálogo para la creación de nuevo


archivo, bien, mecanografiamos la ruta donde se
ubicará, bien marcamos la carpeta classes (1).

Nombramos el archivo a hibernate.properties (2).

Clicamos Finalizar (3).

60
A continuación, proporcionaremos los datos de nuestra conexión, el usuario de la aplicación, la contraseña,
el dialecto que usará hibernate para comunicarse con nuestra bd, etc...

'pitufosapp/src/main/webapp/WEB-INF/classes/hibernate.properties':
hibernate.connection.driver_class=com.mysql.jdbc.Driver
hibernate.connection.url=jdbc:mysql://localhost:3306/pitufosbd
hibernate.connection.username=pitufosappuser
hibernate.connection.password=pitufosappuserpss
hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
hibernate.database=MYSQL
hibernate.generate_statistics=true
hibernate.show_sql=true
hibernate.format_sql=true
hibernate.generateDdl=true

Por otro lado, creamos la unidad de persistencia. Por defecto, este fichero se deberá llamar persistence.xml
y deberá crearse bajo el directorio META-INF dentro del directorio de recursos de la aplicación. Luego os
explicaré para qué:

' pitufosapp /src/main/resources/META-INF/persistence.xml':


<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="pitufosappPU" transaction-type="RESOURCE_LOCAL" />
</persistence>

Modificamos el archivo root-context.xml con el siguiente código:

'pitufosapp/src/main/webapp/WEB-INF/spring/root-context.xml':
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<!-- Root Context: Define recursos compartidos visibles para todos los otros
componentes web -->

<!-- activando la configuración para la aplicación conducida por anotaciones


"annotation driven" -->
<context:annotation-config />

61
<!-- Informamos la localización del archivo con los datos de conexión a la bd -->
<context:property-placeholder location="classpath:hibernate.properties" />
<!-- Definimos un Bean identificado como dataSource, cuyos valores de
configuración serán comodines. Dichos valores, serán tomados en tiempo de
ejecución, del archivo de propiedades que acabamos de configurar e informnar
justo arriba. -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${hibernate.connection.driver_class}" />
<property name="url" value="${hibernate.connection.url}" />
<property name="username" value="${hibernate.connection.username}" />
<property name="password" value="${hibernate.connection.password}" />
</bean>
<!-- Definimos el Bean sessionFactory que se encargará de recuperar las sesiones
de hibernate. Utilizaremos la clase AnnotationSessionFactoryBean, ya que se
utiliza para cargar entidades anotadas mediante el Java Persistence API
(JPA), le inyectamos nuestro dataSource y establecemos sus propiedades. -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="hibernateProperties">
<props>
<prop key="dialect">"${hibernate.dialect}"</prop >
<prop key="database">"${hibernate.database}"</prop>
<prop
key="generate_statistics">"${hibernate.generate_statistics}"</prop>
<prop key="show_sql">"${hibernate.show_sql}"</prop>
<prop key="format_sql">"${hibernate.format_sql}"</prop>
<prop key="generateDdl">"${hibernate.generateDdl}"</prop>
</props>
</property>
<property name="packagesToScan" value="com.guarderias.pitufos.dominio" />
</bean>
<!-- Definimos el Bean hibernateTemplate, que nos proporciona
HibernateTemplate, una plantilla para realizar operaciones con Hibernate. Lo
que estamos haciendo aquí es crear una instancia para poder inyectársela a
nuestros DAO. Esta plantilla estará definida en la clase HibernateDaoSupport,
que será heredada por la clase Base que implementaremos para dar
funcionalidades a nuestros objetos DAO. Al mismo,
le inyectamos el sessionFactory. -->
<bean id="hibernateTemplate"
class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- Definimos el Bean transactionManager y le inyectamos nuestro
sessionFactory -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory" />
<!-- habilitamos la configuración para el comportamiento transaccional basado en
anotaciones lo que nos permitirá anotar nuestras clases con la anotación
@Transactional -->
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- Escanea el classpath de la aplicación, en busca de @Components para
desplegarlos como beans. En estos paquetes implementaremos las clases
necesarias para atacar a nuestra bd -->
<context:component-scan base-package="com.guarderias.pitufos.repositorio" />
<context:component-scan base-package="com.guarderias.pitufos.servicio" />
</beans>

62
En éste punto sólo nos falta modificar el archivo spring-security.xml para realizar la autenticación contra la
base de datos y dar de alta dos usuarios. Vamos a dejar en primer lugar, "comentado", el código del ejemplo
de autenticación anterior, donde se proveía de los nombres de usuarios y sus contraseñas en el mismo
archivo de configuración de seguridad, spring-security.xml , para que quede para el recuerdo y
proporcionamos un nuevo proveedor de autenticación contra la base de datos. Sustituimos la porción
correspondiente en el archivo con la siguiente:

'pitufosapp/src/main/webapp/WEB-INF/spring/spring-security.xml':
......
<authentication-manager>
<!--
<authentication-provider>
<password-encoder hash="sha" />
<user-service>
<user name="julio" password="55c95897e527b6925ad98cfcd0b7bd9e5bf78a29"
authorities="ROLE_USER, ROLE_ADMIN" />
<user name="ainhoa" password="cdd73be39a3dc11296e4dfce94738d6d7f2db4fa"
authorities="ROLE_USER" />
</user-service>
</authentication-provider>
-->

<!-- Ahora proveemos un gestor de autenticación contra la base de datos


que usará la aplicación (se crearon dentro de la bd dos tablas, una de usuarios
y otra de roles de usuarios) -->
<authentication-provider>
<password-encoder hash="sha" />
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select usuario,password,enabled from usuarios where
usuario=?"
authorities-by-username-query="select u.usuario, r.rol from usuarios u, roles r
where u.idusuario = r.idusuario and u.usuario=?" />
</authentication-provider>
</authentication-manager>
......

Sólo nos queda dar de alta los usuarios correspondientes en la base de datos. Creamos un nuevo archivo sql
llamado usuariosPitufos.sql y le introducimos las siguientes instrucciones:

'pitufosapp/db/ usuariosPitufos.sql ':


INSERT INTO pitufosbd.usuarios (usuario, password, enabled)
VALUES ('julio', '55c95897e527b6925ad98cfcd0b7bd9e5bf78a29', TRUE);

INSERT INTO pitufosbd.roles (idusuario, rol)


VALUES (1, 'ROLE_USER');

INSERT INTO pitufosbd.usuarios (usuario, password, enabled)


VALUES ('ainhoa', 'cdd73be39a3dc11296e4dfce94738d6d7f2db4fa', TRUE);

INSERT INTO pitufosbd.roles (idusuario, rol)


VALUES (2, 'ROLE_ADMIN');

INSERT INTO pitufosbd.roles (idusuario, rol)


VALUES (2, 'ROLE_USER');

63
Después configuramos el perfil de conexión (procurad que el perfil que creamos anteriormente al que
llamamos mySQL 5.5 Local esté conectado al servidor mySQL), tal y como aparece en la siguiente imagen, y
después de guardar el archivo, lo ejecutamos de la misma forma que hicimos con el script sql anterior:

Y hecho esto, podremos probar por fin, la autenticación contra la base de datos, realizada desde nuestra
aplicación y gestionada por Spring Security.
Queda por aclarar "una cosilla":
1º La conexión a la base de datos la realiza la aplicación, usando un usuario y su contraseña,
(pitufosappuser), que está contenido en el esquema de usuarios, del propio servidor mySQL. Este usuario, es
diferente, de los que hemos creado y vamos a usar para nuestro programa. Se podría configurar el servicio
de autenticación, para que los usuarios de la base de datos coincidieran con los usuarios del servidor mySQL
y coincidieran con el equipo que instancia la aplicación al ser ejecutada, o sea, alimentar el nombre de
usuario y su password en la unidad de persistencia desde el archivo jdbc.properties, desde el formulario de
autenticación, previo login y durante el despliegue de la aplicación... Eso sería lo mejor, desde mi punto de
vista si implementáramos LDAP, pero como dije anteriormente, es subjetivo y para éste tipo de
implementaciones prefiero hacerlo así.

64
Desarrollando la capa de Persistencia
C omenzamos con la implementación de la capa de Persistencia que realizará el acceso a los datos, donde
implementaremos nuestros POJOs, que situaremos en el paquete dominio y nuestras clases de
persistencia que pondremos en el paquete repositorio. Después desarrollaremos nuestra capa de
Aplicación, en el paquete servicio,
donde situaremos nuestras clases
manager y por último, completaremos
la capa de Presentación con los
controller en el paquete web. Estas dos
últimas capas, las realizaremos en los
siguientes dos apartados. A la derecha,
os dejo un esquema de la arquitectura
de nuestra app.

Añadimos los POJOs a la lógica de


negocio de nuestra aplicación. Esto
suele ser una tarea bastante tediosa,
pero muy importante, puesto que un modelo de relación-entidad poco sólido, no encajaría bien con nuestro
framework de persistencia. Así que vamos a crear las clases entidad que representarán a las tablas de la base
de datos con paciencia. De paso veremos cómo implementamos JPA e Hibernate mediante anotaciones.

Creamos la clase Alumnos.java que representará a la tabla ALUMNOS en la bd. A partir de aquí, obviaremos
el proceso de creación de clases en eclipse, puesto que ya ha sido ilustrado anteriormente, por lo que sólo os
dejaré la ubicación del archivo y el código fuente que hay que implementar, que incorporará los comentarios
que lo explican. A medida que vayamos avanzando veréis menos comentarios, puesto que solamente añadiré
los necesarios para explicar los nuevos aspectos que nos vayamos encontrando.

65
'pitufosapp/src/main/java/com/guarderias/pitufos/dominio/Alumnos.java':
package com.guarderias.pitufos.dominio;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

/**
* Todas las clases entidad tienen que implementar la interfaz Serializable.
* La serialización de un objeto consiste en obtener una secuencia de bytes que represente el
* estado de dicho objeto. Esta secuencia puede utilizarse de varias maneras (puede enviarse a
* través de la red, guardarse en un fichero para su uso posterior, utilizarse para recomponer el
* objeto original, etc.).
*
* @author Julio Bellón Aguilera. Enero 2014.
*/
@Entity(name="Alumnos") //Significa que ésta clase es una clase entidad y su id, el String Alumnos
@Table(name="alumnos") // Significa que ésta clase representa a la tabla alumnos de la bd
public class Alumnos implements Serializable {

/*
* La serialiación de objetos requiere un control de versiones que debe declararse
* para que al deserializar (reconstruir) el objeto, se haga correctamente.
* Más información aquí:
* http://docs.oracle.com/javase/7/docs/platform/serialization/spec/serialTOC.html
* http://www.mkyong.com/java-best-practices/understand-the-serialversionuid/
* http://www.mkyong.com/java/how-to-generate-serialversionuid/
*/

private static final long serialVersionUID = 1L;

66
/*
* Las siguientes tres anotaciones significan lo siguiente:
* @Id - Significa que éste campo que mapea una columna de la tabla de la bd, hace de clave primaria
* @Column(name = "idalumno", nullable=false) - Significa que éste campo mapea la columna "idalumno"
* y que su valor no puede ser nulo.
* @GeneratedValue(strategy = GenerationType.IDENTITY) - indica que Hibernate deberá generar
* el valor de la clave primaria.
* El atributo strategy nos indica el método en el que Hibernate debe generar la clave primaria.
* En nuestro ejemplo el valor es GenerationType.IDENTITY que significa que Hibernate
* usará el valor de la columna de tipo autoincremento. Es decir que al insertar la fila
* la base de datos le asignará el valor. La columna debe ser de tipo autonumérico.
*/
@Id
@Column(name = "idalumno", nullable=false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idalumno; // Campo para la columna de la bd.

@Column(name="nombre") // Significa que éste campo mapea la columna "nombre"


private String nombre; // Campo para la columna de la bd. (No tiene por qué coincidir en el nombre)

@Column(name="apellidos")
private String apellidos;

/*
* Las siguientes dos anotaciones significan lo siguiente:
* @ManyToOne(fetch=FetchType.EAGER) - Sirve para realizar una optimización del rendimiento de
* Hibernate a la hora de realizar las consultas a la base de datos. Le estamos diciendo que
* realice una carga de datos "proactiva", o sea, que los datos se cargan cada vez que se consulta
* la entidad "padre/dueña" de la relación.
* @JoinColumn(name="idcentro", nullable=false) - Sirve para enlazar con la columna "idcentro"
* de la tabla centros mapeada por la clase entidad Centros.java, para así establecer la relación Uno
* a Muchos entre las dos tablas. De éste lado, (del lado de los alumnos), la relación se ve como
* Muchos a Uno o ManyToOne. Del lado de los centros, la relación se ve como la hemos definido, o sea,
* como Muchos a Uno o OneToMany (un centro, con muchos alumnos). Se que puede ser repetitivo o parecer
* una perogrullada, pero es conveniente tener éstos conceptos claros, porque una mala identificación
* de las relaciones entre tablas o entidades, puede llevar al traste tu desarrollo!
*/
@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name="idcentro", nullable=false)
private Centros centro;

67
@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name="idcurso", nullable=false)
private Cursos curso;

@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name="idpmt", nullable=false)
private Pmts pmt;

/**
* Una clase entidad debe tener al menos un constructor vacío.
*/
public Alumnos() {}

/**
* Construye un objeto de tipo Pitufos con toda la información del mismo.
* @param idalumno - El índice o clave primaria
* @param nombre - El nombre del alumno
* @param apellidos - Los apellidos del alumno
* @param centro - El centro al que pertenece el alumno
* @param curso - El curso en el que está matriculado el alumno
* @param pmt - El padre, madre o tutor del alumno
*/
public Alumnos(Integer idalumno, String nombre, String apellidos,
Centros centro, Cursos curso, Pmts pmt) {
this.idalumno = idalumno;
this.nombre = nombre;
this.apellidos = apellidos;
this.centro = centro;
this.curso = curso;
this.pmt = pmt;
}

68
/**
* GETTERS Y SETTERS:
* Es recomendable que todas las clases entidad tengan los getters y setters para que hibernate
* acceda a los campos.
* Sin embargo nos puede interesar que no estén alguno de esos métodos para que el usuario
* no pueda cambiar o leer los valores. En ese caso le deberemos decir a Hibernate que acceda
* directamente a las propiedades privadas, ya que por suerte Hibernate sabe hacerlo.
* No obstante, el acceso por parte de Hibernate a éstas propiedades/campos
* será diferente si las anotaciones las colocamos sobre las propiedades o sobre los getters.
* Si colocamos las anotaciones sobre las propiedades,
* el acceso será a las propiedades y no serán necesarios los métodos get/set.
* Si colocamos las anotaciones sobre los métodos get(),
* el acceso será mediante los métodos get/set.
*/

/**
* Getter para la clave primaria
* @return el idalumno
*/
public Integer getIdalumno() {
return idalumno;
}

/**
* Getter para la propiedad "nombre" que mapea la columna "nombre" de la tabla "alumnos" en la bd
* @return el nombre
*/
public String getNombre() {
return nombre;
}

/**
* Getter para la propiedad "apellidos" que mapea la columna "apellidos" de la tabla "alumnos" en la bd
* @return el apellidos
*/
public String getApellidos() {
return apellidos;
}

69
/**
* Getter para la propiedad "centro" que mapea la columna "idcentro" de la tabla "centros" en la bd
* @return el centro
*/
public Centros getCentro() {
return centro;
}

/**
* Getter para la propiedad "curso" que mapea la columna "idcurso" de la tabla "cursos" en la bd
* @return el curso
*/
public Cursos getCurso() {
return curso;
}

/**
* Getter para la propiedad "pmt" que mapea la columna "idpmt" de la tabla "pmt" en la bd
* @return el pmt (padre, madre o tutor)
*/
public Pmts getPmt() {
return pmt;
}

/**
* Setter para la clave primaria
* @param idalumno el idalumno a establecer
*/
public void setIdalumno(Integer idalumno) {
this.idalumno = idalumno;
}

/**
* Setter para la propiedad "nombre" que mapea la columna "nombre" de la tabla "alumnos" en la bd
* @param nombre el nombre a establecer
*/
public void setNombre(String nombre) {
this.nombre = nombre;
}

70
/**
* Setter para la propiedad "apellidos" que mapea la columna "apellidos" de la tabla "alumnos" en la bd
* @param apellidos el apellidos a establecer
*/
public void setApellidos(String apellidos) {
this.apellidos = apellidos;
}

/**
* Setter para la propiedad "centro" que mapea la columna "idcentro" de la tabla "centros" en la bd
* @param centro el centro a establecer
*/
public void setCentro(Centros centro) {
this.centro = centro;
}

/**
* Setter para la propiedad "curso" que mapea la columna "idcurso" de la tabla "cursos" en la bd
* @param curso el curso a establecer
*/
public void setCurso(Cursos curso) {
this.curso = curso;
}
/**
* Setter para la propiedad "pmt" que mapea la columna "idpmt" de la tabla "pmt" en la bd
* @param pmt el pmt a establecer
*/
public void setPmt(Pmts pmt) {
this.pmt = pmt;
}
/**
* Sobrescitura del método toString heredado de la clase Object
* Lo podemos modificar a nuestro gusto para mostrar los datos convenientemente
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("Alumnos [idalumno=%s, nombre=%s, apellidos=%s]", idalumno,
nombre, apellidos);
}
}

71
'pitufosapp/src/main/java/com/guarderias/pitufos/dominio/Centros.java':
package com.guarderias.pitufos.dominio;

import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;

/**
* @author Julio Bellón Aguilera. Enero 2014.
*/
@Entity(name="Centros")
@Table(name="centros")
public class Centros implements Serializable {

private static final long serialVersionUID = 1L;

@Id
@Column(name = "idcentro", nullable=false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idcentro;

@Column(name="nombre")
private String nombre;

72
/*
* La siguientes anotaciones indican:
* @OneToMany(fetch=FetchType.EAGER, mappedBy="centro", cascade=CascadeType.ALL)
* Que es la relación Uno a Muchos, del lado de la clase propietaria de la relación, la cual
* contendrá la lista de los objetos relacionados. (El objeto centro contendrá los objetos alumnos).
* Que la carga de datos se realizará de forma "proactiva". (fetch=FetchType.EAGER)
* Que dicha asociación será mapeada por la, propiedad/campo/variable, global "centro", que está
* definida en la clase Alumnos.java (mappedBy="centro")
* Que si se realiza cualquier tipo de acción sobre el objeto principal, también se realizará sobre los
* objetos relacionados. (Esto os lo aclaro más abajo, con dibujitos y todo...) (cascade=CascadeType.ALL)
* @Fetch(value = FetchMode.SUBSELECT) Establece ésta colección dentro de una consulta tipo "SUBSELECT".
*/
@OneToMany(fetch=FetchType.EAGER, mappedBy="centro", cascade=CascadeType.ALL)
@Fetch(value = FetchMode.SUBSELECT)
private List<Alumnos> alumnosCentro;

@OneToMany(fetch=FetchType.EAGER, mappedBy="centro", cascade=CascadeType.ALL)


@Fetch(value = FetchMode.SUBSELECT)
private List<Profesores> profesoresCentro;

public Centros() {}

public Centros(Integer idcentro, String nombre,


List<Alumnos> alumnosCentro, List<Profesores> profesoresCentro) {
this.idcentro = idcentro;
this.nombre = nombre;
this.alumnosCentro = alumnosCentro;
this.profesoresCentro = profesoresCentro;
}
/**
* @return el idcentro
*/
public Integer getIdcentro() {
return idcentro;
}
/**
* @return el nombre
*/
public String getNombre() {
return nombre;
}

73
/**
* @return el alumnosCentro
*/
public List<Alumnos> getAlumnosCentro() {
return alumnosCentro;
}

/**
* @return el profesoresCentro
*/
public List<Profesores> getProfesoresCentro() {
return profesoresCentro;
}

/**
* @param idcentro el idcentro a establecer
*/
public void setIdcentro(Integer idcentro) {
this.idcentro = idcentro;
}

/**
* @param nombre el nombre a establecer
*/
public void setNombre(String nombre) {
this.nombre = nombre;
}

/**
* @param alumnosCentro el alumnosCentro a establecer
*/
public void setAlumnosCentro(List<Alumnos> alumnosCentro) {
this.alumnosCentro = alumnosCentro;
}

/**
* @param profesoresCentro el profesoresCentro a establecer
*/
public void setProfesoresCentro(List<Profesores> profesoresCentro) {
this.profesoresCentro = profesoresCentro;
}

74
/**
* Sobrescitura del método toString heredado de la clase Object
* Lo podemos modificar a nuestro gusto para mostrar los datos convenientemente
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("Centros [idcentro=%s, nombre=%s]", idcentro,
nombre);
}
}

Antes de continuar dos aclaraciones:

1. Si realizáis la implementación de las clases manualmente aprenderéis mejor los conceptos e iréis viendo cómo tenemos que ir haciendo un poco todo a la
vez, lo que nos demuestra una vez más que la fase de diseño y la estrategia a seguir, que han sido definidas previamente son muy importantes. Casi vitales.
Esto se ve, cuando declaráis el tipo Centros en la clase Alumnos. Os dará error, claro, porque todavía no habéis creado la clase Centros.java, pero, ¿Os vais a
poner a crear ahora la clase Centro.java y dejar a la mitad la clase Alumnos.java? no, ¿verdad? sólo creamos la clase Centros.java, para que se quite el
error...
2. Sobre cascade=CascadeType.ALL , que había dicho que comentaría más abajo con dibujitos; Cuando realizamos el diseño de nuestra base de datos,
introdujimos unas claves foráneas, al objeto de que la gestión de los registros existentes en la base de datos estuviera semi-gestionada por la misma base de
datos, en base a unas reglas de gestión que nosotros mismos establecimos. La cuestión es: ¿Cómo decidimos esas reglas? y ¿Cómo las integramos después
con hibernate?
Lo primero que debéis hacer una vez identificadas las entidades de la base de datos (alumnos, profesores, centros, cursos, etc...) es decidir cómo se van a
relacionar entre ellas. Si un profesor va a pertenecer a un sólo centro o no, si un curso tiene muchos alumnos, etc... Esto, nos lo va a decir la lógica de
negocio que vamos a deducir después de hacer aquellas famosas reuniones con el cliente de las que hablamos al principio de éste curso (métrica).
En cuanto las relaciones entre las entidades que forman la bd estén establecidas, deberemos preguntarnos por la integridad de las mismas, me explico con
un ejemplo: La relación entre un centro y sus alumnos. ¿Si ése centro cierra, los alumnos se quedan en la calle? Si. ¿Si actualizamos el centro deberíamos
actualizar los alumnos? Si. Entonces, la restricción de clave foránea que aplicamos cuando al crear la tabla alumnos de la bd:

KEY FK_AL_CE (idcentro),


CONSTRAINT FK_AL_CE FOREIGN KEY (idcentro) REFERENCES centros (idcentro) ON DELETE CASCADE ON UPDATE CASCADE

75
Está correctamente establecida. Pero, ¿Cómo la integramos después con hibernate?
Bien, en hibernate existe el atributo cascade, que es el que nos permite integrar éste tipo de restricciones.
Dicho atributo tiene once valores posibles, los cuales se presentan en la siguiente tabla:

Valor Descripción
none No se realiza ninguna acción en los objetos relacionados al hacerlo sobre el principal.
save-update Si se inserta o actualiza el objeto principal también se realizará la inserción o actualización en los
objetos relacionados.
delete Si se borra el objeto principal también se realizará el borrado en los objetos relacionados.
evict Si se llama al método Session.evict(Object objeto) para el objeto principal también se
llamará para los objetos relacionados.
lock Si se llama al método LockRequest.lock(Object objeto) para el objeto principal
también se llamará para los objetos relacionados.
merge Si se llama al método Session.merge(Object objeto) con el objeto principal también se
llamará para los objetos relacionados.
refresh Si se llama al método Session.refresh(Object objeto) para el objeto principal también
se llamará para los objetos relacionados.
replicate Si se llama al método Session.replicate(Object objeto,ReplicationMode
replicationMode) para el objeto principal también se llamará para los objetos relacionados.
all Si se realiza cualquiera de las anteriores acciones sobre el objeto principal también se realizará
sobre los objetos relacionados.
delete- Este atributo sólo se usa si el objeto relacionado es una colección. Indica que si en la colección del
orphan objeto principal eliminamos un elemento , al persistir el objeto principal deberemos borrar de la
base de datos el elemento de la colección que habíamos eliminado.
all-delete- Es la unión de los atributos all y delete-orphan.
orphan

De todos los valores los realmente importantes, ya que incluyen la funcionalidad de trabajar con los objetos
relacionados, son los 4 siguientes:

 none
 save-update
 delete
 delete-orphan

Los siguientes 5 valores generalmente querremos incluirlos ya que no son peligrosos si los incluimos aunque
al hacerlo se podrían lanzar más SQL contra la base de datos:

 evict
 lock
 merge
 refresh
 replicate

Los siguientes 2 valores son agrupaciones de los 9 anteriores:

 all
 all-delete-orphan

76
Así que lo normal es que el atributo cascade tome alguno de los siguientes valores:

 none
 all
 all-delete-orphan
 save-update,evict,lock,merge,refresh,replicate

Y ahora diréis... ¿Y esos métodos que aparecen en la tabla? Pues nada, un poco de teoría.
Los objetos que persistimos con Hibernate tienen un estado. Aparentemente no tiene mucho sentido ya que
no hay ninguna propiedad estado en nuestras clases. Sin embargo el siguiente diagrama muestra los estados
de un objeto respecto de Hibernate:

Veamos ahora en qué consisten cada uno de los 4 estados:

 Transitorio (Transient): Un objeto estará en estado Transitorio cuando acaba de ser creado en Java
mediante el operador new. Es decir cuando esté recién creado por nosotros. Este estado tiene la
característica de que hibernate no sabe nada de nuestro objeto. Quizás el objeto ya esté guardado
en base de datos o sea nuevo y tengamos que insertarlo.
 Persistido (Persistent): Un objeto estará en estado Persistido cuando ya está guardado en la base de
datos y además Hibernate también es consciente de ello. La diferencia con el estado anterior radica
en el que el objeto podía estar persistido pero Hibernate lo desconocía. Hibernate en ese caso
guarda el objeto en la cache interna que posee. También es importante destacar que para una
misma fila de la base de datos sólo puede haber un único objeto en estado Persistido.
 Despegado (Detached): Este estado es similar al estado Transitorio sólo que se produce cuando
cerramos la sesión mediante Session.close() o llamamos al método Session.evict(Object objeto) para el
objeto que queremos pasar a este estado. En ese caso Hibernate vuelve a olvidar en qué estado se
encontraban los objetos borrándolo de su caché interna.
 Removido (Removed): A este estado pasan los objetos que se han borrado de la base de datos
mediante el método delete().

77
 Session.evict(Object objeto): Hace que un objeto en estado Persistido pase al estado Despegado y
lo borre de su caché.
 Session.merge(Object objeto): Hace que cualquier objeto pase a estar en el estado Persistido. En
caso de que el objeto está en la base de datos , lo leerá de ella. Si el objeto es nuevo , lo marcará
como pendiente de guardar en la base de datos. Este método se suele usar cuando tenemos un
objeto que no estaba controlado por Hibernate para que vuelva a estar bajo su control. Es
importante destacar que el objeto que le pasamos como parámetro no tendrá el estado Persistido
sino que es el objeto que nos devuelve el que estará en estado Persistido.
 Session.refresh(Object objeto): Es similar a Session.merge(Object objeto) pero sólo se puede usar si
el objeto ya existe en la base de datos. Se suele usar para recargar el estado del objeto porque ha
sido modificado en la base de datos por algún trigger o similar.
 LockRequest.lock(Object objeto): No modifica el estado interno del objeto pero sí que permite hacer
un SELECT ... FOR UPDATE contra la base de datos.
 Session.replicate(Object objeto,ReplicationMode replicationMode): Se usa para copiar un objeto
de una base de datos a otra usando distintas sesiones contra distintas bases de datos. En este curso
no vamos a ver cómo copiar datos entre distintas bases de datos.

Dicho esto, continuamos con la implementación de las clases. Os dejo una captura de cómo llevaríamos la
estructura de nuestros paquetes java dentro de nuestro proyecto. Veréis las clases que hemos tenido que
crear y sólo crear, aunque aún no las hallamos desarrollado:

78
'pitufosapp/src/main/java/com/guarderias/pitufos/dominio/Cursos.java':
package com.guarderias.pitufos.dominio;

import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;

/**
* @author Julio Bellón Aguilera. Enero 2014.
*/
@Entity(name="Cursos")
@Table(name="cursos")
public class Cursos implements Serializable {

private static final long serialVersionUID = 1L;

@Id
@Column(name = "idcurso", nullable=false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idcurso;

@Column(name="curso")
private String curso;

@OneToMany(fetch=FetchType.EAGER, mappedBy="curso", cascade=CascadeType.ALL)


@Fetch(value = FetchMode.SUBSELECT)
private List<Alumnos> alumnosCurso;

@OneToMany(fetch=FetchType.EAGER, mappedBy="curso", cascade=CascadeType.ALL)


@Fetch(value = FetchMode.SUBSELECT)
private List<Profesores> profesoresCurso;

79
public Cursos() {}
public Cursos(Integer idcurso, String curso,
List<Alumnos> alumnosCurso, List<Profesores> profesoresCurso) {
this.idcurso = idcurso;
this.curso = curso;
this.alumnosCurso = alumnosCurso;
this.profesoresCurso = profesoresCurso;
}
/**
* @return el idcurso
*/
public Integer getIdcurso() {
return idcurso;
}

/**
* @return el curso
*/
public String getCurso() {
return curso;
}

/**
* @return el alumnosCurso
*/
public List<Alumnos> getAlumnosCurso() {
return alumnosCurso;
}

/**
* @return el profesoresCurso
*/
public List<Profesores> getProfesoresCurso() {
return profesoresCurso;
}
/**
* @param idcurso el idcurso a establecer
*/
public void setIdcurso(Integer idcurso) {
this.idcurso = idcurso;
}

80
/**
* @param curso el curso a establecer
*/
public void setCurso(String curso) {
this.curso = curso;
}

/**
* @param alumnosCurso el alumnosCurso a establecer
*/
public void setAlumnosCurso(List<Alumnos> alumnosCurso) {
this.alumnosCurso = alumnosCurso;
}

/**
* @param profesoresCurso el profesoresCurso a establecer
*/
public void setProfesoresCurso(List<Profesores> profesoresCurso) {
this.profesoresCurso = profesoresCurso;
}

/**
* Sobrescitura del método toString heredado de la clase Object
* Lo podemos modificar a nuestro gusto para mostrar los datos convenientemente
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("Cursos [idcurso=%s, curso=%s]", idcurso,
curso);
}
}

81
'pitufosapp/src/main/java/com/guarderias/pitufos/dominio/Pmts.java':
package com.guarderias.pitufos.dominio;

import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
/**
* @author Julio Bellón Aguilera. Enero 2014.
*/
@Entity(name="Pmts")
@Table(name="pmt")
public class Pmts implements Serializable {

private static final long serialVersionUID = 1L;

@Id
@Column(name = "idpmt", nullable=false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idpmt;

@Column(name="nombre")
private String nombre;

@Column(name="apellidos")
private String apellidos;

@OneToMany(fetch=FetchType.EAGER, mappedBy="pmt", cascade=CascadeType.ALL)


@Fetch(value = FetchMode.SUBSELECT)
private List<Alumnos> alumnosPmt;

public Pmts() {}

82
public Pmts(Integer idpmt, String nombre, String apellidos, List<Alumnos> alumnosPmt) {
this.idpmt = idpmt;
this.nombre = nombre;
this.apellidos = apellidos;
this.alumnosPmt = alumnosPmt;
}

/**
* @return el idpmt
*/
public Integer getIdpmt() {
return idpmt;
}

/**
* @return el nombre
*/
public String getNombre() {
return nombre;
}

/**
* @return el apellidos
*/
public String getApellidos() {
return apellidos;
}

/**
* @return el alumnosPmt
*/
public List<Alumnos> getAlumnosPmt() {
return alumnosPmt;
}

/**
* @param idpmt el idpmt a establecer
*/
public void setIdpmt(Integer idpmt) {
this.idpmt = idpmt;
}

83
/**
* @param nombre el nombre a establecer
*/
public void setNombre(String nombre) {
this.nombre = nombre;
}

/**
* @param apellidos el apellidos a establecer
*/
public void setApellidos(String apellidos) {
this.apellidos = apellidos;
}

/**
* @param alumnosPmt el alumnosPmt a establecer
*/
public void setAlumnosPmt(List<Alumnos> alumnosPmt) {
this.alumnosPmt = alumnosPmt;
}

/**
* Sobrescitura del método toString heredado de la clase Object
* Lo podemos modificar a nuestro gusto para mostrar los datos convenientemente
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format(
"Pmts [idpmt=%s, nombre=%s, apellidos=%s]",
idpmt, nombre, apellidos);
}
}

84
'pitufosapp/src/main/java/com/guarderias/pitufos/dominio/Profesores.java':
package com.guarderias.pitufos.dominio;

import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
/**
* @author Julio Bellón Aguilera. Enero 2014.
*/
@Entity(name="Profesores")
@Table(name="profesores")
public class Profesores implements Serializable {

private static final long serialVersionUID = 1L;

@Id
@Column(name = "idprofesor", nullable=false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idprofesor;

@Column(name="nombre")
private String nombre;

@Column(name="apellidos")
private String apellidos;

@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name="idcentro", nullable=false)
private Centros centro;

85
@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name="idcurso", nullable=false)
private Cursos curso;

/*
* @ManyToMany:Indica que la propiedad contiene una lista de objetos que participan en una r elación
* Muchos a Muchos.
* mappedBy: Contiene el nombre de la propiedad Java de la otra clase desde la cual se relaciona
* con ésta. En nuestro ejemplo es la propiedad profesores de la clase Roles.java
*/
@ManyToMany(fetch=FetchType.EAGER, mappedBy="profesores", cascade=CascadeType.ALL)
@Fetch(value = FetchMode.SUBSELECT)
private List<Roles> roles;

public Profesores() {}

public Profesores(Integer idprofesor, String nombre, String apellidos,


Centros centro, Cursos curso, List<Roles> roles) {
this.idprofesor = idprofesor;
this.nombre = nombre;
this.apellidos = apellidos;
this.centro = centro;
this.curso = curso;
this.roles = roles;
}

/**
* @return el idprofesor
*/
public Integer getIdprofesor() {
return idprofesor;
}

/**
* @return el nombre
*/
public String getNombre() {
return nombre;
}

86
/**
* @return el apellidos
*/
public String getApellidos() {
return apellidos;
}

/**
* @return el centro
*/
public Centros getCentro() {
return centro;
}

/**
* @return el curso
*/
public Cursos getCurso() {
return curso;
}

/**
* @return el roles
*/
public List<Roles> getRoles() {
return roles;
}

/**
* @param idprofesor el idprofesor a establecer
*/
public void setIdprofesor(Integer idprofesor) {
this.idprofesor = idprofesor;
}

/**
* @param nombre el nombre a establecer
*/
public void setNombre(String nombre) {
this.nombre = nombre;
}

87
/**
* @param apellidos el apellidos a establecer
*/
public void setApellidos(String apellidos) {
this.apellidos = apellidos;
}

/**
* @param centro el centro a establecer
*/
public void setCentro(Centros centro) {
this.centro = centro;
}

/**
* @param curso el curso a establecer
*/
public void setCurso(Cursos curso) {
this.curso = curso;
}

/**
* @param roles el roles a establecer
*/
public void setRoles(List<Roles> roles) {
this.roles = roles;
}

/**
* Sobrescitura del método toString heredado de la clase Object
* Lo podemos modificar a nuestro gusto para mostrar los datos convenientemente
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String
.format("Profesores [idprofesor=%s, nombre=%s, apellidos=%s]",
idprofesor, nombre, apellidos);
}
}

88
A continuación vamos a implementar las siguientes clases entidad, siguiendo el orden listado a continuación:
1. Usuarios.java
2. Roles.java
3. Calificaciones.java
4. Fechas.java
5. Observaciones.java
6. Actividades.java

Podréis comprobar que no incluimos las clases que corresponderían a las tablas roles_profesores, actividades_profesores y actividades_alumnos. Esto es así,
porque son tablas que definen relaciones Muchos a Muchos, las cuales son definidas dentro de las clases implicadas en la relación.

'pitufosapp/src/main/java/com/guarderias/pitufos/dominio/Usuarios.java':
package com.guarderias.pitufos.dominio;

import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;

/**
* @author Julio Bellón Aguilera. Enero 2014.
*/
@Entity(name="Usuarios")
@Table(name="usuarios")
public class Usuarios implements Serializable {

private static final long serialVersionUID = 1L;

89
@Id
@Column(name = "idusuario", nullable=false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idusuario;

@Column(name="usuario")
private String usuario;

@Column(name="password")
private String password;

@Column(name="enabled")
private Boolean enabled;

@OneToMany(fetch=FetchType.EAGER, mappedBy="usuario", cascade=CascadeType.ALL)


@Fetch(value = FetchMode.SUBSELECT)
private List<Roles> rolesUsuario;

public Usuarios() {}

public Usuarios(Integer idusuario, String usuario, String password,


Boolean enabled, List<Roles> rolesUsuario) {
this.idusuario = idusuario;
this.usuario = usuario;
this.password = password;
this.enabled = enabled;
this.rolesUsuario = rolesUsuario;
}
/**
* @return el idusuario
*/
public Integer getIdusuario() {
return idusuario;
}

/**
* @return el usuario
*/
public String getUsuario() {
return usuario;
}

90
/**
* @return el password
*/
public String getPassword() {
return password;
}

/**
* @return el enabled
*/
public Boolean getEnabled() {
return enabled;
}

/**
* @return el rolesUsuario
*/
public List<Roles> getRolesUsuario() {
return rolesUsuario;
}

/**
* @param idusuario el idusuario a establecer
*/
public void setIdusuario(Integer idusuario) {
this.idusuario = idusuario;
}

/**
* @param usuario el usuario a establecer
*/
public void setUsuario(String usuario) {
this.usuario = usuario;
}

/**
* @param password el password a establecer
*/
public void setPassword(String password) {
this.password = password;
}

91
/**
* @param enabled el enabled a establecer
*/
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}

/**
* @param rolesUsuario el rolesUsuario a establecer
*/
public void setRolesUsuario(List<Roles> rolesUsuario) {
this.rolesUsuario = rolesUsuario;
}

/**
* Sobrescitura del método toString heredado de la clase Object
* Lo podemos modificar a nuestro gusto para mostrar los datos convenientemente
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("Usuarios [idusuario=%s, usuario=%s, enabled=%s]",
idusuario, usuario, enabled);
}

'pitufosapp/src/main/java/com/guarderias/pitufos/dominio/Roles.java':
package com.guarderias.pitufos.dominio;

import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;

92
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;

/**
* @author Julio Bellón Aguilera. Enero 2014.
*/
@Entity(name="Roles")
@Table(name="roles")
public class Roles implements Serializable {

private static final long serialVersionUID = 1L;

@Id
@Column(name = "idrol", nullable=false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idrol;

@Column(name="rol")
private String rol;

@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name="idusuario", nullable=false)
private Usuarios usuario;

/*
* Las siguientes anotaciones significan lo siguiente:
* @ManyToMany:Como su nombre indica le dice a Hibernate que la propiedad contendrá
* una lista de objetos que participa en una relación Muchos a Muchos.
* @Fetch(value = FetchMode.SUBSELECT): Que la consulta será de tipo SUBSELECT.
* @JoinTable: Esta anotación contiene la información sobre la tabla que realiza la relación
* name: Nombre de la tabla que realiza la relación. En nuestro ejemplo es roles_profesores.
* joinColumns: Contiene cada una de las columnas que forman la clave primaria de la clase
* que estamos definiendo. Cada columna se indica mediante una anotación @JoinColumn
* y el atributo name contiene el nombre de la columna.
* inverseJoinColumns: Contiene cada una de las columnas que forman la clave primaria
* de la clase clase con la que tenemos la relación.
*/

93
@ManyToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
@Fetch(value = FetchMode.SUBSELECT)
@JoinTable(name="roles_profesores", joinColumns={
@JoinColumn(name="idrol", nullable=false, updatable=false)},
inverseJoinColumns={
@JoinColumn(name="idprofesor", nullable=false, updatable=false)})
private List<Profesores> profesores;

public Roles() {}

public Roles(Integer idrol, String rol, Usuarios usuario,


List<Profesores> profesores) {
this.idrol = idrol;
this.rol = rol;
this.usuario = usuario;
this.profesores = profesores;
}

/**
* @return el idrol
*/
public Integer getIdrol() {
return idrol;
}

/**
* @return el rol
*/
public String getRol() {
return rol;
}

/**
* @return el usuario
*/
public Usuarios getUsuario() {
return usuario;
}

94
/**
* @return el profesores
*/
public List<Profesores> getProfesores() {
return profesores;
}

/**
* @param idrol el idrol a establecer
*/
public void setIdrol(Integer idrol) {
this.idrol = idrol;
}

/**
* @param rol el rol a establecer
*/
public void setRol(String rol) {
this.rol = rol;
}

/**
* @param usuario el usuario a establecer
*/
public void setUsuario(Usuarios usuario) {
this.usuario = usuario;
}

/**
* @param profesores el profesores a establecer
*/
public void setProfesores(List<Profesores> profesores) {
this.profesores = profesores;
}

95
/**
* Sobrescitura del método toString heredado de la clase Object
* Lo podemos modificar a nuestro gusto para mostrar los datos convenientemente
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("Roles [idrol=%s, rol=%s]", idrol, rol);
}

'pitufosapp/src/main/java/com/guarderias/pitufos/dominio/Calificaciones.java':
package com.guarderias.pitufos.dominio;

import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;

/**
* @author Julio Bellón Aguilera. Enero 2014.
*/
@Entity(name="Calificaciones")
@Table(name="calificaciones")
public class Calificaciones implements Serializable {

private static final long serialVersionUID = 1L;

96
@Id
@Column(name = "idcalificacion", nullable=false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idcalificacion;

@Column(name="calificacion")
private String calificacion;

@OneToMany(fetch=FetchType.EAGER, mappedBy="calificacion", cascade=CascadeType.ALL)


@Fetch(value = FetchMode.SUBSELECT)
private List<Actividades> actividadesCalificacion;

public Calificaciones() {}

public Calificaciones(Integer idcalificacion, String calificacion,


List<Actividades> actividadesCalificacion) {
this.idcalificacion = idcalificacion;
this.calificacion = calificacion;
this.actividadesCalificacion = actividadesCalificacion;
}

/**
* @return el idcalificacion
*/
public Integer getIdcalificacion() {
return idcalificacion;
}

/**
* @return el calificacion
*/
public String getCalificacion() {
return calificacion;
}

/**
* @return el actividadesCalificacion
*/
public List<Actividades> getActividadesCalificacion() {
return actividadesCalificacion;
}

97
/**
* @param idcalificacion el idcalificacion a establecer
*/
public void setIdcalificacion(Integer idcalificacion) {
this.idcalificacion = idcalificacion;
}

/**
* @param calificacion el calificacion a establecer
*/
public void setCalificacion(String calificacion) {
this.calificacion = calificacion;
}

/**
* @param actividadesCalificacion el actividadesCalificacion a establecer
*/
public void setActividadesCalificacion(List<Actividades> actividadesCalificacion) {
this.actividadesCalificacion = actividadesCalificacion;
}

/**
* Sobrescitura del método toString heredado de la clase Object
* Lo podemos modificar a nuestro gusto para mostrar los datos convenientemente
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String
.format("Calificaciones [idcalificacion=%s, calificacion=%s]",
idcalificacion, calificacion);
}
}

98
'pitufosapp/src/main/java/com/guarderias/pitufos/dominio/Fechas.java':
package com.guarderias.pitufos.dominio;

import java.io.Serializable;
import java.sql.Date;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;

/**
* @author Julio Bellón Aguilera. Enero 2014.
*/
@Entity(name="Fechas")
@Table(name="fechas")
public class Fechas implements Serializable {

private static final long serialVersionUID = 1L;

@Id
@Column(name = "idfecha", nullable=false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idfecha;

@Column(name="fecha")
private String fecha;

@OneToMany(fetch=FetchType.EAGER, mappedBy="fecha", cascade=CascadeType.ALL)


@Fetch(value = FetchMode.SUBSELECT)
private List<Actividades> fechasEjecucion;

public Fechas() {}

99
public Fechas(Integer idfecha, Date fecha,
List<Actividades> fechasEjecucion) {
this.idfecha = idfecha;
this.fecha = fecha;
this.fechasEjecucion = fechasEjecucion;
}

/**
* @return el idfecha
*/
public Integer getIdfecha() {
return idfecha;
}

/**
* @return el fecha
*/
public Date getFecha() {
return fecha;
}

/**
* @return el fechasEjecucion
*/
public List<Actividades> getFechasEjecucion() {
return fechasEjecucion;
}

/**
* @param idfecha el idfecha a establecer
*/
public void setIdfecha(Integer idfecha) {
this.idfecha = idfecha;
}

/**
* @param fecha el fecha a establecer
*/
public void setFecha(Date fecha) {
this.fecha = fecha;
}

100
/**
* @param fechasEjecucion el fechasEjecucion a establecer
*/
public void setFechasEjecucion(List<Actividades> fechasEjecucion) {
this.fechasEjecucion = fechasEjecucion;
}

/**
* Sobrescitura del método toString heredado de la clase Object
* Lo podemos modificar a nuestro gusto para mostrar los datos convenientemente
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format(
"Fechas [idfecha=%s, fecha=%s]", idfecha, fecha);
}
}

'pitufosapp/src/main/java/com/guarderias/pitufos/dominio/Observaciones.java':
package com.guarderias.pitufos.dominio;

import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;

101
/**
* @author Julio Bellón Aguilera. Enero 2014.
*/
@Entity(name="Observaciones")
@Table(name="observaciones")
public class Observaciones implements Serializable {

private static final long serialVersionUID = 1L;

@Id
@Column(name = "idobservacion", nullable=false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idobservacion;

@Column(name="observacion")
private String observacion;

@OneToMany(fetch=FetchType.EAGER, mappedBy="observacion", cascade=CascadeType.ALL)


@Fetch(value = FetchMode.SUBSELECT)
private List<Actividades> observacionesActividades;

public Observaciones() {}

public Observaciones(Integer idobservacion, String observacion,


List<Actividades> observacionesActividades) {
this.idobservacion = idobservacion;
this.observacion = observacion;
this.observacionesActividades = observacionesActividades;
}
/**
* @return el idobservacion
*/
public Integer getIdobservacion() {
return idobservacion;
}
/**
* @return el observacion
*/
public String getObservacion() {
return observacion;
}

102
/**
* @return el observacionesActividades
*/
public List<Actividades> getObservacionesActividades() {
return observacionesActividades;
}

/**
* @param idobservacion el idobservacion a establecer
*/
public void setIdobservacion(Integer idobservacion) {
this.idobservacion = idobservacion;
}

/**
* @param observacion el observacion a establecer
*/
public void setObservacion(String observacion) {
this.observacion = observacion;
}

/**
* @param observacionesActividades el observacionesActividades a establecer
*/
public void setObservacionesActividades(
List<Actividades> observacionesActividades) {
this.observacionesActividades = observacionesActividades;
}

/**
* Sobrescitura del método toString heredado de la clase Object
* Lo podemos modificar a nuestro gusto para mostrar los datos convenientemente
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String
.format("Observaciones [idobservacion=%s, observacion=%s]",
idobservacion, observacion);
}
}

103
'pitufosapp/src/main/java/com/guarderias/pitufos/dominio/Actividades.java':
package com.guarderias.pitufos.dominio;

import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;

/**
* @author Julio Bellón Aguilera. Enero 2014.
*/
@Entity(name="Actividades")
@Table(name="actividades")
public class Actividades implements Serializable {

private static final long serialVersionUID = 1L;

@Id
@Column(name = "idactividad", nullable=false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idactividad;

@Column(name="denominacion")
private String denominacion;

@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name="idcalificacion", nullable=false)
private Calificaciones calificacion;

104
@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name="idfechaejecucion", nullable=false)
private Fechas fecha;

@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name="idobservacion", nullable=false)
private Observaciones observacion;

@ManyToMany(fetch=FetchType.EAGER, mappedBy="actividadesProfesores", cascade=CascadeType.ALL)


@Fetch(value = FetchMode.SUBSELECT)
private List<Profesores> profesores;

@ManyToMany(fetch=FetchType.EAGER, mappedBy="actividadesAlumnos", cascade=CascadeType.ALL)


@Fetch(value = FetchMode.SUBSELECT)
private List<Alumnos> alumnos;

public Actividades() {}

public Actividades(Integer idactividad, String denominacion, Calificaciones calificacion, Fechas fecha,


Observaciones observacion, List<Profesores> profesores,
List<Alumnos> alumnos) {
this.idactividad = idactividad;
this.denominacion = denominacion;
this.calificacion = calificacion;
this.fecha = fecha;
this.observacion = observacion;
this.profesores = profesores;
this.alumnos = alumnos;
}
/**
* @return el idactividad
*/
public Integer getIdactividad() {
return idactividad;
}
/**
* @return el denominacion
*/
public String getDenominacion() {
return denominacion;
}

105
/**
* @return el calificacion
*/
public Calificaciones getCalificacion() {
return calificacion;
}

/**
* @return el fecha
*/
public Fechas getFecha() {
return fecha;
}

/**
* @return el observacion
*/
public Observaciones getObservacion() {
return observacion;
}

/**
* @return el profesores
*/
public List<Profesores> getProfesores() {
return profesores;
}

/**
* @return el alumnos
*/
public List<Alumnos> getAlumnos() {
return alumnos;
}

/**
* @param idactividad el idactividad a establecer
*/
public void setIdactividad(Integer idactividad) {
this.idactividad = idactividad;
}

106
/**
* @param denominacion el denominacion a establecer
*/
public void setDenominacion(String denominacion) {
this.denominacion = denominacion;
}

/**
* @param calificacion el calificacion a establecer
*/
public void setCalificacion(Calificaciones calificacion) {
this.calificacion = calificacion;
}

/**
* @param fecha el fecha a establecer
*/
public void setFecha(Fechas fecha) {
this.fecha = fecha;
}

/**
* @param observacion el observacion a establecer
*/
public void setObservacion(Observaciones observacion) {
this.observacion = observacion;
}

/**
* @param profesores el profesores a establecer
*/
public void setProfesores(List<Profesores> profesores) {
this.profesores = profesores;
}

/**
* @param alumnos el alumnos a establecer
*/
public void setAlumnos(List<Alumnos> alumnos) {
this.alumnos = alumnos;
}

107
/**
* Sobrescitura del método toString heredado de la clase Object
* Lo podemos modificar a nuestro gusto para mostrar los datos convenientemente
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String
.format("Actividades [idactividad=%s, denominacion=%s]",
idactividad, denominacion);
}
}

A continuación, debemos insertar las relaciones Muchos a Muchos en las clases Profesores.java y Alumnos.java, que nos faltan.
'pitufosapp/src/main/java/com/guarderias/pitufos/dominio/Alumnos.java':
..............Entre las importaciones..........
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
..............Entre la declaración de variables..........
@ManyToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
@Fetch(value = FetchMode.SUBSELECT)
@JoinTable(name="actividades_alumnos", joinColumns={
@JoinColumn(name="idactividad", nullable=false, updatable=false)},
inverseJoinColumns={
@JoinColumn(name="idalumno", nullable=false, updatable=false)})
private List<Actividades> actividadesAlumnos;
..............En el constructor..........
* @param pmt - El padre, madre o tutor del alumno
* @param actividadesAlumnos - Lista de actividades del alumno
*/
public Alumnos(Integer idalumno, String nombre, String apellidos,
Centros centro, Cursos curso, Pmts pmt, List<Actividades> actividadesAlumnos) {
this.idalumno = idalumno;
this.nombre = nombre;
this.apellidos = apellidos;
this.centro = centro;
this.curso = curso;
this.pmt = pmt;

108
this.actividadesAlumnos = actividadesAlumnos;
}
..............Al final de los Getters..........
/**
* @return el actividadesAlumnos
*/
public List<Actividades> getActividadesAlumnos() {
return actividadesAlumnos;
}
..............Al final de los Setters..........
/**
* @param actividadesAlumnos el actividadesAlumnos a establecer
*/
public void setActividadesAlumnos(List<Actividades> actividadesAlumnos) {
this.actividadesAlumnos = actividadesAlumnos;
}

'pitufosapp/src/main/java/com/guarderias/pitufos/dominio/Profesores.java':
..............Entre las importaciones..........
import javax.persistence.JoinTable;
..............Entre la declaración de variables..........
@ManyToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
@Fetch(value = FetchMode.SUBSELECT)
@JoinTable(name="actividades_profesores", joinColumns={
@JoinColumn(name="idactividad", nullable=false, updatable=false)},
inverseJoinColumns={
@JoinColumn(name="idprofesor", nullable=false, updatable=false)})
private List<Actividades> actividadesProfesores;..............
..............En el constructor..........
public Profesores(Integer idprofesor, String nombre, String apellidos,
Centros centro, Cursos curso, List<Roles> roles, List<Actividades> actividadesProfesores) {
this.idprofesor = idprofesor;
this.nombre = nombre;
this.apellidos = apellidos;
this.centro = centro;
this.curso = curso;
this.roles = roles;
this.actividadesProfesores = actividadesProfesores;
}

109
..............Al final de los Getters..........
/**
* @return el actividadesProfesores
*/
public List<Actividades> getActividadesProfesores() {
return actividadesProfesores;
}
..............Al final de los Setters..........
/**
* @param actividadesProfesores el actividadesProfesores a establecer
*/
public void setActividadesProfesores(List<Actividades> actividadesProfesores) {
this.actividadesProfesores = actividadesProfesores;
}

110
A continuación, os dejo una captura de cómo deberíais tener el paquete dominio de nuestro proyecto, a
éstas alturas:

A través de éstas once clases, junto con las relaciones definidas en ellas y la implementación de nuestras
anotaciones de hibernate, daremos una representación de nuestra base de datos, configurada por las
catorce tablas que creamos con anterioridad.

Las funcionalidades CRUD de nuestro proyecto, las vamos ahora a completar mediante la implementación
del patrón DAO.

Antes de empezar, un poco de teoría, para ilustrarnos acerca de lo que vamos a utilizar.

En software de computadores, un Data Access Object (DAO, Objeto de Acceso a Datos) es un


componente de software que suministra una interfaz común entre la aplicación y uno o más
dispositivos de almacenamiento de datos, tales como una Base de datos o un archivo. El término
se aplica frecuentemente al Patrón de diseño Object [Wikipedia.org].

Un DAO no es más que un adaptador entre la lógica de negocio y la capa de persistencia , que en nuestro
caso está formada por la base de datos y los paquetes dominio y repositorio. Las clases existentes en la capa
dominio, son las clases entidad, o POJO's que representan a las tablas de nuestra base de datos y para el
patrón DAO, a partir de ahora se denominarán, DTO's, que es el acrónimo del término en inglés DATA
TRANSFER OBJECT, que se denota para nombrar al tipo de clases que actúan como mecanismo para
transportar información de un punto a otro y que no tienen comportamiento. O sea, igual que si
transportáramos la información a través de una estructura en XML o JSON (¿vamos viendo ya, para qué nos
sirve la implementación en nuestros DTOs de la interfaz Serializable?, ¿recordáis que servía para "obtener
una secuencia de bytes que represente el estado de dicho objeto." como os anoté en los
comentarios de la clase Alumnos.java?).

111
Pues sí, lo que se trata aquí, es de independizar la capa de persistencia del resto de código, dejando el
mismo con un nivel de acoplamiento lo más bajo posible. Nuestra aplicación debe conseguir los datos o ser
capaz de guardarlos en algún sitio, pero no tiene por qué saber de dónde los está sacando o dónde se
guardan.

Esto pasa, por ejemplo, cuando en nuestra aplicación tenemos diversas fuentes de datos de donde tenemos
que extraerlos para trabajar con ellos, por ejemplo; Si tenemos un escenario donde las fuentes de datos son
archivos TXT (texto), archivos XLS (Excel) o archivos PDF (Adobe) y una base de datos relacional en MySQL:

Imagina que te encargan actualizar una aplicación, por ejemplo, en la que hay que MIGRAR toda
la información antígua, referente a los usuarios, la cual, se encuentra en ficheros de texto y
hojas de cálculo de excel a la nueva aplicación donde se va a realizar el almacenamiento en una
base de datos relacional en MySQL.

Pues sencillo, implementaríamos nuestro patrón DAO para cada una de las fuentes de datos, y
solucionaríamos el problema cómodamente, por ejemplo, estableciendo una factoría de DAOs que nos
resolvería cuál de ellos habría que utilizar, estableciéndolo para cada fuente de datos, implementándolo, por
último, de forma común a todos ellos a través de una interfaz. La aplicación nueva, no sabe de dónde o cómo
se sacan los datos con los que trabaja y la hora de almacenarlos, lo haríamos sólo en la bd, consiguiendo
nuestro objetivo de una forma clara, transparente, rápida y sencilla:

112
En éste breve ejemplo, se ha usado también el patrón Factory Method, pero el estudio de
patrones está fuera del ámbito de éste curso.

Para utilizar el patrón DAO en nuestra aplicación, debemos realizar los siguientes pasos:

1) Crear los DTOs. Hecho; los creamos cuando definimos los POJOs o clases entidad, en el
paquete dominio.
2) Crear las interfaces DAO e implementarlas.
3) Usar las implementaciones de nuestras interfaces DAO en la lógica de negocio.

El primer paso de la implementación ya lo hemos realizado. Se trataba de realizar los TOs. El segundo paso
consiste en crear las interfaces. Esta interfaz debe ser específica para cada clase, pues debe contener los
métodos necesarios para recuperar y guardar los objetos en las tablas de la base de datos. El tercer paso, lo
implementaremos en el siguiente apartado, cuando desarrollemos la lógica de negocio.

Vamos a desarrollar ahora el segundo paso. Crearemos una interfaz común que todos nuestros DAOs
implementarán, la interfaz BaseDao.java, y una implementación de la misma usando Hibernate, la clase
BaseDaoHibernate.java, que todos extenderán (heredarán); de ésta forma, os voy a enseñar una manera de
hacerlo todo, lo más cómodo y lo menos tedioso posible, usando a la vez, otra característica de Java: Los
tipos genéricos, o más conocida como Generics, que resumo a continuación:

Generics are a facility of generic programming that were added to the Java programming
language in 2004 within J2SE 5.0. They allow "a type or method to operate on objects of various
types while providing compile-time type safety." [Wikipedia.org].

Resumiendo, una característica que permite que a un sólo método, se le puedan pasar objetos de tipos
diferentes y operar con ellos.

¿De qué nos va a servir? pues para hacer unas interfaces genéricas y ahorrarnos una gran cantidad de código
a la hora de implementarlo para realizar las operaciones contra la bd a través de hibernate, además de
modularizar más la aplicación.

Las clases las vamos a crear en el paquete repositorio, en nuestra capa de Persistencia.

113
'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/BaseDao.java':
package com.guarderias.pitufos.repositorio;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.hibernate.criterion.DetachedCriteria;

public interface BaseDao<T extends Serializable, E> {


public void deleteAll(Collection<T> instances) throws Exception;
public int bulkUpdate(String query) throws Exception;
public E save(T instance) throws Exception;
public void saveOrUpdateAll(Collection<T> instances) throws Exception;
public void saveOrUpdate(T instance) throws Exception;
public void persist(T transientInstance) throws Exception;
public void attachDirty(T instance) throws Exception;
public void attachClean(T instance) throws Exception;
public void delete(T persistentInstance) throws Exception;
public List<T> findByExample(T instance) throws Exception;
public List<T> findByQuery(String query) throws Exception;
public List<Map<String, Object>> findMapByQuery(String queryString) throws Exception;
public List<T> findByCriteria(DetachedCriteria criteria) throws Exception;
public T merge(T detachedInstance) throws Exception;
public List<T> findAll() throws Exception;
public T findById(E id) throws Exception;
}

La interfaz BaseDao.java, implementa operaciones típicas de consulta, actualización, eliminación y creación de datos. Su acrónimo en inglés son las siglas
CRUD (Create Read Update Delete), para aclarar un poco los conceptos. Veréis los tipos de generics, T y E: T, es el tipo del objeto mientras que E es el tipo
del identificador. T es una clase java, un entity de Hibernate, que en nuestro caso hemos implementado en el primer paso.

La clase BaseDaoHibernate.java, es una implementación abstracta por defecto, para trabajar con JPA e Hibernate donde implementamos las operaciones
de BaseDao.java. Completaremos las clases hijas con sus propios métodos de búsqueda. La comentaré después del código.

114
'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/BaseDaoHibernate.java':
package com.guarderias.pitufos.repositorio;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.LockMode;
import org.hibernate.SessionFactory;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.criterion.DetachedCriteria;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.transaction.annotation.Transactional;

public abstract class BaseDaoHibernate<T extends Serializable, E> extends HibernateDaoSupport implements BaseDao<T, E> {

protected final Log logger = LogFactory.getLog(getClass());

@Autowired
public void init(SessionFactory factory) {
Dialect dialect = ((SessionFactoryImplementor) factory).getDialect();
logger.info("Inyectando el SessionFactory. Dialecto usado: " + dialect);
setSessionFactory(factory);
}
@Transactional(readOnly=false)
public void deleteAll(final Collection<T> instances) throws Exception {
try {
getHibernateTemplate().deleteAll(instances);
} catch (final Exception e) {
throw e;
}
}
@Transactional(readOnly=false)
public int bulkUpdate(final String query) throws Exception {
try {
return getHibernateTemplate().bulkUpdate(query);

115
} catch (final Exception e) {
throw e;
}
}
@Transactional(readOnly=false)
@SuppressWarnings("unchecked")
public E save(final T instance) throws Exception {
try {
return (E) getHibernateTemplate().save(instance);
} catch (final Exception e) {
throw e;
}
}
@Transactional(readOnly=false)
public void saveOrUpdateAll(final Collection<T> instances) throws Exception {
try {
getHibernateTemplate().saveOrUpdateAll(instances);
} catch (final Exception e) {
throw e;
}
}
@Transactional(readOnly=false)
public void saveOrUpdate(final T instance) throws Exception {
try {
getHibernateTemplate().saveOrUpdate(instance);
} catch (final Exception e) {
throw e;
}
}
@Transactional(readOnly=false)
public void persist(final T transientInstance) throws Exception {
try {
getHibernateTemplate().persist(transientInstance);
} catch (final Exception e) {
throw e;
}
}
@Transactional(readOnly=false)
public void attachDirty(final T instance) throws Exception {
try {
getHibernateTemplate().saveOrUpdate(instance);

116
} catch (final Exception e) {
throw e;
}
}
@Transactional(readOnly=false)
public void attachClean(final T instance) throws Exception {
try {
getHibernateTemplate().lock(instance, LockMode.NONE);
} catch (final Exception e) {
throw e;
}
}
@Transactional(readOnly=false)
public void delete(final T persistentInstance) throws Exception {
try {
getHibernateTemplate().delete(persistentInstance);
} catch (final Exception e) {
throw e;
}
}
@Transactional(readOnly=false)
public T merge(final T detachedInstance) throws Exception {
try {
final T result = getHibernateTemplate().merge(detachedInstance);
return result;
} catch (final Exception e) {
throw e;
}
}
@Transactional(readOnly=true)
@SuppressWarnings("unchecked")
public List<T> findByExample(final T instance) throws Exception {
try {
final List<T> results = getHibernateTemplate().findByExample(instance);
return results;
} catch (final Exception e) {
throw e;
}
}
@Transactional(readOnly=true)
@SuppressWarnings("unchecked")

117
public List<T> findByQuery(final String queryString) throws Exception {
try {
final List<T> results = getHibernateTemplate().find(queryString);
return results;
} catch (final Exception e) {
throw e;
}
}
@Transactional(readOnly=true)
@SuppressWarnings("unchecked")
public List<Map<String, Object>> findMapByQuery(final String queryString) throws Exception {
try {
final List<Map<String, Object>> results = getHibernateTemplate().find(queryString);
return results;
} catch (final Exception e) {
throw e;
}
}
@Transactional(readOnly=true)
@SuppressWarnings("unchecked")
public List<T> findByCriteria(final DetachedCriteria criteria) throws Exception {
try {
return getHibernateTemplate().findByCriteria(criteria);
} catch (final Exception e) {
throw e;
}
}
public abstract List<T> findAll() throws Exception;
public abstract T findById(E id) throws Exception;
}

¿Que es HibernateDaoSupport? es simplemente una plantilla para crear un DAO que use Hibernate. Nos permite inyectar, en nuestros DAOs, o bien
nuestro SessionFactory de Hibernate o bien una instancia de HibernateTemplate. En éste caso nos viene inyectado por dependencias desde los beans
declarados en root-context.xml para realizar operaciones sobre sus métodos. Sencillo ¿no? Todos los métodos de nuestro DAO son igual, obtener la
plantilla y llamar al método correspondiente. Además se encarga de toda la gestión de la conexión, (apertura, cierre, etcétera). Habréis observado que
existen dos métodos que se implementan en las clases hijas, puestos que los mismos han sido declarados abstract. Esto ha sido así, por una cuestión de
eficiencia y comodidad en el desarrollo del código.

118
A continuación vamos a crear los correspondientes DAOs para nuestro esquema de datos y configuraremos
todas las dependencias con Spring. Empecemos con la clase Alumnos.java:

'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/AlumnosDao.java':
package com.guarderias.pitufos.repositorio;

import com.guarderias.pitufos.dominio.Alumnos;

public interface AlumnosDao extends BaseDao<Alumnos, String> {}

Queremos que nuestro DAO tenga todas las operaciones que especificamos anteriormente en BaseDao, así
que lo extendemos. Mediante generics, especificamos que nuestro DAO es sobre la entidad Alumnos y su id
es de tipo String. En este caso, nuestro DAO no tiene ninguna operación adicional, así que no especificamos
ningún método, sino que sólo tendrá los que por defecto especificamos en BaseDao. Creamos la
implementación de AlumnosDao.java, que denominaremos AlumnosDaoImpl.java, y en la que
implementaremos los métodos findAll y findById heredados de BaseDa a través de nuestra interfaz:

'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/AlumnosDaoImpl.java':
package com.guarderias.pitufos.repositorio;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.guarderias.pitufos.dominio.Alumnos;

/*
* La anotación @Repository, indica que esta clase está relacionada
* con la capa de persistencia y que la misma debe ser un singleton
* o sea, que solo habrá una instancia de la misma para todos los hilos
* de ejecución de spring.
*/
@Repository(value = "alumnosDao")
public class AlumnosDaoImpl extends BaseDaoHibernate<Alumnos, String> implements
AlumnosDao {

protected final Log logger = LogFactory.getLog(getClass());

@Transactional(readOnly=true)
@Override
public List<Alumnos> findAll() throws Exception {
logger.info("Recuperando Alumnos");
return getHibernateTemplate().loadAll(Alumnos.class);
}

@Transactional(readOnly=true)
@Override
public Alumnos findById(String id) throws Exception {
logger.info("Recuperando Alumno");
return getHibernateTemplate().get(Alumnos.class, Integer.parseInt(id));
}
}

119
Fácil, ¿no? imaginaros la cantidad de código que nos acabamos de ahorrar, puesto que ahora, sólo nos
quedan las interfaces Dao y clases DaoImpl de las demás entidades del paquete dominio, que son
exactamente iguales a éstas. Imaginad por un momento, lo tedioso que resultaría implementar para cada
una de las ONCE clases, todo el código contenido en la clase BaseDaoJPA.java y para cada una de ellas su
interfaz específica con todo el código contenido en BaseDao.java. Pues éste es el inconveniente del patrón
DAO, pero con generics, os aseguro que merece la pena implementarlo en vuestras aplicaciones. Además,
aquí sólo hemos hecho uso de una implementación en la que explotamos los recursos que nos ofrece
HibernateDaoSupport e HibernateTemplate. En los vínculos encontraréis más información al respecto.
Os dejo a continuación, las clases restantes para completar nuestra capa de Persistencia:

'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/ActividadesDao.java':
package com.guarderias.pitufos.repositorio;

import com.guarderias.pitufos.dominio.Actividades;

public interface ActividadesDao extends BaseDao<Actividades, String> {}

'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/ActividadesDaoImpl.java':
package com.guarderias.pitufos.repositorio;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.guarderias.pitufos.dominio.Actividades;

@Repository(value = "actividadesDao")
public class ActividadesDaoImpl extends BaseDaoHibernate<Actividades, String> implements
ActividadesDao {

protected final Log logger = LogFactory.getLog(getClass());

@Transactional(readOnly=true)
@Override
public List<Actividades> findAll() throws Exception {
logger.info("Recuperando Actividades");
return getHibernateTemplate().loadAll(Actividades.class);
}

@Transactional(readOnly=true)
@Override
public Actividades findById(String id) throws Exception {
logger.info("Recuperando Actividad");
return getHibernateTemplate().get(Actividades.class, Integer.parseInt(id));
}
}

120
'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/CalificacionesDao.java':
package com.guarderias.pitufos.repositorio;

import com.guarderias.pitufos.dominio.Calificaciones;

public interface CalificacionesDao extends BaseDao<Calificaciones, String> {}

'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/CalificacionesDaoImpl.java':
package com.guarderias.pitufos.repositorio;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.guarderias.pitufos.dominio.Calificaciones;

@Repository(value = "calificacionesDao")
public class CalificacionesDaoImpl extends BaseDaoHibernate<Calificaciones, String>
implements CalificacionesDao {

protected final Log logger = LogFactory.getLog(getClass());

@Transactional(readOnly=true)
@Override
public List<Calificaciones> findAll() throws Exception {
logger.info("Recuperando Calificaciones");
return getHibernateTemplate().loadAll(Calificaciones.class);
}

@Transactional(readOnly=true)
@Override
public Calificaciones findById(String id) throws Exception {
logger.info("Recuperando Calificación");
return getHibernateTemplate().get(Calificaciones.class,
Integer.parseInt(id));
}
}

'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/CentrosDao.java':
package com.guarderias.pitufos.repositorio;

import com.guarderias.pitufos.dominio.Centros;

public interface CentrosDao extends BaseDao<Centros, String> {}

'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/CentrosDaoImpl.java':
package com.guarderias.pitufos.repositorio;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;

121
import org.springframework.transaction.annotation.Transactional;
import com.guarderias.pitufos.dominio.Centros;

@Repository(value = "centrosDao")
public class CentrosDaoImpl extends BaseDaoHibernate<Centros, String> implements
CentrosDao {

protected final Log logger = LogFactory.getLog(getClass());

@Transactional(readOnly=true)
@Override
public List<Centros> findAll() throws Exception {
logger.info("Recuperando Centros");
return getHibernateTemplate().loadAll(Centros.class);
}

@Transactional(readOnly=true)
@Override
public Centros findById(String id) throws Exception {
logger.info("Recuperando Centro");
return getHibernateTemplate().get(Centros.class, Integer.parseInt(id));
}
}

'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/CursosDao.java':
package com.guarderias.pitufos.repositorio;

import com.guarderias.pitufos.dominio.Cursos;

public interface CursosDao extends BaseDao<Cursos, String> {}

'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/CursosDaoImpl.java':
package com.guarderias.pitufos.repositorio;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.guarderias.pitufos.dominio.Cursos;

@Repository(value = "cursosDao")
public class CursosDaoImpl extends BaseDaoHibernate<Cursos, String> implements CursosDao
{

protected final Log logger = LogFactory.getLog(getClass());

@Transactional(readOnly=true)
@Override
public List<Cursos> findAll() throws Exception {
logger.info("Recuperando Cursos");
return getHibernateTemplate().loadAll(Cursos.class);
}

122
@Transactional(readOnly=true)
@Override
public Cursos findById(String id) throws Exception {
logger.info("Recuperando Curso");
return getHibernateTemplate().get(Cursos.class, Integer.parseInt(id));
}
}

'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/FechasDao.java':
package com.guarderias.pitufos.repositorio;

import com.guarderias.pitufos.dominio.Fechas;

public interface FechasDao extends BaseDao<Fechas, String> {}

'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/FechasDaoImpl.java':
package com.guarderias.pitufos.repositorio;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.guarderias.pitufos.dominio.Fechas;

@Repository(value = "fechasDao")
public class FechasDaoImpl extends BaseDaoHibernate<Fechas, String> implements FechasDao
{

protected final Log logger = LogFactory.getLog(getClass());

@Transactional(readOnly=true)
@Override
public List<Fechas> findAll() throws Exception {
logger.info("Recuperando Fechas");
return getHibernateTemplate().loadAll(Fechas.class);
}

@Transactional(readOnly=true)
@Override
public Fechas findById(String id) throws Exception {
logger.info("Recuperando Fecha");
return getHibernateTemplate().get(Fechas.class, Integer.parseInt(id));
}
}

'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/ObservacionesDao.java':
package com.guarderias.pitufos.repositorio;

import com.guarderias.pitufos.dominio.Observaciones;

public interface ObservacionesDao extends BaseDao<Observaciones, String> {}

123
'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/ObservacionesDaoImpl.java':
package com.guarderias.pitufos.repositorio;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.guarderias.pitufos.dominio.Observaciones;

@Repository(value = "observacionesDao")
public class ObservacionesDaoImpl extends BaseDaoHibernate<Observaciones, String>
implements ObservacionesDao {

protected final Log logger = LogFactory.getLog(getClass());

@Transactional(readOnly=true)
@Override
public List<Observaciones> findAll() throws Exception {
logger.info("Recuperando Observaciones");
return getHibernateTemplate().loadAll(Observaciones.class);
}

@Transactional(readOnly=true)
@Override
public Observaciones findById(String id) throws Exception {
logger.info("Recuperando Observación");
return getHibernateTemplate().get(Observaciones.class,
Integer.parseInt(id));
}
}

'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/PmtsDao.java':
package com.guarderias.pitufos.repositorio;

import com.guarderias.pitufos.dominio.Pmts;

public interface PmtsDao extends BaseDao<Pmts, String> {}

'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/PmtsDaoImpl.java':
package com.guarderias.pitufos.repositorio;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.guarderias.pitufos.dominio.Pmts;

@Repository(value = "pmtsDao")

124
public class PmtsDaoImpl extends BaseDaoHibernate<Pmts, String> implements PmtsDao {

protected final Log logger = LogFactory.getLog(getClass());

@Transactional(readOnly=true)
@Override
public List<Pmts> findAll() throws Exception {
logger.info("Recuperando Padres/Madres/Tutores");
return getHibernateTemplate().loadAll(Pmts.class);
}

@Transactional(readOnly=true)
@Override
public Pmts findById(String id) throws Exception {
logger.info("Recuperando Padre/Madre/Tutor");
return getHibernateTemplate().get(Pmts.class, Integer.parseInt(id));
}
}

'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/ProfesoresDao.java':
package com.guarderias.pitufos.repositorio;

import com.guarderias.pitufos.dominio.Profesores;

public interface ProfesoresDao extends BaseDao<Profesores, String> {}

'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/ProfesoresDaoImpl.java':
package com.guarderias.pitufos.repositorio;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.guarderias.pitufos.dominio.Profesores;

@Repository(value = "profesoresDao")
public class ProfesoresDaoImpl extends BaseDaoHibernate<Profesores, String> implements
ProfesoresDao {

protected final Log logger = LogFactory.getLog(getClass());

@Transactional(readOnly=true)
@Override
public List<Profesores> findAll() throws Exception {
logger.info("Recuperando Profesores");
return getHibernateTemplate().loadAll(Profesores.class);
}

@Transactional(readOnly=true)
@Override
public Profesores findById(String id) throws Exception {
logger.info("Recuperando Profesor");
return getHibernateTemplate().get(Profesores.class, Integer.parseInt(id));
}
}

125
'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/RolesDao.java':
package com.guarderias.pitufos.repositorio;

import com.guarderias.pitufos.dominio.Roles;

public interface RolesDao extends BaseDao<Roles, String> {}

'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/RolesDaoImpl.java':
package com.guarderias.pitufos.repositorio;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.guarderias.pitufos.dominio.Roles;

@Repository(value = "rolesDao")
public class RolesDaoImpl extends BaseDaoHibernate<Roles, String> implements RolesDao {

protected final Log logger = LogFactory.getLog(getClass());

@Transactional(readOnly=true)
@Override
public List<Roles> findAll() throws Exception {
logger.info("Recuperando Roles");
return getHibernateTemplate().loadAll(Roles.class);
}

@Transactional(readOnly=true)
@Override
public Roles findById(String id) throws Exception {
logger.info("Recuperando Rol");
return getHibernateTemplate().get(Roles.class, Integer.parseInt(id));
}
}

'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/UsuariosDao.java':
package com.guarderias.pitufos.repositorio;

import com.guarderias.pitufos.dominio.Usuarios;

public interface UsuariosDao extends BaseDao<Usuarios, String> {}

'pitufosapp/src/main/java/com/guarderias/pitufos/repositorio/UsuariosDaoImpl.java':
package com.guarderias.pitufos.repositorio;

import java.util.List;

126
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.guarderias.pitufos.dominio.Usuarios;

@Repository(value = "usuariosDao")
public class UsuariosDaoImpl extends BaseDaoHibernate<Usuarios, String> implements
UsuariosDao {

protected final Log logger = LogFactory.getLog(getClass());

@Transactional(readOnly=true)
@Override
public List<Usuarios> findAll() throws Exception {
logger.info("Recuperando Usuarios");
return getHibernateTemplate().loadAll(Usuarios.class);
}

@Transactional(readOnly=true)
@Override
public Usuarios findById(String id) throws Exception {
logger.info("Recuperando Usuario");
return getHibernateTemplate().get(Usuarios.class, Integer.parseInt(id));
}
}

Podréis comprobar que aunque hemos reducido considerablemente la cantidad de código a programar, se
hace tedioso de todos modos, crear cada una de las interfaces y de las clases implementadoras. Imaginaros
cuando operamos con bases de datos de cientos de tablas...

Desarrollando la capa de Aplicación


C omenzamos el desarrollo de la capa de Aplicación, que situaremos en el paquete servicio, donde vamos
a situar la lógica de negocio, completando así, el tercer paso en la utilización del patrón DAO en nuestra
aplicación.

Esta capa va a estar formada por las clases administradoras/gestoras de la información proveniente de la
capa de Persistencia. Son las clases Manager, que serán interfaces que después implementaremos en las
clases ManagerImpl que actuarán como el servicio de datos para la capa de Presentación. También estará
formada por los objetos de modelo, para los que vamos a usar objetos de transferencia de datos DTOs
nuevamente. Más tarde, en la capa de Presentación, dichos DTOs los añadiremos al modelo de datos sobre
el que construiremos nuestras vistas.

Ahora es cuando se aplican las reglas de negocio que hayamos diseñado e inferido del estudio de
implantación de nuestro sistema. Entra en juego, la validación de la información que va a ser introducida en
nuestro aplicativo y tratada por el mismo, por lo que nuestra capa de Aplicación también estará formada por
validadores de datos que actuarán como inspectores de calidad en la mecanización de datos, no
interviniendo en la transferencia de datos entre capas, sino solamente a nivel de aplicación. Estas serán las

127
clases Validator. Nosotros, para éste curso y a modo de ejemplo sólo estableceremos una regla de negocio;
La calificación de un alumno no podrá ser diferente de "Progresa Adecuadamente" o "Necesita Mejorar".

Con todo esto, conseguimos independizar la capa de Presentación de la capa de Persistencia, en la capa de
Aplicación, usando un sólo paquete, dejando nuestro esquema lo más claro posible.

Empezamos el desarrollo desde "abajo" hacia "arriba", es decir, desde lo que va a conectar con la capa de
Persistencia hasta lo que va a conectar con la capa de Presentación. En primer lugar, desarrollaremos las
clases Manager. Las mismas no son más que una interfaz que nos proveerá de métodos, que después serán
implementados por sus respectivas clases ManagerImpl. El objetivo de esto es facilitar la posible necesidad
de aumentar o personalizar las funcionalidades que incorporan nuestras clases, en función de la
particularización a la hora de procesar cada objeto de dominio, o incluso, la extensión de nuestra aplicación,
en caso necesario. Pues dicho esto, comencemos:

'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/AlumnosManager.java':

package com.guarderias.pitufos.servicio;

import java.io.Serializable;
import java.util.List;

import com.guarderias.pitufos.dominio.Actividades;
import com.guarderias.pitufos.dominio.Alumnos;
import com.guarderias.pitufos.dominio.Centros;
import com.guarderias.pitufos.dominio.Cursos;

public interface AlumnosManager extends Serializable {


// Devuelve una lista completa de los Alumnos
public List<Alumnos> leerAlumnos();
// Devuelve una lista de los Alumnos por centro
public List<Alumnos> leerAlumnosPorCentro(Centros centro);
// Devuelve una lista de los Alumnos por curso
public List<Alumnos> leerAlumnosPorCurso(Cursos curso);
// Devuelve una lista de alumnos por actividad
public List<Alumnos> leerAlumnosPorActividad(Actividades actividad);
// Devuelve una lista de actividades por alumno
public List<Actividades> leerActividadesPorAlumno(Alumnos alumno);
// Devuelve un Alumno en concreto
public Alumnos leerAlumno(Integer id);
// Crea un Alumno.
public boolean crearAlumno(Alumnos alumno);
// Actualiza los datos de un Alumno
public boolean actualizarAlumno(Alumnos alumno);
// Borra un Alumno
public boolean borrarAlumno(Alumnos alumno);
}

Los métodos ahora implementados son más legibles. Estamos en la capa de Aplicación y nos interesa tenerlo
todo cada vez más claro, de cara a facilitarnos el desarrollo en la capa de Presentación. También veréis que
aumentamos las funcionalidades de búsqueda de datos en nuestra base de datos, en éste caso, referente a
los alumnos. Gracias a la implementación de nuestro patrón DAO, usando generics en la capa de
Persistencia y nuestra HibernateTemplate, veréis lo sencillo que nos va a resultar desarrollar todos estos
métodos añadidos.

128
Realizamos ahora la implementación de nuestra clase gestora. Los métodos que implementamos de la interfaz van a ser desarrollados, delegando los
mismos en su clase DaoImpl correspondiente y haciendo uso incluso de los getters de las clases entidad, así, desacoplaremos lo máximo posible el código
de cara a la capa de Presentación y al paquete web:

'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/AlumnosManagerImpl.java':
package com.guarderias.pitufos.servicio;

import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.guarderias.pitufos.dominio.Actividades;
import com.guarderias.pitufos.dominio.Alumnos;
import com.guarderias.pitufos.dominio.Centros;
import com.guarderias.pitufos.dominio.Cursos;
import com.guarderias.pitufos.repositorio.AlumnosDao;

/*
* La anotación @Component, es el estereotipo principal. Indica que
* la clase anotada es un component o un bean de spring.
* Algunas de los más utilizadas son, a modo de repaso:
* •@Service: clases que pertenecen a la capa de servicios. Nos sirve
* para establecer una diferenciación semántica que puede
* ser útil a la hora de, por ejemplo, aplicar aspectos
* a todos los servicios.
* •@Repository: clases que pertenecen a la capa de acceso a datos. (Persistencia).
* •@Controller: se utiliza en Spring MVC para anotar las clases
* que actúan como controladores.
* •@Configuration: indica que la clase anotada es una clase de configuración
* de Spring. (A partir de la versión 3 de spring, éste puede
* ser configurado mediante código en vez de xml. Pero ésto
* queda fuera del alcance de éste curso.
* Nota: @Repository, @Service y @Controller son especializaciones de @Component
* para casos concretos (persistencia, servicios y presentación).
* Esto significa que puede usarse siempre @Component pero lo adecuado es usar
* estos estereotipos ya que algunas herramientas o futuras versiones de Spring
* podrían añadir semántica adicional (por ejemplo Spring Roo usa estas anotaciones
* y genera aspectos).
*/

129
@Service("alumnosService")
@Transactional
public class AlumnosManagerImpl implements AlumnosManager {

private static final long serialVersionUID = 1L;


protected final Log logger = LogFactory.getLog(getClass());
/*
* La anotación @Autowired sirve para decirle al Spring framework que
* nos inyecte la instancia que tenemos en nuestro contexto. En caso de
* que tuviéramos más de una implementación, puede que Spring inyectara
* la que no es, por lo que para estar seguros, habría que sustituírla por
* la anotación: @Resource(name="alumnosDao") para indicar excatamente qué
* componente queremos inyectar. Resource es una anotación estándar de
* J2EE (JSR-250), cuya dependencia está añadida al pom.
* En cualquier caso, no hay que olvidar definir el setter para AlumnosDao
* o Spring no tendrá manera de inyectar nuestra dependencia.
*/
@Autowired
private AlumnosDao alumnosDao;

/**
* @param alumnosDao el alumnosDao a establecer
*/
public void setAlumnosDao(AlumnosDao alumnosDao) {
this.alumnosDao = alumnosDao;
}
@Override
public List<Alumnos> leerAlumnos() {
logger.info("Recuperando alumnos.");
try {
return alumnosDao.findAll();
} catch (Exception e) {
logger.info("Error al crear la lista de alumnos.");
e.printStackTrace();
}
return null;
}
@Override
public List<Alumnos> leerAlumnosPorCentro(Centros centro) {
logger.info("Recuperando lista de alumnos por centro.");
try {

130
return centro.getAlumnosCentro();
} catch (Exception e) {
logger.info("Error al crear la lista de alumnos por centro.");
e.printStackTrace();
}
return null;
}
@Override
public List<Alumnos> leerAlumnosPorCurso(Cursos curso) {
logger.info("Recuperando lista de alumnos por curso.");
try {
return curso.getAlumnosCurso();
} catch (Exception e) {
logger.info("Error al crear la lista de alumnos por curso.");
e.printStackTrace();
}
return null;
}
@Override
public List<Actividades> leerActividadesPorAlumno(Alumnos alumno) {
logger.info("Recuperando lista de actividades por alumno.");
try {
return alumno.getActividadesAlumnos();
} catch (Exception e) {
logger.info("Error al crear la lista de actividades por alumno.");
e.printStackTrace();
}
return null;
}
@Override
public List<Alumnos> leerAlumnosPorActividad(Actividades actividad) {
logger.info("Recuperando lista de alumnos por actividad.");
try {
return actividad.getAlumnos();
} catch (Exception e) {
logger.info("Error al crear la lista de alumnos por actividad.");
e.printStackTrace();
}
return null;
}

131
@Override
public Alumnos leerAlumno(Integer id) {
logger.info("Recuperando alumno.");
try {
return alumnosDao.findById(Integer.toString(id));
} catch (Exception e) {
logger.info("Error al leer alumno.");
e.printStackTrace();
}
return null;
}
@Override
public boolean crearAlumno(Alumnos alumno) {
logger.info("Creando alumno.");
try {
alumnosDao.save(alumno);
logger.info("Alumno Creado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al crear alumno.");
e.printStackTrace();
}
return false;
}
@Override
public boolean actualizarAlumno(Alumnos alumno) {
logger.info("Actualizando alumno.");
try {
alumnosDao.saveOrUpdate(alumno);
logger.info("Alumno actualizado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al actualizar alumno.");
e.printStackTrace();
}
return false;
}
@Override
public boolean borrarAlumno(Alumnos alumno) {
logger.info("Borrando alumno.");
try {

132
alumnosDao.delete(alumno);
logger.info("Alumno borrado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al borrar alumno.");
e.printStackTrace();
}
return false;
}
}

A continuación, las demás clases; ahora no son iguales, hay que prestar atención a cada una puesto que ya empezamos a particularizarlo todo de cara la
capa de Presentación. Vuelvo a insistir en la delegación de los métodos en sus correspondientes clases DaoImpl además del uso de los getters de las clases
entidad para el desacoplo y en la utilidad de haber hecho uso en la capa de Persistencia de la plantilla de HibernateTemplate. Os dejo el código:

'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/ActividadesManager.java':
package com.guarderias.pitufos.servicio;

import java.io.Serializable;
import java.util.List;
import com.guarderias.pitufos.dominio.Actividades;
import com.guarderias.pitufos.dominio.Alumnos;
import com.guarderias.pitufos.dominio.Calificaciones;
import com.guarderias.pitufos.dominio.Fechas;
import com.guarderias.pitufos.dominio.Observaciones;
import com.guarderias.pitufos.dominio.Profesores;

public interface ActividadesManager extends Serializable {

// Devuelve una lista completa de las actividades


public List<Actividades> leerActividades();
// Devuelve una lista de actividades por profesor
public List<Actividades> leerActividadesPorProfesor(Profesores profesor);
// Devuelve una lista de actividades por profesor
public List<Actividades> leerActividadesPorAlumno(Alumnos alumno);
// Devuelve una lista de actividades por fecha de ejecución
public List<Actividades> leerActividadesPorFecha(Fechas fecha);
// Devuelve una lista de actividades por calificación
public List<Actividades> leerActividadesPorCalificacion(Calificaciones calificacion);

133
// Devuelve una lista de actividades por observación
public List<Actividades> leerActividadesPorObservacion(Observaciones observacion);
// Devuelve una lista de alumnos por actividad
public List<Alumnos> leerAlumnosPorActividad(Actividades actividad);
// Devuelve una lista de profesores por actividad
public List<Profesores> leerProfesoresPorActividad(Actividades actividad);
// Devuelve una actividad
public Actividades leerActividad(Integer id);
// Crea una actividad
public boolean crearActividad(Actividades actividad);
//Actualiza los datos de una actividad
public boolean actualizarActividad(Actividades actividad);
//Borra una actividad
public boolean borrarActividad(Actividades actividad);
}

'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/ActividadesManagerImpl.java':
package com.guarderias.pitufos.servicio;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.guarderias.pitufos.dominio.Actividades;
import com.guarderias.pitufos.dominio.Alumnos;
import com.guarderias.pitufos.dominio.Calificaciones;
import com.guarderias.pitufos.dominio.Fechas;
import com.guarderias.pitufos.dominio.Observaciones;
import com.guarderias.pitufos.dominio.Profesores;
import com.guarderias.pitufos.repositorio.ActividadesDao;

@Service("actividadesService")
@Transactional
public class ActividadesManagerImpl implements ActividadesManager {

private static final long serialVersionUID = 1L;


protected final Log logger = LogFactory.getLog(getClass());

134
@Autowired
private ActividadesDao actividadesDao;
/**
* @param actividadesDao el actividadesDao a establecer
*/
public void setActividadesDao(ActividadesDao actividadesDao) {
this.actividadesDao = actividadesDao;
}
@Override
public List<Actividades> leerActividades() {
logger.info("Recuperando actividades.");
try {
return actividadesDao.findAll();
} catch (Exception e) {
logger.info("Error al crear la lista de actividades.");
e.printStackTrace();
}
return null;
}
@Override
public List<Actividades> leerActividadesPorProfesor(Profesores profesor) {
logger.info("Recuperando actividades por profesor.");
try {
return profesor.getActividadesProfesores();
} catch (Exception e) {
logger.info("Error al crear la lista de actividades por profesor.");
e.printStackTrace();
}
return null;
}
@Override
public List<Actividades> leerActividadesPorAlumno(Alumnos alumno) {
logger.info("Recuperando actividades por alumno.");
try {
return alumno.getActividadesAlumnos();
} catch (Exception e) {
logger.info("Error al crear la lista de actividades por alumno.");
e.printStackTrace();
}
return null;
}

135
@Override
public List<Actividades> leerActividadesPorFecha(Fechas fecha) {
logger.info("Recuperando actividades por fecha.");
try {
return fecha.getFechasEjecucion();
} catch (Exception e) {
logger.info("Error al crear la lista de actividades por fecha.");
e.printStackTrace();
}
return null;
}
@Override
public List<Actividades> leerActividadesPorCalificacion(Calificaciones calificacion) {
logger.info("Recuperando actividades por calificación.");
try {
return calificacion.getActividadesCalificacion();
} catch (Exception e) {
logger.info("Error al crear la lista de actividades por calificación.");
e.printStackTrace();
}
return null;
}
@Override
public List<Actividades> leerActividadesPorObservacion(Observaciones observacion) {
logger.info("Recuperando actividades por observación.");
try {
return observacion.getObservacionesActividades();
} catch (Exception e) {
logger.info("Error al crear la lista de actividades por observación.");
e.printStackTrace();
}
return null;
}
@Override
public List<Alumnos> leerAlumnosPorActividad(Actividades actividad) {
logger.info("Recuperando alumnos por actividad.");
try {
return actividad.getAlumnos();
} catch (Exception e) {
logger.info("Error al crear la lista de alumnos por actividad.");
e.printStackTrace();

136
}
return null;
}
@Override
public List<Profesores> leerProfesoresPorActividad(Actividades actividad) {
logger.info("Recuperando profesores por actividad.");
try {
return actividad.getProfesores();
} catch (Exception e) {
logger.info("Error al crear la lista de profesores por actividad.");
e.printStackTrace();
}
return null;
}
@Override
public Actividades leerActividad(Integer id) {
logger.info("Recuperando actividad.");
try {
return actividadesDao.findById(Integer.toString(id));
} catch (Exception e) {
logger.info("Error al leer actividad.");
e.printStackTrace();
}
return null;
}
@Override
public boolean crearActividad(Actividades actividad) {
logger.info("Creando actividad.");
try {
actividadesDao.save(actividad);
logger.info("actividad Creada con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al crear actividad.");
e.printStackTrace();
}
return false;
}
@Override
public boolean actualizarActividad(Actividades actividad) {
logger.info("Actualizando actividad.");

137
try {
actividadesDao.saveOrUpdate(actividad);
logger.info("actividad actualizada con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al actualizar actividad.");
e.printStackTrace();
}
return false;
}
@Override
public boolean borrarActividad(Actividades actividad) {
logger.info("Borrando actividad.");
try {
actividadesDao.delete(actividad);
logger.info("actividad borrada con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al borrar actividad.");
e.printStackTrace();
}
return false;
}
}

'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/CalificacionesManager.java':
package com.guarderias.pitufos.servicio;

import java.io.Serializable;
import java.util.List;

import com.guarderias.pitufos.dominio.Actividades;
import com.guarderias.pitufos.dominio.Calificaciones;

public interface CalificacionesManager extends Serializable {

// Devuelve una lista completa de las calificaciones


public List<Calificaciones> leerCalificaciones();
// Devuelve una lista de actividades por calificación
public List<Actividades> leerActividadesPorCalificacion(Calificaciones calificacion);

138
// Devuelve una calificación
public Calificaciones leerCalificacion(Integer id);
// Crea una calificación
public boolean crearCalificacion(Calificaciones calificacion);
//Actualiza los datos de una calificación
public boolean actualizarCalificacion(Calificaciones calificacion);
//Borra una calificación
public boolean borrarCalificacion(Calificaciones calificacion);
}

'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/CalificacionesManagerImpl.java':
package com.guarderias.pitufos.servicio;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.guarderias.pitufos.dominio.Actividades;
import com.guarderias.pitufos.dominio.Calificaciones;
import com.guarderias.pitufos.repositorio.CalificacionesDao;

@Service("calificacionesService")
@Transactional
public class CalificacionesManagerImpl implements CalificacionesManager {

private static final long serialVersionUID = 1L;


protected final Log logger = LogFactory.getLog(getClass());

@Autowired
private CalificacionesDao calificacionesDao;
/**
* @param calificacionesDao el calificacionesDao a establecer
*/
public void setCalificacionesDao(CalificacionesDao calificacionesDao) {
this.calificacionesDao = calificacionesDao;
}
@Override
public List<Calificaciones> leerCalificaciones() {
logger.info("Recuperando calificaciones.");

139
try {
return calificacionesDao.findAll();
} catch (Exception e) {
logger.info("Error al crear la lista de calificaciones.");
e.printStackTrace();
}
return null;
}
@Override
public List<Actividades> leerActividadesPorCalificacion(Calificaciones calificacion) {
logger.info("Recuperando actividades por calificación.");
try {
return calificacion.getActividadesCalificacion();
} catch (Exception e) {
logger.info("Error al crear la lista de actividades por calificación.");
e.printStackTrace();
}
return null;
}
@Override
public Calificaciones leerCalificacion(Integer id) {
logger.info("Recuperando calificación.");
try {
return calificacionesDao.findById(Integer.toString(id));
} catch (Exception e) {
logger.info("Error al leer calificación.");
e.printStackTrace();
}
return null;
}
@Override
public boolean crearCalificacion(Calificaciones calificacion) {
logger.info("Creando calificación.");
try {
calificacionesDao.save(calificacion);
logger.info("calificación Creada con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al crear calificación.");
e.printStackTrace();
}

140
return false;
}
@Override
public boolean actualizarCalificacion(Calificaciones calificacion) {
logger.info("Actualizando calificación.");
try {
calificacionesDao.saveOrUpdate(calificacion);
logger.info("calificación actualizada con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al actualizar calificación.");
e.printStackTrace();
}
return false;
}
@Override
public boolean borrarCalificacion(Calificaciones calificacion) {
logger.info("Borrando calificación.");
try {
calificacionesDao.delete(calificacion);
logger.info("calificación borrada con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al borrar calificación.");
e.printStackTrace();
}
return false;
}
}

'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/CentrosManager.java':
package com.guarderias.pitufos.servicio;

import java.io.Serializable;
import java.util.List;

import com.guarderias.pitufos.dominio.Centros;
import com.guarderias.pitufos.dominio.Profesores;
import com.guarderias.pitufos.dominio.Alumnos;

141
public interface CentrosManager extends Serializable {

// Devuelve una lista de Centros


public List<Centros> leerCentros();
// Devuelve una lista de profesores por centro
public List<Profesores> leerProfesoresPorCentro(Centros centro);
// Devuelve una lista de alumnos por centro
public List<Alumnos> leerAlumnosPorCentro(Centros centro);
// Devuelve un centro
public Centros leerCentro(Integer id);
// Crea un centro
public boolean crearCentro(Centros centro);
// Actualiza un centro
public boolean actualizarCentro(Centros centro);
// Borra un centro
public boolean borrarCentro(Centros centro);
}

'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/CentrosManagerImpl.java':
package com.guarderias.pitufos.servicio;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.guarderias.pitufos.dominio.Alumnos;
import com.guarderias.pitufos.dominio.Centros;
import com.guarderias.pitufos.dominio.Profesores;
import com.guarderias.pitufos.repositorio.CentrosDao;

@Service("centrosService")
@Transactional
public class CentrosManagerImpl implements CentrosManager {

private static final long serialVersionUID = 1L;


protected final Log logger = LogFactory.getLog(getClass());

@Autowired
private CentrosDao centrosDao;

142
/**
* @param centrosDao el centrosDao a establecer
*/
public void setCentrosDao(CentrosDao centrosDao) {
this.centrosDao = centrosDao;
}
@Override
public List<Centros> leerCentros() {
logger.info("Recuperando centros.");
try {
return centrosDao.findAll();
} catch (Exception e) {
logger.info("Error al crear la lista de centros.");
e.printStackTrace();
}
return null;
}
@Override
public List<Profesores> leerProfesoresPorCentro(Centros centro) {
logger.info("Recuperando profesores por centro.");
try {
return centro.getProfesoresCentro();
} catch (Exception e) {
logger.info("Error al crear la lista de profesores por centro.");
e.printStackTrace();
}
return null;
}
@Override
public List<Alumnos> leerAlumnosPorCentro(Centros centro) {
logger.info("Recuperando alumnos por centro.");
try {
return centro.getAlumnosCentro();
} catch (Exception e) {
logger.info("Error al crear la lista de alumnos por centro.");
e.printStackTrace();
}
return null;
}

143
@Override
public Centros leerCentro(Integer id) {
logger.info("Recuperando centro.");
try {
return centrosDao.findById(Integer.toString(id));
} catch (Exception e) {
logger.info("Error al leer centro.");
e.printStackTrace();
}
return null;
}
@Override
public boolean crearCentro(Centros centro) {
logger.info("Creando centro.");
try {
centrosDao.save(centro);
logger.info("centro Creado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al crear centro.");
e.printStackTrace();
}
return false;
}
@Override
public boolean actualizarCentro(Centros centro) {
logger.info("Actualizando centro.");
try {
centrosDao.saveOrUpdate(centro);
logger.info("centro actualizado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al actualizar centro.");
e.printStackTrace();
}
return false;
}
@Override
public boolean borrarCentro(Centros centro) {
logger.info("Borrando centro.");
try {

144
centrosDao.delete(centro);
logger.info("centro borrado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al borrar centro.");
e.printStackTrace();
}
return false;
}
}

'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/CursosManager.java':
package com.guarderias.pitufos.servicio;

import java.io.Serializable;
import java.util.List;

import com.guarderias.pitufos.dominio.Alumnos;
import com.guarderias.pitufos.dominio.Cursos;
import com.guarderias.pitufos.dominio.Profesores;

public interface CursosManager extends Serializable {

// Devuelve una lista completa de los cursos


public List<Cursos> leerCursos();
// Devuelve una lista de alumnos por curso
public List<Alumnos> leerAlumnosPorCurso(Cursos curso);
// Devuelve una lista de profesores por curso
public List<Profesores> leerProfesoresPorCurso(Cursos curso);
// Devuelve un curso
public Cursos leerCurso(Integer id);
// Crea un curso
public boolean crearCurso(Cursos curso);
// Actualiza un curso
public boolean actualizarCurso(Cursos curso);
// Borra un curso
public boolean borrarCurso(Cursos curso);
}

145
'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/CursosManagerImpl.java':
package com.guarderias.pitufos.servicio;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.guarderias.pitufos.dominio.Alumnos;
import com.guarderias.pitufos.dominio.Cursos;
import com.guarderias.pitufos.dominio.Profesores;
import com.guarderias.pitufos.repositorio.CursosDao;

@Service("cursosService")
@Transactional
public class CursosManagerImpl implements CursosManager {

private static final long serialVersionUID = 1L;


protected final Log logger = LogFactory.getLog(getClass());

@Autowired
private CursosDao cursosDao;

/**
* @param cursosDao el cursosDao a establecer
*/
public void setCursosDao(CursosDao cursosDao) {
this.cursosDao = cursosDao;
}
@Override
public List<Cursos> leerCursos() {
logger.info("Recuperando cursos.");
try {
return cursosDao.findAll();
} catch (Exception e) {
logger.info("Error al crear la lista de cursos.");
e.printStackTrace();
}
return null;
}

146
@Override
public List<Alumnos> leerAlumnosPorCurso(Cursos curso) {
logger.info("Recuperando alumnos por curso.");
try {
return curso.getAlumnosCurso();
} catch (Exception e) {
logger.info("Error al crear la lista de alumnos por cursos.");
e.printStackTrace();
}
return null;
}
@Override
public List<Profesores> leerProfesoresPorCurso(Cursos curso) {
logger.info("Recuperando profesores por curso.");
try {
return curso.getProfesoresCurso();
} catch (Exception e) {
logger.info("Error al crear la lista de profesores por curso.");
e.printStackTrace();
}
return null;
}
@Override
public Cursos leerCurso(Integer id) {
logger.info("Recuperando curso.");
try {
return cursosDao.findById(Integer.toString(id));
} catch (Exception e) {
logger.info("Error al leer curso.");
e.printStackTrace();
}
return null;
}
@Override
public boolean crearCurso(Cursos curso) {
logger.info("Creando curso.");
try {
cursosDao.save(curso);
logger.info("curso Creado con éxito.");
return true;
} catch (Exception e) {

147
logger.info("Error al crear curso.");
e.printStackTrace();
}
return false;
}
@Override
public boolean actualizarCurso(Cursos curso) {
logger.info("Actualizando curso.");
try {
cursosDao.saveOrUpdate(curso);
logger.info("curso actualizado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al actualizar curso.");
e.printStackTrace();
}
return false;
}
@Override
public boolean borrarCurso(Cursos curso) {
logger.info("Borrando curso.");
try {
cursosDao.delete(curso);
logger.info("curso borrado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al borrar curso.");
e.printStackTrace();
}
return false;
}
}

148
'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/FechasManager.java':
package com.guarderias.pitufos.servicio;

import java.io.Serializable;
import java.util.List;
import com.guarderias.pitufos.dominio.Fechas;
import com.guarderias.pitufos.dominio.Actividades;

public interface FechasManager extends Serializable {


// Devuelve una lista de fechas
public List<Fechas> leerFechas();
// Devuelve una lista de actividades por fecha
public List<Actividades> leerActividadesPorFecha(Fechas fecha);
// Devuelve una fecha
public Fechas leerFecha(Integer id);
// Crea una fecha
public boolean crearFecha(Fechas fecha);
// Actualiza una fecha
public boolean actualizarFecha(Fechas fecha);
// Borra una fecha
public boolean borrarFecha(Fechas fecha);
}

'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/FechasManagerImpl.java':
package com.guarderias.pitufos.servicio;

import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.guarderias.pitufos.dominio.Actividades;
import com.guarderias.pitufos.dominio.Fechas;
import com.guarderias.pitufos.repositorio.FechasDao;

@Service("fechasService")
@Transactional
public class FechasManagerImpl implements FechasManager {
private static final long serialVersionUID = 1L;
protected final Log logger = LogFactory.getLog(getClass());

149
@Autowired
private FechasDao fechasDao;
/**
* @param fechasDao el fechasDao a establecer
*/
public void setFechasDao(FechasDao fechasDao) {
this.fechasDao = fechasDao;
}
@Override
public List<Fechas> leerFechas() {
logger.info("Recuperando fechas.");
try {
return fechasDao.findAll();
} catch (Exception e) {
logger.info("Error al crear la lista de fechas.");
e.printStackTrace();
}
return null;
}
@Override
public List<Actividades> leerActividadesPorFecha(Fechas fecha) {
logger.info("Recuperando actividades por fecha.");
try {
return fecha.getFechasEjecucion();
} catch (Exception e) {
logger.info("Error al crear la lista de actividades por fecha.");
e.printStackTrace();
}
return null;
}
@Override
public Fechas leerFecha(Integer id) {
logger.info("Recuperando fecha.");
try {
return fechasDao.findById(Integer.toString(id));
} catch (Exception e) {
logger.info("Error al leer fecha.");
e.printStackTrace();
}
return null;
}

150
@Override
public boolean crearFecha(Fechas fecha) {
logger.info("Creando fecha.");
try {
fechasDao.save(fecha);
logger.info("fecha Creada con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al crear fecha.");
e.printStackTrace();
}
return false;
}
@Override
public boolean actualizarFecha(Fechas fecha) {
logger.info("Actualizando fecha.");
try {
fechasDao.saveOrUpdate(fecha);
logger.info("fecha actualizada con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al actualizar fecha.");
e.printStackTrace();
}
return false;
}
@Override
public boolean borrarFecha(Fechas fecha) {
logger.info("Borrando fecha.");
try {
fechasDao.delete(fecha);
logger.info("fecha borrada con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al borrar fecha.");
e.printStackTrace();
}
return false;
}
}

151
'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/ObservacionesManager.java':
package com.guarderias.pitufos.servicio;

import java.io.Serializable;
import java.util.List;
import com.guarderias.pitufos.dominio.Observaciones;
import com.guarderias.pitufos.dominio.Actividades;

public interface ObservacionesManager extends Serializable {


// Devuelve una lista de observaciones
public List<Observaciones> leerObservaciones();
// Devuelve una lista de actividades por observación
public List<Actividades> leerActividadesPorObservacion(Observaciones observacion);
// Devuelve una observación
public Observaciones leerObservacion(Integer id);
// Crea una observación
public boolean crearObservacion(Observaciones observacion);
// Actualiza una observación
public boolean actualizarObservacion(Observaciones observacion);
// Borra una observación
public boolean borrarObservacion(Observaciones observacion);
}

'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/ObservacionesManagerImpl.java':
package com.guarderias.pitufos.servicio;

import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.guarderias.pitufos.dominio.Actividades;
import com.guarderias.pitufos.dominio.Observaciones;
import com.guarderias.pitufos.repositorio.ObservacionesDao;

@Service("observacionesService")
@Transactional
public class ObservacionesManagerImpl implements ObservacionesManager {
private static final long serialVersionUID = 1L;
protected final Log logger = LogFactory.getLog(getClass());

152
@Autowired
private ObservacionesDao observacionesDao;
/**
* @param observacionesDao el observacionesDao a establecer
*/
public void setObservacionesDao(ObservacionesDao observacionesDao) {
this.observacionesDao = observacionesDao;
}
@Override
public List<Observaciones> leerObservaciones() {
logger.info("Recuperando observaciones.");
try {
return observacionesDao.findAll();
} catch (Exception e) {
logger.info("Error al crear la lista de observaciones.");
e.printStackTrace();
}
return null;
}
@Override
public List<Actividades> leerActividadesPorObservacion(Observaciones observacion) {
logger.info("Recuperando actividades por observación.");
try {
return observacion.getObservacionesActividades();
} catch (Exception e) {
logger.info("Error al crear la lista de actividades por observación.");
e.printStackTrace();
}
return null;
}
@Override
public Observaciones leerObservacion(Integer id) {
logger.info("Recuperando observación.");
try {
return observacionesDao.findById(Integer.toString(id));
} catch (Exception e) {
logger.info("Error al leer observación.");
e.printStackTrace();
}
return null;
}

153
@Override
public boolean crearObservacion(Observaciones observacion) {
logger.info("Creando observación.");
try {
observacionesDao.save(observacion);
logger.info("observación Creada con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al crear observación.");
e.printStackTrace();
}
return false;
}
@Override
public boolean actualizarObservacion(Observaciones observacion) {
logger.info("Actualizando observación.");
try {
observacionesDao.saveOrUpdate(observacion);
logger.info("observación actualizada con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al actualizar observación.");
e.printStackTrace();
}
return false;
}
@Override
public boolean borrarObservacion(Observaciones observacion) {
logger.info("Borrando observación.");
try {
observacionesDao.delete(observacion);
logger.info("observación borrada con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al borrar observación.");
e.printStackTrace();
}
return false;
}
}

154
'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/PmtsManager.java':
package com.guarderias.pitufos.servicio;

import java.io.Serializable;
import java.util.List;
import com.guarderias.pitufos.dominio.Pmts;
import com.guarderias.pitufos.dominio.Alumnos;

public interface PmtsManager extends Serializable {


// Devuelve una lista de Padres/madres/tutores.
public List<Pmts> leerPmts();
// Devulve una lista de alumnos por pmt
public List<Alumnos> leerAlumnosPorPmt(Pmts pmt);
// Devuelve un pmt
public Pmts leerPmt(Integer id);
// Crea un pmt
public boolean crearPmt(Pmts pmt);
// Actualiza un pmt
public boolean actualizarPmt(Pmts pmt);
// Borra un pmt
public boolean borrarPmt(Pmts pmt);
}

'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/ PmtsManagerImpl.java':
package com.guarderias.pitufos.servicio;

import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.guarderias.pitufos.dominio.Alumnos;
import com.guarderias.pitufos.dominio.Pmts;
import com.guarderias.pitufos.repositorio.PmtsDao;

@Service("pmtsService")
@Transactional
public class PmtsManagerImpl implements PmtsManager {
private static final long serialVersionUID = 1L;
protected final Log logger = LogFactory.getLog(getClass());

155
@Autowired
private PmtsDao pmtsDao;
/**
* @param pmtsDao el pmtsDao a establecer
*/
public void setPmtsDao(PmtsDao pmtsDao) {
this.pmtsDao = pmtsDao;
}
@Override
public List<Pmts> leerPmts() {
logger.info("Recuperando Padres/Madres/Tutores.");
try {
return pmtsDao.findAll();
} catch (Exception e) {
logger.info("Error al crear la lista de Padres/Madres/Tutores.");
e.printStackTrace();
}
return null;
}
@Override
public List<Alumnos> leerAlumnosPorPmt(Pmts pmt) {
logger.info("Recuperando alumnos por Padre/Madre/Tutor.");
try {
return pmt.getAlumnosPmt();
} catch (Exception e) {
logger.info("Error al crear la lista de alumnos por Padre/Madre/Tutor.");
e.printStackTrace();
}
return null;
}
@Override
public Pmts leerPmt(Integer id) {
logger.info("Recuperando Padre/Madre/Tutor.");
try {
return pmtsDao.findById(Integer.toString(id));
} catch (Exception e) {
logger.info("Error al leer Padre/Madre/Tutor.");
e.printStackTrace();
}
return null;
}

156
@Override
public boolean crearPmt(Pmts pmt) {
logger.info("Creando Padre/Madre/Tutor.");
try {
pmtsDao.save(pmt);
logger.info("Padre/Madre/Tutor Creado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al crear Padre/Madre/Tutor.");
e.printStackTrace();
}
return false;
}
@Override
public boolean actualizarPmt(Pmts pmt) {
logger.info("Actualizando Padre/Madre/Tutor.");
try {
pmtsDao.saveOrUpdate(pmt);
logger.info("Padre/Madre/Tutor actualizado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al actualizar Padre/Madre/Tutor.");
e.printStackTrace();
}
return false;
}
@Override
public boolean borrarPmt(Pmts pmt) {
logger.info("Borrando Padre/Madre/Tutor.");
try {
pmtsDao.delete(pmt);
logger.info("Padre/Madre/Tutor borrado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al borrar Padre/Madre/Tutor.");
e.printStackTrace();
}
return false;
}
}

157
'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/ProfesoresManager.java':
package com.guarderias.pitufos.servicio;

import java.io.Serializable;
import java.util.List;
import com.guarderias.pitufos.dominio.Actividades;
import com.guarderias.pitufos.dominio.Profesores;
import com.guarderias.pitufos.dominio.Centros;
import com.guarderias.pitufos.dominio.Cursos;
import com.guarderias.pitufos.dominio.Roles;

public interface ProfesoresManager extends Serializable {


// Devuelve una lista de profesores
public List<Profesores> leerProfesores();
// Devuelve una lista de profesores por centro
public List<Profesores> leerProfesoresPorCentro(Centros centro);
// Devuelve una lista de profesores por curso
public List<Profesores> leerProfesoresPorCurso(Cursos curso);
// Devuelve una lista de profesores por actividad
public List<Profesores> leerProfesoresPorActividad(Actividades actividad);
// Devuelve una lista de profesores por rol
public List<Profesores> leerProfesoresPorRol(Roles rol);
// Devuelve una lista de actividades por profesor
public List<Actividades> leerActividadesPorProfesor(Profesores profesor);
// Devuelve una lista de roles por profesor
public List<Roles> leerRolesPorProfesor(Profesores profesor);
// Devuelve un profesor
public Profesores leerProfesor(Integer id);
// Crea un profesor
public boolean crearProfesor(Profesores profesor);
// Actualiza un profesor
public boolean actualizarProfesor(Profesores profesor);
// Borra un profesor
public boolean borrarProfesor(Profesores profesor);
}

158
'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/ ProfesoresManagerImpl.java':
package com.guarderias.pitufos.servicio;

import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.guarderias.pitufos.dominio.Actividades;
import com.guarderias.pitufos.dominio.Centros;
import com.guarderias.pitufos.dominio.Cursos;
import com.guarderias.pitufos.dominio.Profesores;
import com.guarderias.pitufos.dominio.Roles;
import com.guarderias.pitufos.repositorio.ProfesoresDao;

@Service("profesoresService")
@Transactional
public class ProfesoresManagerImpl implements ProfesoresManager {

private static final long serialVersionUID = 1L;


protected final Log logger = LogFactory.getLog(getClass());

@Autowired
private ProfesoresDao profesoresDao;
/**
* @param profesoresDao el profesoresDao a establecer
*/
public void setProfesoresDao(ProfesoresDao profesoresDao) {
this.profesoresDao = profesoresDao;
}
@Override
public List<Profesores> leerProfesores() {
logger.info("Recuperando profesores.");
try {
return profesoresDao.findAll();
} catch (Exception e) {
logger.info("Error al crear la lista de profesores.");
e.printStackTrace();
}
return null;
}

159
@Override
public List<Profesores> leerProfesoresPorCentro(Centros centro) {
logger.info("Recuperando profesores por centro.");
try {
return centro.getProfesoresCentro();
} catch (Exception e) {
logger.info("Error al crear la lista de profesores por centro.");
e.printStackTrace();
}
return null;
}
@Override
public List<Profesores> leerProfesoresPorCurso(Cursos curso) {
logger.info("Recuperando profesores por curso.");
try {
return curso.getProfesoresCurso();
} catch (Exception e) {
logger.info("Error al crear la lista de profesores por curso.");
e.printStackTrace();
}
return null;
}
@Override
public List<Profesores> leerProfesoresPorActividad(Actividades actividad) {
logger.info("Recuperando profesores por actividad.");
try {
return actividad.getProfesores();
} catch (Exception e) {
logger.info("Error al crear la lista de profesores por actividad.");
e.printStackTrace();
}
return null;
}
@Override
public List<Profesores> leerProfesoresPorRol(Roles rol) {
logger.info("Recuperando profesores por rol.");
try {
return rol.getProfesores();
} catch (Exception e) {
logger.info("Error al crear la lista de profesores por rol.");
e.printStackTrace();

160
}
return null;
}
@Override
public List<Actividades> leerActividadesPorProfesor(Profesores profesor) {
logger.info("Recuperando actividades por profesor.");
try {
return profesor.getActividadesProfesores();
} catch (Exception e) {
logger.info("Error al crear la lista de actividades por profesor.");
e.printStackTrace();
}
return null;
}
@Override
public List<Roles> leerRolesPorProfesor(Profesores profesor) {
logger.info("Recuperando roles por profesor.");
try {
return profesor.getRoles();
} catch (Exception e) {
logger.info("Error al crear la lista de roles por profesor.");
e.printStackTrace();
}
return null;
}
@Override
public Profesores leerProfesor(Integer id) {
logger.info("Recuperando profesor.");
try {
return profesoresDao.findById(Integer.toString(id));
} catch (Exception e) {
logger.info("Error al leer profesor.");
e.printStackTrace();
}
return null;
}
@Override
public boolean crearProfesor(Profesores profesor) {
logger.info("Creando profesor.");
try {
profesoresDao.save(profesor);

161
logger.info("profesor Creado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al crear profesor.");
e.printStackTrace();
}
return false;
}
@Override
public boolean actualizarProfesor(Profesores profesor) {
logger.info("Actualizando profesor.");
try {
profesoresDao.saveOrUpdate(profesor);
logger.info("profesor actualizado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al actualizar profesor.");
e.printStackTrace();
}
return false;
}
@Override
public boolean borrarProfesor(Profesores profesor) {
logger.info("Borrando profesor.");
try {
profesoresDao.delete(profesor);
logger.info("profesor borrado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al borrar profesor.");
e.printStackTrace();
}
return false;
}
}

162
'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/RolesManager.java':
package com.guarderias.pitufos.servicio;

import java.io.Serializable;
import java.util.List;
import com.guarderias.pitufos.dominio.Roles;
import com.guarderias.pitufos.dominio.Usuarios;
import com.guarderias.pitufos.dominio.Profesores;

public interface RolesManager extends Serializable {


// Devuelve la lista de roles
public List<Roles> leerRoles();
// Devuelve la lista de roles por usuario
public List<Roles> leerRolesPorUsuario(Usuarios usuario);
// Devuelve la lista de roles por profesor
public List<Roles> leerRolesPorProfesor(Profesores profesor);
// Devuelve la lista de profesores por rol
public List<Profesores> leerProfesoresPorRol(Roles rol);
// Devuelve un rol
public Roles leerRol(Integer id);
// Crea un rol
public boolean crearRol(Roles rol);
// Actualiza un rol
public boolean actualizarRol(Roles rol);
// Borra un rol
public boolean borrarRol(Roles rol);
}

'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/ RolesManagerImpl.java':
package com.guarderias.pitufos.servicio;

import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.guarderias.pitufos.dominio.Profesores;
import com.guarderias.pitufos.dominio.Roles;
import com.guarderias.pitufos.dominio.Usuarios;
import com.guarderias.pitufos.repositorio.RolesDao;

163
@Service("rolesService")
@Transactional
public class RolesManagerImpl implements RolesManager {

private static final long serialVersionUID = 1L;


protected final Log logger = LogFactory.getLog(getClass());

@Autowired
private RolesDao rolesDao;
/**
* @param rolesDao el rolesDao a establecer
*/
public void setRolesDao(RolesDao rolesDao) {
this.rolesDao = rolesDao;
}

@Override
public List<Roles> leerRoles() {
logger.info("Recuperando roles.");
try {
return rolesDao.findAll();
} catch (Exception e) {
logger.info("Error al crear la lista de roles.");
e.printStackTrace();
}
return null;
}

@Override
public List<Roles> leerRolesPorUsuario(Usuarios usuario) {
logger.info("Recuperando roles por usuarios.");
try {
return usuario.getRolesUsuario();
} catch (Exception e) {
logger.info("Error al crear la lista de roles por usuarios.");
e.printStackTrace();
}
return null;
}

164
@Override
public List<Roles> leerRolesPorProfesor(Profesores profesor) {
logger.info("Recuperando roles por profesor.");
try {
return profesor.getRoles();
} catch (Exception e) {
logger.info("Error al crear la lista de roles por profesor.");
e.printStackTrace();
}
return null;
}

@Override
public List<Profesores> leerProfesoresPorRol(Roles rol) {
logger.info("Recuperando profesores por rol.");
try {
return rol.getProfesores();
} catch (Exception e) {
logger.info("Error al crear la lista de profesores por rol.");
e.printStackTrace();
}
return null;
}

@Override
public Roles leerRol(Integer id) {
logger.info("Recuperando rol.");
try {
return rolesDao.findById(Integer.toString(id));
} catch (Exception e) {
logger.info("Error al leer rol.");
e.printStackTrace();
}
return null;
}

@Override
public boolean crearRol(Roles rol) {
logger.info("Creando rol.");
try {
rolesDao.save(rol);

165
logger.info("rol Creado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al crear rol.");
e.printStackTrace();
}
return false;
}

@Override
public boolean actualizarRol(Roles rol) {
logger.info("Actualizando rol.");
try {
rolesDao.saveOrUpdate(rol);
logger.info("rol actualizado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al actualizar rol.");
e.printStackTrace();
}
return false;
}

@Override
public boolean borrarRol(Roles rol) {
logger.info("Borrando rol.");
try {
rolesDao.delete(rol);
logger.info("rol borrado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al borrar rol.");
e.printStackTrace();
}
return false;
}
}

166
'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/UsuariosManager.java':
package com.guarderias.pitufos.servicio;

import java.io.Serializable;
import java.util.List;
import com.guarderias.pitufos.dominio.Usuarios;
import com.guarderias.pitufos.dominio.Roles;

public interface UsuariosManager extends Serializable {


// Devuelve la lista de usuarios
public List<Usuarios> leerUsuarios();
// Devuelve la lista roles por usuario
public List<Roles> leerRolesPorUsuario(Usuarios usuario);
// Devuelve un usuario
public Usuarios leerUsuario(Integer id);
// Crea un usuario
public boolean crearUsuario(Usuarios usuario);
// Actualiza un usuario
public boolean actualizarUsuario(Usuarios usuario);
// Borra un usuario
public boolean borrarUsuario(Usuarios usuario);
}

'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/UsuariosManagerImpl.java':
package com.guarderias.pitufos.servicio;

import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.guarderias.pitufos.dominio.Roles;
import com.guarderias.pitufos.dominio.Usuarios;
import com.guarderias.pitufos.repositorio.UsuariosDao;

@Service("usuariosService")
@Transactional
public class UsuariosManagerImpl implements UsuariosManager {
private static final long serialVersionUID = 1L;
protected final Log logger = LogFactory.getLog(getClass());

167
@Autowired
private UsuariosDao usuariosDao;

/**
* @param usuariosDao el usuariosDao a establecer
*/
public void setUsuariosDao(UsuariosDao usuariosDao) {
this.usuariosDao = usuariosDao;
}
@Override
public List<Usuarios> leerUsuarios() {
logger.info("Recuperando usuarios.");
try {
return usuariosDao.findAll();
} catch (Exception e) {
logger.info("Error al crear la lista de usuarios.");
e.printStackTrace();
}
return null;
}
@Override
public List<Roles> leerRolesPorUsuario(Usuarios usuario) {
logger.info("Recuperando roles por usuario.");
try {
return usuario.getRolesUsuario();
} catch (Exception e) {
logger.info("Error al crear la lista de roles por usuario.");
e.printStackTrace();
}
return null;
}
@Override
public Usuarios leerUsuario(Integer id) {
logger.info("Recuperando usuario.");
try {
return usuariosDao.findById(Integer.toString(id));
} catch (Exception e) {
logger.info("Error al leer usuario.");
e.printStackTrace();
}
return null;

168
}
@Override
public boolean crearUsuario(Usuarios usuario) {
logger.info("Creando usuario.");
try {
usuariosDao.save(usuario);
logger.info("usuario Creado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al crear usuario.");
e.printStackTrace();
}
return false;
}
@Override
public boolean actualizarUsuario(Usuarios usuario) {
logger.info("Actualizando usuario.");
try {
usuariosDao.saveOrUpdate(usuario);
logger.info("usuario actualizado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al actualizar usuario.");
e.printStackTrace();
}
return false;
}
@Override
public boolean borrarUsuario(Usuarios usuario) {
logger.info("Borrando usuario.");
try {
usuariosDao.delete(usuario);
logger.info("usuario borrado con éxito.");
return true;
} catch (Exception e) {
logger.info("Error al borrar usuario.");
e.printStackTrace();
}
return false;
}
}

169
Es hora de reorganizar nuestros paquetes de clases. Mejor habría sido incluso haberlo hecho desde el
principio, pero no he querido llevaros a confusión, realizando el desarrollo de una forma tan separada, al
igual que tampoco he querido realizar el desarrollo por funcionalidades, desde la capa inferior hasta la capa
superior, clase por clase. Mi idea es la de implementar "capa por capa, con todas sus funcionalidades". Lo
que pasa es que a medida que subimos, vamos desarrollando más código y debemos "modularizar" nuestros
paquetes, para que nos sean más fáciles de encontrar y modificar o actualizar tanto ahora como en el futuro.
Creamos los paquetes siguientes: (Crear un paquete ya sabemos, así que obviamos este paso).

 com.guarderias.pitufos.repositorio.basedao
 com.guarderias.pitufos.repositorio.dao
 com.guarderias.pitufos.servicio.dto
 com.guarderias.pitufos.servicio.manager
 com.guarderias.pitufos.servicio.validacion

Recolocamos las clases correspondientes en sus paquetes respectivos. Estas clases van a ser:

 Para el paquete com.guarderias.pitufos.repositorio.basedao: (Las clases Base del patrón DAO).


o BaseDao.java
o BaseDaoHibernate.java
 Para el paquete com.guarderias.pitufos.repositorio.dao: (Todas las interfaces DAO).
o ActividadesDao.java
o AlumnosDao.java
o CalificacionesDao.java
o CentrosDao.java
o CursosDao.java
o FechasDao.java
o ObservacionesDao.java
o PmtsDao.java
o ProfesoresDao.java
o RolesDao.java
o UsuariosDao.java
 Para el paquete com.guarderias.pitufos.servicio.dto (Aún sin desarrollar, queda vacío).
 Para el paquete com.guarderias.pitufos.servicio.validacion (Aún sin desarrollar, queda vacío).
 Para el paquete com.guarderias.pitufos.servicio.manager: (Todas las interfaces MANAGER).
o ActividadesManager.java
o AlumnosManager.java
o CalificacionesManager.java
o CentrosManager.java
o CursosManager.java
o FechasManager.java
o ObservacionesManager.java
o PmtsManager.java
o ProfesoresManager.java
o RolesManager.java
o UsuariosManager.java

170
Para el que no lo recuerde, recolocar una clase lo vimos ya cuando movimos la clase HomeController.java, al
paquete com.guarderias.pitufos.web, así que también vamos a obviar éstos pasos aquí. Lo único, que quería
resaltar, es que a la hora de refactorizar varias clases para moverlas todas, siempre que la nueva ubicación
sea igual para todas las clases, las podemos seleccionar todas al mismo tiempo con la tecla control pulsada y
clicando con el botón izquierdo del ratón en cada clase; Cuando estén todas seleccionadas, clicamos con el
botón derecho del ratón, seleccionamos refactorizar/mover... y en el cuadro de diálogo seleccionamos la
nueva ubicación. Si pulsáis el botón de Vista Previa podréis visualizar los cambios de código que se realizarán
para los nuevos ajustes de ubicación de las clases.

De ésta forma, en los paquetes "principales", dominio, repositorio, servicio y web, nos van a quedar las
clases de implementación y en sus respectivos paquetes nos van a quedar las demás clases; Las interfaces,
abstractas, funcionalidades, etc. Os dejo una imagen de cómo queda la estructura de paquetes de la
aplicación:

Ahora, a modo de ejemplo, y para que nos sirva también de prueba de que todo lo que estamos
desarrollando lo estamos desarrollando correctamente y no hay fallos, en vez de hacer los test, que aunque
sé que es la forma políticamente correcta de comprobar el correcto desarrollo y funcionamiento de cualquier
aplicación, me resulta una duplicidad de trabajo innecesaria (Sobre todo cuando programas tú solo). Repito;
Como prueba, vamos a desarrollar la funcionalidad completa para las capas Aplicación y Presentación, única
y exclusivamente para gestionar los usuarios y sus roles. Y repito: A modo de ejemplo y prueba. Con ésto,
terminaremos la primera parte de éste curso. Después pasaremos a completar la capa Aplicación y
después la capa de Presentación como es debido. Esto lo hago por los impacientes, porque yo reconozco que
también soy uno de ellos, y por la cantidad ingente de código que os he metido tan de seguido, del que
espero que hayáis aprendido todo lo posible. Pero me gustaría que quedara claro que lo que vamos a hacer
se puede hacer ahora y no antes, puesto que casi hemos llegado al final del desarrollo de los cimientos de
nuestra aplicación. También me gustaría, que una vez hecho, lo impacientes no dejéis el curso pensando Ya
tengo lo que quería, pues aún hay mucho, mucho que ver y que aprender de lo que nos queda y como
ejemplo de ello os diré que lo que vamos a ver ahora, es un tema poco tratado y del que podréis encontrar
pocos tutoriales en internet. La creación de un formulario web de spring, que maneja una relación uno a
muchos, como es la relación entre los usuarios y sus roles, mostrando los datos en tablas y con el diseño
terminado. Pero todavía nos queda su securización, la estructura de páginas web de la aplicación y su
securización, el desarrollo de los formularios que gestionan las relaciones muchos a muchos, etc...

171
Creamos los DTOs. Recordamos:

Los Data Transfer Object (DTO), son objetos que no tienen ningún otro comportamiento
excepto el de actuar como puente de información transfiriendo datos de un sitio a otro.

Nuestra aplicación, lo que tiene que hacer es pasar la información de una capa a otra de la forma más
transparente y funcional posible. En ésta capa, únicamente se realiza el puente de datos procedentes de la
capa de Persistencia y que más tarde serán utilizados por la capa de Presentación.

Básicamente, nuestras clases DTO serán clases similares a las clases entidad, que desarrollamos
enteriormente y que situamos en el paquete dominio, diferenciándose de estas en que no van a disponer de
anotaciones ni de serialización ni de constructores ni sobrescribirán métodos heredados del tipo toString().
Unica y exclusivamente servirán para almacenar los datos provenientes de la bd en variables
semánticamente iguales que las de las clases entidad. ¿Qué conseguimos con esto? Desacoplar el uso de las
clases de la capa de Persistencia desde las clases de la capa de Presentación.

Desarrollamos los DTO para los usuarios y para sus roles:

'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/dto/UsuariosDto.java':
package com.guarderias.pitufos.servicio.dto;

import java.util.List;

import com.guarderias.pitufos.dominio.Roles;

public class UsuariosDto {

private Integer idusuario;


private String usuario;
private String password;
private Boolean enabled;
private List<Roles> rolesUsuario;

/**
* @return el idusuario
*/
public Integer getIdusuario() {
return idusuario;
}
/**
* @param idusuario el idusuario a establecer
*/
public void setIdusuario(Integer idusuario) {
this.idusuario = idusuario;
}
/**
* @return el usuario
*/
public String getUsuario() {
return usuario;
}
/**
* @param usuario el usuario a establecer
*/

172
public void setUsuario(String usuario) {
this.usuario = usuario;
}
/**
* @return el password
*/
public String getPassword() {
return password;
}
/**
* @param password el password a establecer
*/
public void setPassword(String password) {
this.password = password;
}
/**
* @return el enabled
*/
public Boolean getEnabled() {
return enabled;
}
/**
* @param enabled el enabled a establecer
*/
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
/**
* @return el rolesUsuario
*/
public List<Roles> getRolesUsuario() {
return rolesUsuario;
}
/**
* @param rolesUsuario el rolesUsuario a establecer
*/
public void setRolesUsuario(List<Roles> rolesUsuario) {
this.rolesUsuario = rolesUsuario;
}
}

'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/dto/RolesDto.java':
package com.guarderias.pitufos.servicio.dto;

import java.util.List;

import com.guarderias.pitufos.dominio.Profesores;
import com.guarderias.pitufos.dominio.Usuarios;

public class RolesDto {

private Integer idrol;


private String rol;
private Usuarios usuario;
private List<Profesores> profesores;

173
/**
* @return el idrol
*/
public Integer getIdrol() {
return idrol;
}
/**
* @param idrol el idrol a establecer
*/
public void setIdrol(Integer idrol) {
this.idrol = idrol;
}
/**
* @return el rol
*/
public String getRol() {
return rol;
}
/**
* @param rol el rol a establecer
*/
public void setRol(String rol) {
this.rol = rol;
}
/**
* @return el usuario
*/
public Usuarios getUsuario() {
return usuario;
}
/**
* @param usuario el usuario a establecer
*/
public void setUsuario(Usuarios usuario) {
this.usuario = usuario;
}
/**
* @return el profesores
*/
public List<Profesores> getProfesores() {
return profesores;
}
/**
* @param profesores el profesores a establecer
*/
public void setProfesores(List<Profesores> profesores) {
this.profesores = profesores;
}
}

Listo! Sencillo, ¿no? ahora, para acceder y manipular los datos usaremos éstas clases en vez de las clases
entidad directamente. La clase Controller correspondiente del paquete web, perteneciente a la capa de
Presentación, usará la interfaz Manager y la clase Dto correspondiente del paquete servicio de la capa de
Aplicación; La clase Dto la rellenará, extrayendo los datos de las clases entidad que sean necesarias, para
pasárselos a través del objeto dto a la página jsp correspondiente, donde se realizará la manipulación de los
datos.

174
Dicho esto, nos queda desarrollar la clase UsuariosController.java que será la encargada de proporcionar los
datos para el modelo de la vista usuarios.jsp, donde se realizará la visualización y manipulación de los
mismos. En primer lugar desarrollaremos la parte de la clase que se encargará de gestionar los "requests", o
sea, las solicitudes de dicha página, o sea, cada vez que se pida la página usuarios.jsp, que será mapeada por
el patrón /usuarios y que se mostrará como usuarios.htm en la barra del navegador. Esto se gestiona de
forma automática a través de la configuración que establecimos en nuestro archivo web.xml.

La cuestión ahora es que en el método que maneja la petición de la página usuarios.jsp, implementaremos
toda la lógica para la extracción de datos de la bd y su posterior traslado en forma de lista para su
visualización, como acabamos de ilustrar en el párrafo anterior. Después añadiremos los métodos
correspondientes para manejar las solicitudes de manipulación de datos (edición y eliminación), que serán
gestionadas por sus formularios jsp correspondientes. Prestad atención a los comentarios, pues ahí va
explicada la clase línea por línea. Ahora al lío:

'pitufosapp/src/main/java/com/guarderias/pitufos/web/UsuariosController.java':
package com.guarderias.pitufos.web;

import java.io.IOException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import com.guarderias.pitufos.dominio.Usuarios;
import com.guarderias.pitufos.servicio.dto.UsuariosDto;
import com.guarderias.pitufos.servicio.manager.UsuariosManager;

/**
* Maneja los requests de la pagina usuarios.jsp de la app.
*/
@Controller
public class UsuariosController {

protected final Log logger = LogFactory.getLog(getClass());


/*
* Inyectamos el recurso de tipo servicio llamado "usuariosService".
* Este recurso es un bean que se declaró mediante la anotación
* @Service("usuariosService"), cuando se desarrolló la clase
* UsuariosManagerImpl.java. Lo que hacemos aquí es usar su interfaz
* para leer los datos.
*/
@Resource(name="usuariosService")
private UsuariosManager usuariosService;

175
/*
* Agregamos un atributo al modelo llamado "Usuario"
* que se inyectará como bean y sobre el que trabajaremos
*/
@ModelAttribute("Usuario")
public UsuariosDto createModel() {
return new UsuariosDto();
}

/**
* Recupera la vista usuarios.
*/
@RequestMapping(value = "/usuarios", method = RequestMethod.GET)
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse
response, Model model, Principal principal )
throws ServletException, IOException {
logger.info("Devolviendo la vista de usuarios");
/*
* Recogemos el nombre del usuario validado por spring security y lo
* añadimos al modelo
*/
String name = principal.getName();
model.addAttribute("username", name);

// Recuperamos la lista de usuarios


List<Usuarios> usuarios = usuariosService.leerUsuarios();

// Preparamos el objeto que vamos a añadir al modelo


List<UsuariosDto> usuariosDTO = new ArrayList<UsuariosDto>();

/*
* Preparamos la lista de usuarios mediante un objeto dto
* y la añadimos al objeto que vamos a añadir al modelo
* (que es una lista de objetos dto).
*/
for(Usuarios usuario: usuarios) {
// preparamos un dto para configurar la lista
UsuariosDto dto = new UsuariosDto();
// Lo rellenamos con los datos de su clase entidad correspondiente
dto.setIdusuario(usuario.getIdusuario());
dto.setUsuario(usuario.getUsuario());
dto.setPassword(usuario.getPassword());
dto.setEnabled(usuario.getEnabled());
dto.setRolesUsuario(usuario.getRolesUsuario());

// Añadimos el dto a la lista


usuariosDTO.add(dto);
}

// Añadimos el objeto (la lista de objetos dto), al modelo


model.addAttribute("usuarios", usuariosDTO);

// Devolvemos la vista y su modelo correspondientes


return new ModelAndView("usuarios", "model", model);
}
}

176
Bien. Ahora necesitamos una página usuarios.jsp que nos muestre toda la información, recogiéndola de su clase UsuariosController.java correspondiente.
Atención a los comentarios, que ahí va la explicación línea por línea:

'pitufosapp/src/main/webapp/WEB-INF/views/usuarios.jsp ':
<%@ include file="/WEB-INF/views/include.jsp" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Usuarios</title>
</head>
<body>
<!-- Establecemos recursos para la página web -->
<c:url var="editImgUrl" value="/resources/img/edit.png" />
<c:url var="deleteImgUrl" value="/resources/img/delete.png" />
<!-- Establecemos la dirección donde se ubicará la página para añadir usuarios -->
<c:url var="addUsuarioUrl" value="/usuario/addUsuario.htm" />
<!-- Creamos una tabla, con las cabeceras necesarias y un poquito de estilo... -->
<table style="border: 1px solid; width: 100%; text-align:center">
<thead style="background:#d3dce3">
<tr>
<!-- Parte de la tabla correspondiente a los datos de usuario -->
<th>Id</th>
<th>Usuario</th>
<th>Password</th>
<th>Enabled</th>
<th colspan="2"></th>
<!-- Parte de la tabla correspondiente a los roles de usuario -->
<th>Rol</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody style="background:#ccc">
<!-- Recogemos el atributo "usuarios" del modelo, el cual ha sido proporcionado por
la clase UsuariosController.java y lo vamos a recorrer, introduciendo cada item
cada vez, en una variable que llamamos "usuario" -->
<c:forEach items="${usuarios}" var="usuario">
<!-- Establecemos las direcciones donde se ubicarán las páginas
para editar y eliminar usuarios -->
<c:url var="editUsuarioUrl" value="/usuario/editUsuario.htm?idusuario=${usuario.idusuario}" />
<c:url var="deleteUsuarioUrl" value="/usuario/deleteUsuario.htm?idusuario=${usuario.idusuario}" />

177
<!-- Condición: Si la lista de roles de usuario no está vacía... -->
<c:if test="${!empty usuario.rolesUsuario}">
<!-- Recorremos su lista de roles introduciendo cada item cada vez, en una variable
que llamamos "rol" -->
<c:forEach items="${usuario.rolesUsuario}" var="rol">
<!-- Por cada rol, sacamos en la tabla su usuario correspondiente y
sus enlaces para su edición y eliminación -->
<tr>
<!-- Parte de la tabla correspondiente a los datos de usuario -->
<td><c:out value="${usuario.idusuario}" /></td> <!-- Id -->
<td><c:out value="${usuario.usuario}" /></td> <!-- Usuario -->
<td><c:out value="${usuario.password}" /></td> <!-- Password -->
<td><c:out value="${usuario.enabled}" /></td> <!-- Enabled -->
<td><a href="${editUsuarioUrl}"><img src="${editImgUrl}"></img></a></td> <!-- Colspan 1 de 2 -->
<td><a href="${deleteUsuarioUrl}"><img src="${deleteImgUrl}"></img></a></td> <!-- Colspan 2 de 2 -->
<!-- Parte de la tabla correspondiente a los roles de usuario -->
<td><c:out value="${rol.rol}" /></td> <!-- Rol -->
<!-- Por cada rol, configuramos sus enlaces para su adición, edición y eliminación -->
<c:url var="addRolUrl" value="/rol/addRol.htm?idusuario=${usuario.idusuario}" />
<c:url var="editRolUrl" value="/rol/editRol.htm?pidusuario=${usuario.idusuario}&cidrol=${rol.idrol}" />
<c:url var="deleteRolUrl" value="/rol/deleteRol.htm?idrol=${rol.idrol}" />
<!-- Por cada rol, sacamos sus enlaces para su adición, edición y eliminación -->
<td><a href="${addRolUrl}">+</a></td> <!-- Colspan 1 de 3 -->
<td><a href="${editRolUrl}"><img src="${editImgUrl}"></img></a></td> <!-- Colspan 2 de 3 -->
<td><a href="${deleteRolUrl}"><img src="${deleteImgUrl}"></img></a></td> <!-- Colspan 3 de 3 -->
</tr>
<!-- Fin del recorrido de los items que haya en la lista de roles -->
</c:forEach>
<!-- Fin del tratamiento de la condición de que la lista de roles de usuario no esté vacía -->
</c:if>
<!-- Condición: Si la lista de roles de usuario está vacía... -->
<c:if test="${empty usuario.rolesUsuario}">
<!-- Rellenamos la tabla convenientemente, sacando el usuario y los enlaces pertinentes -->
<tr>
<td><c:out value="${usuario.idusuario}" /></td>
<td><c:out value="${usuario.usuario}" /></td>
<td><c:out value="${usuario.password}" /></td>
<td><c:out value="${usuario.enabled}" /></td>
<td><a href="${editUsuarioUrl}"><img src="${editImgUrl}"></img></a></td>
<td><a href="${deleteUsuarioUrl}"><img src="${deleteImgUrl}"></img></a></td>

178
<td>N/A</td>
<td>N/A</td>
<c:url var="addRolUrl" value="/rol/addRol.htm?idusuario=${usuario.idusuario}" />
<td><a href="${addRolUrl}">+</a></td>
<td></td>
<td></td>
</tr>
<!-- Fin del tratamiento de la condición de que la lista de roles de usuario esté vacía -->
</c:if>
<!-- Fin del recorrido de los items que haya en la lista de usuarios -->
</c:forEach>
</tbody>
</table>
<!-- Si la lista de usuarios está vacía mostramos el mensaje correspondiente -->
<c:if test="${empty usuarios}">
No se han encontrado usuarios.
</c:if>
<!-- Mostramos bajo la tabla el enlace para añadir usuarios -->
<p><a href="${addUsuarioUrl}">Crear usuario</a></p>
</body>
</html>

Bien. Sólo nos queda situar los archivos correspondientes a las opciones de edición y eliminación, bajo las rutas siguientes:

 'pitufosapp/src/main/webapp/resources/img/delete.png ':
 'pitufosapp/src/main/webapp/resources/img/edit.png ':

Podéis Bajaros los archivos que queráis desde internet, ya sabéis que hay infinidad de ellos, (respetad el formato .png, eso si...), o bien podéis usar los que
se acompañan con el cd del curso. Obviamos también la creación de la carpeta img bajo la carpeta resources de nuestra aplicación, puesto que éste asunto
debería estar dominado a éstas alturas.

179
Si ejecutamos ahora la aplicación y nos validamos, (esto es importante, puesto que si no lo hacemos, nos
devolverá una excepción de tipo java.lang.NullPointerException, puesto que nuestro método
handleRequest de la clase UsuariosController.java, espera un objeto de tipo Principal de spring security, que
entre otras cosas, contiene el nombre del usuario que se ha validado y como estamos aún en desarrollo éste
aspecto no lo hemos tratado...), Repito; Ejecutamos ahora la aplicación y nos validamos y a continuación
escribimos en la barra del navegador: http://localhost:8080/pitufos/usuarios.htm y pulsamos Enter, nos
aparece la siguiente página, que se corresponde con todo lo desarrollado hasta ahora:

En la captura, podéis apreciar en la zona 1 parte de cómo deberíais llevar la estructura de directorios y
archivos del proyecto a éstas alturas, la página usuarios.jsp mostrada con toda la información procedente de
la base de datos y los enlaces pertinentes en la zona 2 y la salida de consola en la zona 3, donde podemos
observar nuestros loggins y la salida de hibernate por consola, tal y como ya se configuró anteriormente en
las propiedades de hibernate.

La página es casi funcional, a excepción de los enlaces que en la misma aparecen para dar de alta o editar o
eliminar, tanto a los usuarios como a sus roles. Vamos a completar éstas funcionalidades. Para ello,
desarrollaremos los formularios correspondientes, implementándolos en páginas .jsp, cuyos métodos de
captura en la clase Controller correspondiente, se servirán de la capa Aplicación para completar las
funcionalidades CRUD.

180
Editamos la clase UsuariosController.java. En ella, introducimos un método tipo request para manejar las
solicitudes de la página que nos servirá para añadir usuarios:

'pitufosapp/src/main/java/com/guarderias/pitufos/web/UsuariosController.java':
...................... Después de handleRequest ......................
/**
* Recupera la vista añadir usuario "/usuario/addUsuario"
*/
@RequestMapping(value = "/usuario/addUsuario", method = RequestMethod.GET)
public ModelAndView getAdd(Model model, Principal principal )
throws ServletException, IOException {

logger.info("Devolviendo la vista para añadir usuarios");

// Creamos un nuevo objeto UsuariosDto y lo añadimos al modelo


model.addAttribute("Usuario", new UsuariosDto());

// Devolvemos la vista addUsuario.jsp y su modelo correspondiente.


return new ModelAndView("addUsuario", "model", model);
}
......................

Muy fácil, ¿verdad? aquí, lo unico que hemos hecho es añadir un objeto de la clase UsuariosDto.java al
modelo que acompañará a la vista (la página jsp), que se mostrará cuando sea solicitada. ¿Por qué usamos el
objeto DTO? Porque así desacoplamos la capa de Persistencia de la capa de Presentación completamente y
además sobre ése objeto realizaremos la validación de los datos. Queda por resaltar el modo en que va a
trabajar nuestro método tipo request; En modo GET. Esto es así porque vamos a recoger datos del servidor.
En este caso, es el objeto de la clase Usuarios.java que "POSTeriormente" vamos a editar.
Es por ello, por lo que ahora realizamos la programación del método que va a controlar la vista
addUsuario.jsp, desde su parte POST.

'pitufosapp/src/main/java/com/guarderias/pitufos/web/UsuariosController.java':
...................... Después de getAdd ......................
/**
* Añade un nuevo Usuario.
*/
@RequestMapping(value = "/usuario/addUsuario", method = RequestMethod.POST)
public String postAdd(@ModelAttribute("Usuario") UsuariosDto usuario,
Principal principal ) throws ServletException, IOException {

logger.info("Añadiendo un nuevo usuario");

// Transferimos los datos del objeto DTO al objeto de Dominio.


Usuarios usr = new Usuarios();
usr.setUsuario(usuario.getUsuario());
usr.setPassword(usuario.getPassword());
usr.setEnabled(usuario.getEnabled());
// Delegamos en el servicio...
usuariosService.crearUsuario(usr);
// Redireccionamos a usuarios.jsp
return "redirect:/usuarios.htm";
}
......................

181
Igual de fácil, recogemos el atributo "Usuario" del modelo, y delegamos en nuestro servicio de usuarios, la
creación de ése nuevo usuario cuyos datos hemos editado en el formulario que vamos a crear a
continuación. Luego, redireccionamos a la página principal de gestión de usuarios. Ahora creamos el
formulario para la edición de datos de nuestro nuevo usuario:

'pitufosapp/src/main/webapp/WEB-INF/views/addUsuario.jsp ':
<%@ include file="/WEB-INF/views/include.jsp"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<h1>Crear nuevo Usuario</h1>
<!-- Establecemos recursos para la página web -->
<c:url var="saveUrl" value="/usuario/addUsuario.htm" />
<form:form modelAttribute="Usuario" method="POST"
action="${saveUrl}">
<table>
<tr>
<td><form:label path="usuario">Usuario:</form:label></td>
<td><form:input path="usuario" /></td>
</tr>
<tr>
<td><form:label path="password">Password:</form:label></td>
<td><form:password path="password" /></td>
</tr>
<tr>
<td><form:label path="enabled">Habilitado:</form:label></td>
<td><form:checkbox path="enabled" /></td>
</tr>
</table>
<input type="submit" value="Guardar" />
</form:form>
</body>
</html>

Si ahora ejecutamos la aplicación y navegamos hasta la página de gestión de usuarios y clicamos sobre el
enlace para crear un nuevo usuario nos saldrá el siguiente formulario:

182
Ahora bien, tenemos dos asuntos que resolver antes de continuar:

1. ¿Estamos seguros de que los datos que se van a introducir en la base de datos van a ser correctos?
¿Qué pasa si los dejamos en blanco, por ejemplo?
2. ¿Hemos desarrollado "algo" para que la contraseña introducida sea encriptada antes de ser
almacenada?

Evidentemente, dándonos cuenta de estos dos aspectos podremos afrontar su solución de dos formas. La
primera es llevar a cabo la ejecución observando qué pasa cuando falle y corrigiendo ésos errores u
observando cómo se comporta la aplicación si no falla, y depurando el código hasta corregir los errores. Por
eso me refiero a que los test, pueden ayudar a revelar ciertos fallos en el código si, pero hay otros fallos de
código que los test no los pueden detectar. La otra forma de solucionar ésto es poniéndonos manos a la obra
y desarrollando las carencias de código. Vamos a ello.

Para garantizar cierto nivel de calidad sobre los datos que van a ser introducidos en la base de datos,
disponemos de la validación. La validación de datos la vamos a realizar desde el primer momento, cuando el
ususario introduce la información en el formulario y lanza la petición CRUD. Para ello, existen multitud de
formas, pero ahora y a modo de ejemplo, vamos a utilizar dos de ellas. La primera va a usar el JSR 303: Bean
Validation, que es un marco de trabajo (framework), que actualmente forma parte de las especificaciones de
la plataforma Java EE 6. Bueno, en realidad, las dos formas que vamos a usar usan el JSR303. Lo único que
cambia es la forma de implementarlo que como veréis, nos brindará diferentes grados de personalización
según la forma que utilicemos. Para no andarme con rodeos, os describo los pasos a seguir para cada una de
las formas y después las implementaremos. Al final quedará funcionando la segunda:

1ª Forma:
1. Creamos un archivo proveedor de mensajes de la aplicación; messages.properties bajo el directorio
WEB-INF/classes.
2. Creamos el bean messageSource, que informará a Spring de la localización del archivo de mensajes
de la aplicación, en el archivo root-context.xml.
3. Modificamos la página .jsp que va a contener la validación con el siguiente código. (Lo añadimos
entre las etiquetas <head> </head>). Y completamos los formularios con la tag form:errors.

<style>
.errorblock {
color: #FF0000;
background-color: #EEFFBA;
border: 2px solid #FF0000;
}
</ style>

4. Añadimos las anotaciones @NotBlank, @NotEmpty(message="El campo no puede quedar


vacío.") y @Size(min = 5, max = 8), al campo sobre el que se va a validar (Incluso se pueden
crear anotaciones personalizadas, pero eso, más adelante), en la clase Dto Correspondiente.
5. Modificamos la clase Controller añadiendo al atributo de modelo que se le pasa en el método POST,
la anotación @Valid, y un parámetro de la clase BindingResult, que nos permita controlar el
resultado de la validación, junto con el código correspondiente que nos devuelva al formulario de
nuevo en caso que hubieran errores de validación en el mismo.
6. Añadir los mensajes que queremos que se muestren en el archivo mesages.properties. Esta
operación, sigue una convención que explicaremos más adelante.
7. Ejecutar y observar los resultados.

183
2ª Forma:
1. Creamos un archivo proveedor de mensajes de la aplicación; messages.properties bajo el directorio
WEB-INF/classes. (Esto ya se hizo para implementar la primera forma).
2. Creamos el bean messageSource, que informará a Spring de la localización del archivo de mensajes
de la aplicación. (Esto ya se hizo para implementar la primera forma).
3. Modificamos la página .jsp que va a contener la validación con el siguiente código. (Lo añadimos
entre las etiquetas <head> </head>). Y completamos los formularios con la tag form:errors. (Esto ya
se hizo para implementar la primera forma).

<style>
.errorblock {
color: #FF0000;
background-color: #EEFFBA;
border: 2px solid #FF0000;
}
</ style>

4. Borramos las anotaciones @NotBlank, @NotEmpty(message="El campo no puede quedar


vacío.") y @Size(min = 5, max = 8), del campo sobre el que se va a validar, en la clase Dto
Correspondiente.
5. Modificamos la clase Controller añadiendo al atributo de modelo que se le pasa en el método POST,
la anotación @Valid, y un parámetro de la clase BindingResult, que nos permita controlar el
resultado de la validación, junto con el código correspondiente que nos devuelva al formulario de
nuevo en caso que hubieran errores de validación en el mismo. (Esto ya se hizo para implementar la
primera forma).
6. Reescribir los mensajes que queremos que se muestren en el archivo mesages.properties.
7. Creamos nuestra clase DtoValidator, bajo el paquete validacion.
8. Inyectamos nuestro DtoValidator en la clase Controller y lo inicializamos para el formulario
correspondiente a través del método initBinder().
9. Ejecutar y observar los resultados.

Como podéis observar, el proceso es sencillo y no muy laborioso. Antes de continuar vamos a explicar la
convención a la hora de la creación de los mensajes personalizados que se lanzarán desde el archvio
messages.properties, y dejaremos para más adelante, la creación de anotaciones personalizadas.

Cada mensaje existente en dicho archivo proveedor de mensajes se compone de dos partes. clave-valor. La
parte referente al valor es la que nosotros escribimos a nuestro antojo, pero la parte de la clave sigue un
espacio de nombres que el JSR-303, se encarga de buscar, para aplicarle ése mensaje al campo
correspondiente en el formulario web que se ha validado. El espacio de nombres es el siguiente:

[Clase de la etiqueta Validadora].[Atributo de Modelo].[Campo a validar]=mensaje de texto a mostrar

En nuestro ejemplo, para cuando queramos mostrar el mensaje que corresponda a un campo que no puede
quedar vacío, deberemos utilizar la convención de nombres de la siguiente forma:

Size.Usuario.password=La contraseña debe tener entre 5 y 8 caracteres.

Como véis, no tiene ningún misterio. Una vez conocido esto, nos ponemos manos a la obra.

184
Implementando la 1ª Forma:
1º Creamos el archivo de propiedades: Nos situamos en el directiorio WEB-INF/classes y creamos un
archivo de propiedades que llamaremos messages.properties, para ello, clicamos con botón derecho del
ratón sobre la carpeta en la que vamos a crear el archivo y seleccionamos Nuevo/Otro... y en el cuadro de
diálogo que aparece introducimos el nombre del fichero y su extensión; Clicamos Finalizar y Listo:

2º Creamos el bean asociado: A continuación modificamos el archivo root-context.xml, añadiendo el bean


messageSource, que informará a Spring de la localización del archivo messages.properties que acabamos de
crear y que contendrá los mensajes de la aplicación. Para ello, nos situaremos sobre el archivo, hacemos
doble clic para abrirlo y debajo del siguiente código...

<!-- activando la configuración para la aplicación conducida por anotaciones "annotation


driven" -->
<context:annotation-config />

...vamos a añadir nuestro bean:

<!-- activando la configuración para la aplicación conducida por anotaciones "annotation


driven" -->
<context:annotation-config />

<!-- Informamos la localización del archivo con los mensajes de la aplicación -->
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages"/>
</bean>

185
3º Modificamos la página jsp: Abrimos la página addUsuario.jsp y bajo la etiqueta <head> insertamos el
siguiente código:

<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style>
.errorblock {
color: #FF0000;
background-color: #EEFFBA;
border: 2px solid #FF0000;
}
</style>
</head>

Completamos los formularios con la tag form:errors: Ahora, nos desplazamos hacia abajo en el archivo hasta
encontrar el formulario y la parte del mismo que se corresponde con la tabla donde se encuentran los
campos y las etiquetas correspondientes al usuario y la password y le insertamos el siguiente código:

<tr>
<td><form:label path="usuario">Usuario:</form:label></td>
<td><form:input path="usuario" /></td>
<td><form:errors path="usuario" cssClass="errorblock" /></td>
</tr>
<tr>
<td><form:label path="password">Password:</form:label></td>
<td><form:password path="password" /></td>
<td><form:errors path="password" cssClass="errorblock" /></td>
</tr>

También podríamos centralizar los mensajes de error que envía el formulario, utilizando una sóla
tag, y concentrando los caminos o variables "path" que envían los errores en todos "*" usando
las siguientes líneas de código al final del formulario, en vez de las otras dos:
<tr>
<td><form:errors path="*" cssClass="errorblock" /></td>
</tr>
Más adelante veremos cómo depurar éste tipo de mensajes para hacerlos más atractivos.

4º Añadimos las anotaciones: Abrimos la clase UsuariosDto.java y la modificamos, añadiendo sobre los
campos (variables globales), usuario y password, las siguientes anotaciones. (El código final de la
declaración de dichas variables quedaría así):

@NotBlank
@NotEmpty(message="El campo no puede quedar vacío.")
@Size(min = 5, max = 8)
private String usuario;

@NotBlank
@NotEmpty(message="El campo no puede quedar vacío.")
@Size(min = 5, max = 8)
private String password;

186
Fijaros. En el caso de la anotación @NotEmpty, vemos que incorpora su propio mensaje. Nosotros depués
configuraremos el archivo messages.properties con los mensajes por convención y así podréis ir haciendo
pruebas y viendo quién "rige", a la hora de mostrar el mensaje de error en tiempo de ejecución.

5º Modificamos la clase Controller: Abrimos el archivo de clase UsuariosController.java y vamos al método


postAdd() que vamos a modificar, quedando el código de la siguiente manera:

/**
* Añade un nuevo Usuario.
*/
@RequestMapping(value = "/usuario/addUsuario", method = RequestMethod.POST)
public String postAdd(@ModelAttribute("Usuario") @Valid UsuariosDto usuario,
BindingResult result, Principal principal)
throws ServletException, IOException {

logger.info("Añadiendo un nuevo usuario");

if(result.hasErrors())
return "addUsuario";

// Transferimos los datos del objeto DTO al objeto de Dominio.


Usuarios usr = new Usuarios();
usr.setUsuario(usuario.getUsuario());
usr.setPassword(usuario.getPassword());
usr.setEnabled(usuario.getEnabled());
// Delegamos en el servicio...
usuariosService.crearUsuario(usr);

// Redireccionamos a usuarios.jsp
return "redirect:/usuarios.htm";
}

6º Añadimos los mensajes al archivo messages.properties: Incluímos el siguiente código en el archivo:


'pitufosapp/src/main/webapp/WEB-INF/classes/messages.properties ':
Size=El nombre de usuario debe tener entre 5 y 8 caracteres.
Size.Usuario.password=La contraseña debe tener entre 5 y 8 caracteres.
NotEmpty.Usuario.password=El campo contraseña no puede estar vacío.
NotBlank=El campo no puede contener espacios en blanco.

187
7º Ejecutamos y observamos el resultado: Haced varias pruebas. rellenar mal los campos, rellenadlos con
espacios en blanco, sin nada, con más de los especificados... luego fijaros en qué mensaje sale, borrad la
clave+valor que queráis del archivo messages.properites a ver qué pasa... etcétera. Así es como mejor lo váis
a comprobar!! Os dejo una captura con alguna de las ejecuciones. Sí que salen superpuestos los mensajes
cuando hay más de uno, pero recordad, todo se puede mejorar y depurar... ya lo veremos:

Evidentemente, ni que decir tiene que si hay errores, el formulario no se enviará y no se introducirá ningún
datos en nuestra base de datos.

Implementando la 2ª Forma:
1º Creamos un archivo proveedor de mensajes de la aplicación; messages.properties bajo el directorio
WEB-INF/classes. (Esto ya se hizo para implementar la primera forma).

2º Creamos el bean messageSource, que informará a Spring de la localización del archivo de mensajes de la
aplicación. (Esto ya se hizo para implementar la primera forma).

3º Modificamos la página .jsp que va a contener la validación con el siguiente código. (Lo añadimos entre
las etiquetas <head> </head>). Y completamos los formularios con la tag form:errors. (Esto ya se hizo para
implementar la primera forma).

<style>
.errorblock {
color: #FF0000;
background-color: #EEFFBA;
border: 2px solid #FF0000;
}
</ style>

188
4º Borramos las anotaciones: @NotBlank, @NotEmpty(message="El campo no puede quedar vacío.")
y @Size(min = 5, max = 8), del campo sobre el que se va a validar, en la clase Dto Correspondiente.
(Mejor que borrar, lo que vamos a hacer es comentarlas. De ésta forma conservaréis el código para que
elijáis el que más os guste):

private Integer idusuario;

/*@NotBlank
@NotEmpty(message="El campo no puede quedar vacío.")
@Size(min = 5, max = 8)*/
private String usuario;

/*@NotBlank
@NotEmpty(message="El campo no puede quedar vacío.")
@Size(min = 5, max = 8)*/
private String password;

private Boolean enabled;

5º Modificamos la clase Controller: Añadiendo al atributo de modelo que se le pasa en el método POST, la
anotación @Valid, y un parámetro de la clase BindingResult, que nos permita controlar el resultado de la
validación, junto con el código correspondiente que nos devuelva al formulario de nuevo en caso que
hubieran errores de validación en el mismo. (Esto ya se hizo para implementar la primera forma).

6º Reescribimos los mensajes del archivo messages.properties que queremos que se muestren cuando
nuestro formulario contenga errores. Esto, realmente no hace falta, puesto que ya dejamos preparado
anteriormente dicho archivo, adaptado por convención a las dos formas. El motivo lo explicaré cuando
implementemos nuestra clase Validator en el siguiente paso.

7º Creamos nuestra clase UsuariosDtoValidator.java: la crearemos bajo el paquete validacion y será la clase
encargada de gestionar los errores posibles que podrá contener nuestro formulario, en sustitución de las
anotaciones que hemos eliminado en el paso 4º:

'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/validacion/UsuariosDtoValidator.java':
package com.guarderias.pitufos.servicio.validacion;

import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

import com.guarderias.pitufos.servicio.dto.UsuariosDto;

@Component // Indicamos que será un bean, que después inyectaremos.


public class UsuariosDtoValidator implements Validator {

/*
* Establecemos unas constantes para la longitud mínima y máxima
* del nombre de usuario y el password
*/

189
private static final int MINIMUM_PASSWORD_LENGTH = 5;
private static final int MAXIMUM_PASSWORD_LENGTH = 8;
private static final int MINIMUM_USER_LENGTH = 5;
private static final int MAXIMUM_USER_LENGTH = 8;

@SuppressWarnings("rawtypes")
public boolean supports(Class clazz) {
return UsuariosDto.class.isAssignableFrom(clazz);
}

public void validate(Object target, Errors errors) {

/*
* Rechaza el campo para el nombre de usuario y el password
* si están vacíos o en blanco.
* Buscará el mensaje que mostrar en la etiqueta "Size"
* del archivo messages.properties, por convención, de forma
* genérica. Y si está personalizada para el campo correspondiente
* usará la personalizada.
*/
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "usuario", "Size");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "Size");

// Convertimos el objeto sobre el que se va a validar a nuestro Dto.


UsuariosDto usuario = (UsuariosDto) target;

// Realizamos la validación de la longitud para el nombre de usuario.


if(usuario.getUsuario().length() < MINIMUM_USER_LENGTH ||
usuario.getUsuario().length() > MAXIMUM_USER_LENGTH) {

errors.rejectValue("usuario", "Size");
}

// Realizamos la validación de la longitud para el password.


if(usuario.getPassword().length() < MINIMUM_PASSWORD_LENGTH ||
usuario.getPassword().length() > MAXIMUM_PASSWORD_LENGTH) {

errors.rejectValue("password", "Size");
}
}
}

8º Inyectamos nuestro DtoValidator: Para ello, modificaremos la clase UsuariosController.java y lo


inicializaremos para el formulario, a través del método initBinder(), que situaremos justo encima de nuestro
método postAdd().

................ Importaciones anteriores ...............


import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;

190
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import com.guarderias.pitufos.dominio.Usuarios;
import com.guarderias.pitufos.servicio.dto.UsuariosDto;
import com.guarderias.pitufos.servicio.manager.UsuariosManager;
import com.guarderias.pitufos.servicio.validacion.UsuariosDtoValidator;

/**
* Maneja los requests de la pagina usuarios.jsp de la app.
*/
@Controller
public class UsuariosController {

protected final Log logger = LogFactory.getLog(getClass());

@Autowired
private UsuariosDtoValidator usuariosValidator;

................ Código posterior hasta el método postAdd() ..............

@InitBinder
private void initBinder(WebDataBinder binder) {
binder.setValidator(usuariosValidator);
}

/**
* Añade un nuevo Usuario.
*/
@RequestMapping(value = "/usuario/addUsuario", method = RequestMethod.POST)
public String postAdd(@ModelAttribute("Usuario") @Valid UsuariosDto usuario,
BindingResult result, Principal principal)
throws ServletException, IOException {

logger.info("Añadiendo un nuevo usuario");

if(result.hasErrors())
return "addUsuario";

// Transferimos los datos del objeto DTO al objeto de Dominio.


Usuarios usr = new Usuarios();
usr.setUsuario(usuario.getUsuario());
usr.setPassword(usuario.getPassword());
usr.setEnabled(usuario.getEnabled());
// Delegamos en el servicio...
usuariosService.crearUsuario(usr);

// Redireccionamos a usuarios.jsp
return "redirect:/usuarios.htm";
}
................ Código posterior hasta el final de la clase ..............

191
9º Ejecutamos y observamos el resultado: Haced igual que antes, para ver la diferencia en los mensajes que
se muestran :

En la imagen, podemos observar que, como al realizar la validación del campo correspondiente al nombre de
usuario ha detectado dos errores, (porque lo dejamos en blanco y porque la longitud debe estar entre 5 y 8
caracteres), muestra dos veces el mensaje de error, mientras que abajo se escribió un password con sólo tres
caracteres, por lo que sólo muestra el mensaje una vez por la longitud, devolviendo el formulario con el
campo vacío (si hubiera sido el campo del nombre de usuario, lo habría devuelto con los caracteres que
hubiésemos introducido).

Hemos resuelto uno de los dos asuntos que teníamos pendientes antes de continuar con la implementación
del resto de funcionalidades CRUD de nuestros formularios para la gestión de usuarios. Ahora queda
pendiente el asunto concerniente a la encriptación de la contraseña. Evidentemente, la contraseña
querremos almacenarla cifrada en la base de datos, para prevenir la captura de éstas por parte de algún
"usuario malintencionado". Para ello, deberemos tener en cuenta que la encriptación de la misma, debe
hacerse, después de haber validado los datos provenientes del formulario pero antes de ser introducidos en
la bd. Por lo que vamos a proceder a modificar la clase UsuariosController.java, concretamente el método
postAdd() de la siguiente forma:

....... Importaciones anteriores ........


import org.springframework.security.authentication.encoding.ShaPasswordEncoder;
....... Resto código hasta el método postAdd() ........
// Si encuentra errores en la validación, nos devuelve al formulario de nuevo.
if(result.hasErrors())
return "addUsuario";

// Encriptamos la contraseña con el algoritmo SHA-1.


ShaPasswordEncoder shaEncoder = new ShaPasswordEncoder();
String passwordEncriptado = shaEncoder.encodePassword(usuario.getPassword(), null);
logger.info(passwordEncriptado);

// Transferimos los datos del objeto DTO al objeto de Dominio.


Usuarios usr = new Usuarios();
usr.setUsuario(usuario.getUsuario());
usr.setPassword(passwordEncriptado);
....... Resto código hasta el final de la clase ........

192
Una vez terminado, comprobemos que todo funciona. Ejecutamos la aplicación, nos validamos, navegamos
hasta la página de creación de usuarios e introducimos el siguiente usuario:

 Usuario: pitufo
 Password: pitufops

Al pulsar sobre el botón de guardar, se nos redireccionará a la página principal de gestión de usuarios, donde
deberemos observar cómo los datos han sido correctamente introducidos en la base de datos, incluyendo
nuestra contraseña ya encriptada.

Continuamos la gestión de usuarios. Nos queda aún, la edición de usuarios su eliminación, y la introducción
de sus roles. Manos a la obra!

Implementamos ahora la función de edición. Para ello, crearemos nuestro método controlador en la clase
UsuariosController.java y después nuestro formulario jsp correspondiente. Añadimos el siguiente método a
la clase UsuariosController.java:

/**
* Recupera la vista editar usuario "/usuario/editUsuario"
*/
@RequestMapping(value = "/usuario/editUsuario", method = RequestMethod.GET)
public ModelAndView getEdit(@RequestParam("idusuario") Integer idUsuario,
Model model, Principal principal )
throws ServletException, IOException {

logger.info("Devolviendo la vista para editar usuarios");

// Recuperamos el objeto Usuario por su id.


Usuarios usuario = usuariosService.leerUsuario(idUsuario);

// Creamos un dto para ése usuario y lo rellenamos con los datos correspondientes
UsuariosDto usuarioDto = new UsuariosDto();
usuarioDto.setIdusuario(usuario.getIdusuario());
usuarioDto.setUsuario(usuario.getUsuario());
usuarioDto.setPassword(usuario.getPassword());

193
usuarioDto.setEnabled(usuario.getEnabled());
usuarioDto.setRolesUsuario(usuario.getRolesUsuario());

// Añadimos el dto al modelo


model.addAttribute("Usuario", usuarioDto);

// Devolvemos la vista editUsuario.jsp y su modelo correspondiente.


return new ModelAndView("editUsuario", "model", model);
}

Este método, nos va a preparar la vista de edición del usuario seleccionado, recuperando al mismo de la base
de datos a través de su id de usuario, (su número de orden en la tabla cuando se creó) y transfiriéndolo a un
objeto de dto, que será sobre el que trabajaremos en el formulario y el cual es el que añade al modelo de
datos.

Preparamos el formulario correspondiente editUsuario.jsp:

'pitufosapp/src/main/webapp/WEB-INF/views/editUsuario.jsp ':
<%@ include file="/WEB-INF/views/include.jsp"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style>
.errorblock {
color: #FF0000;
background-color: #EEFFBA;
border: 2px solid #FF0000;
}
</style>
</head>
<body>
<h1>Editar Usuario</h1>
<!-- Establecemos recursos para la página web -->
<c:url var="saveUrl"
value="/usuario/editUsuario.htm?idusuario=${Usuario.idusuario}" />
<form:form modelAttribute="Usuario" method="POST"
action="${saveUrl}">
<table>
<tr>
<td><form:label path="idusuario">Id:</form:label></td>
<td><form:input path="idusuario" disabled="true" /></td>
</tr>
<tr>
<td><form:label path="usuario">Usuario:</form:label></td>
<td><form:input path="usuario" /></td>
<td><form:errors path="usuario" cssClass="errorblock" /></td>
</tr>
<tr>
<td><form:label path="password">Password:</form:label></td>
<td><form:password path="password" /></td>
<td><form:errors path="password" cssClass="errorblock" /></td>
</tr>
<tr>
<td><form:label path="enabled">Habilitado:</form:label></td>
<td><form:checkbox path="enabled" /></td>
</tr>

194
<tr>
<!--<td><form:errors path="*" cssClass="errorblock" /></td>-->
</tr>
</table>
<input type="submit" value="Guardar" />
</form:form>
</body>
</html>

A continuación debemos preparar el método POST, que vamos a usar para cuando el usuario de la aplicación
termine de editar la información en el formulario de edición de datos de usuario. Este método lo añadiremos
a continuación del método getEdit(), en la clase UsuariosController.java:

/**
* Edita un usuario.
*/
@RequestMapping(value = "/usuario/editUsuario", method = RequestMethod.POST)
public String postEdit(@RequestParam("idusuario") Integer idUsuario,
@ModelAttribute("Usuario") @Valid UsuariosDto usuario,
BindingResult result, Principal principal) throws ServletException, IOException {

logger.info("Editando usuario");

// Si encuentra errores en la validación, nos devuelve al formulario de nuevo.


if(result.hasErrors())
return "editUsuario";

// Encriptamos la contraseña con el algoritmo SHA-1.


ShaPasswordEncoder shaEncoder = new ShaPasswordEncoder();
String passwordEncriptado = shaEncoder.encodePassword(
usuario.getPassword(), null);
logger.info(passwordEncriptado);

// Transferimos los datos del objeto DTO al objeto de Dominio.


Usuarios usr = new Usuarios();
usr.setIdusuario(idUsuario);
usr.setUsuario(usuario.getUsuario());
usr.setPassword(passwordEncriptado);
usr.setEnabled(usuario.getEnabled());
// Delegamos en el servicio...
usuariosService.actualizarUsuario(usr);

// Redireccionamos a usuarios.jsp
return "redirect:/usuarios.htm";
}

Como podemos comprobar, hemos integrado en nuestro desarrollo para la edición de usuarios, tanto la
encriptación de la contraseña, como el código necesario para la validación de la información, cuyo soporte se
había desarrollado anteriormente.

195
Si ejecutamos, nos aparecerá el siguiente formulario, donde ya sólo nos queda probar un rato a introcudir
datos para que se efectúen errores de validación, etcétera...:

Para terminar con las funciones CRUD asociadas a los usuarios, desarrollaremos la función de borrar usuario.
Esta será la más sencilla, puesto que no necesitaremos apoyarnos en un objeto tipo DTO para operar, ni
implementar ningún tipo de formulario. Lo único que haremos será pedir una confirmación de la eliminación,
al objeto de evitar que el borrado sea directo al clicar por error sobre el icono correspondiente. Ahí va el
código, de la parte de la clase controladora, UsuariosController.java:

....... Después del método postEdit() ........


/**
* Borra un usuario
*/
@RequestMapping(value = "/usuario/deleteUsuario", method = RequestMethod.GET)
public String getDelete(@RequestParam("idusuario") Integer idUsuario,
Principal principal) throws ServletException, IOException {

logger.info("Borrando usuario " + idUsuario);

// Borramos el usuario
Usuarios usuario = usuariosService.leerUsuario(idUsuario);
usuariosService.borrarUsuario(usuario);
logger.info("Usuario Borrado");

// Redireccionamos a usuarios.jsp
return "redirect:/usuarios.htm";
}
....... Resto código hasta el final de la clase ........

Ahora bien, del lado de la página usuarios.jsp, vamos a tener que realizar unas cuantas modificaciones, para
poder implementar el diálogo de confirmación de borrado de usuario. Si bien es cierto, que una primera
aproximación al problema, podría ser la implementación del método showConfirmDialog(), de la clase
javax.swing.JOptionPane, enseguida debemos darnos cuenta de que ésto no es posible, ya que los
componentes de swing, no se pueden ejecutar en un servlet. Habría que programarlo para que se ejecutara
sólo ese componente fuera del servlet, y hay que tener en cuenta, que se ejecutaría del lado del cliente, con

196
lo cual, el cliente tendría que tener instalada la JVM... (A ver, demasiado complicado para implementar un sólo cuadro de diálogo, ¿no?). Tenemos otra
solución. Varias en concreto; Javascript, AJAX, jQuery, etc...

Nosotros vamos a implementar la solución con Javascript y estilos CSS. Por ahora, es una solución elegante y simple, teniendo en cuenta que el aprendizaje
de Javascript y CSS está fuera de los objetivos de éste curso. A continuación os dejo el código completo de la vista usuarios.jsp, con las modificaciones
realizadas resaltadas en amarillo. Ahí va:

'pitufosapp/src/main/webapp/WEB-INF/views/usuarios.jsp ':
<%@ include file="/WEB-INF/views/include.jsp" %>
<html>
<!-- Creamos una función Javascript que contendrá el cuadro de diálogo de confirmación de borrado de usuario.
detrás del cuadro, colocaremos una capa grisácea semitransparente que oscurezca todo lo que queda detrás,
dando un efecto visual de encontrase ante una ventana modal (que lo es; Por cierto, una ventana modal, es
un tipo de ventana que no permite interactuar con el resto de la aplicación hasta que no se termina de
interactuar con ella.) -->
<script type="text/javascript">
function confirmar() {
document.getElementById("fade").style.display="block";
if(confirm("¿Esta seguro?")) {
document.getElementById("fade").style.display="none";
return true;
} else {
document.getElementById("fade").style.display="none";
return false;
}
}
</script>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
/* Estilo para la base semi-transparente */
.overlay {
margin: 0;
padding: 0;
display: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;

197
background: #000;
z-index: 1001;
opacity: 0.75;
-moz-opacity: 0.75;
filter: alpha(opacity = 75);
}
</style>
<title>Usuarios</title>
</head>
<body>
<!-- Este será el componente que hará de capa semitransparente -->
<div id="fade" class="overlay"></div>
<!-- Establecemos recursos para la página web -->
<c:url var="editImgUrl" value="/resources/img/edit.png" />
<c:url var="deleteImgUrl" value="/resources/img/delete.png" />
<!-- Establecemos la dirección donde se ubicará la página para añadir usuarios -->
<c:url var="addUsuarioUrl" value="/usuario/addUsuario.htm" />
<!-- Creamos una tabla, con las cabeceras necesarias y un poquito de estilo... -->
<table style="border: 1px solid; width: 100%; text-align:center">
<thead style="background:#d3dce3">
<tr>
<!-- Parte de la tabla correspondiente a los datos de usuario -->
<th>Id</th>
<th>Usuario</th>
<th>Password</th>
<th>Enabled</th>
<th colspan="2"></th>
<!-- Parte de la tabla correspondiente a los roles de usuario -->
<th>Rol</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody style="background:#ccc">
<!-- Recogemos el atributo "usuarios" del modelo, el cual ha sido proporcionado por
la clase UsuariosController.java y lo vamos a recorrer, introduciendo cada item
cada vez, en una variable que llamamos "usuario" -->
<c:forEach items="${usuarios}" var="usuario">
<!-- Establecemos las direcciones donde se ubicarán las páginas
para editar y eliminar usuarios -->
<c:url var="editUsuarioUrl" value="/usuario/editUsuario.htm?idusuario=${usuario.idusuario}" />
<c:url var="deleteUsuarioUrl" value="/usuario/deleteUsuario.htm?idusuario=${usuario.idusuario}" />

198
<!-- Condición: Si la lista de roles de usuario no está vacía... -->
<c:if test="${!empty usuario.rolesUsuario}">
<!-- Recorremos su lista de roles introduciendo cada item cada vez, en una variable
que llamamos "rol" -->
<c:forEach items="${usuario.rolesUsuario}" var="rol">
<!-- Por cada rol, sacamos en la tabla su usuario correspondiente y
sus enlaces para su edición y eliminación -->
<tr>
<!-- Parte de la tabla correspondiente a los datos de usuario -->
<td><c:out value="${usuario.idusuario}" /></td> <!-- Id -->
<td><c:out value="${usuario.usuario}" /></td> <!-- Usuario -->
<td><c:out value="${usuario.password}" /></td> <!-- Password -->
<td><c:out value="${usuario.enabled}" /></td> <!-- Enabled -->
<td><a href="${editUsuarioUrl}"><img src="${editImgUrl}"></img></a></td> <!-- Colspan 1 de 2 -->
<!-- Añadimos el evento onclick en el enlace definido, para que llame a nuestro Javascript -->
<td>
<a href="${deleteUsuarioUrl}" onclick = "return confirmar()"><img src="${deleteImgUrl}"></img></a>
</td><!-- Colspan 2 de 2 -->
<!-- Parte de la tabla correspondiente a los roles de usuario -->
<td><c:out value="${rol.rol}" /></td> <!-- Rol -->
<!-- Por cada rol, configuramos sus enlaces para su adición, edición y eliminación -->
<c:url var="addRolUrl" value="/rol/addRol.htm?idusuario=${usuario.idusuario}" />
<c:url var="editRolUrl" value="/rol/editRol.htm?pidusuario=${usuario.idusuario}&cidrol=${rol.idrol}" />
<c:url var="deleteRolUrl" value="/rol/deleteRol.htm?idrol=${rol.idrol}" />
<!-- Por cada rol, sacamos sus enlaces para su adición, edición y eliminación -->
<td><a href="${addRolUrl}">+</a></td> <!-- Colspan 1 de 3 -->
<td><a href="${editRolUrl}"><img src="${editImgUrl}"></img></a></td> <!-- Colspan 2 de 3 -->
<td><a href="${deleteRolUrl}"><img src="${deleteImgUrl}"></img></a></td> <!-- Colspan 3 de 3 -->
</tr>
<!-- Fin del recorrido de los items que haya en la lista de roles -->
</c:forEach>
<!-- Fin del tratamiento de la condición de que la lista de roles de usuario no esté vacía -->
</c:if>
<!-- Condición: Si la lista de roles de usuario está vacía... -->
<c:if test="${empty usuario.rolesUsuario}">
<!-- Rellenamos la tabla convenientemente, sacando el usuario y los enlaces pertinentes -->
<tr>
<td><c:out value="${usuario.idusuario}" /></td>
<td><c:out value="${usuario.usuario}" /></td>
<td><c:out value="${usuario.password}" /></td>
<td><c:out value="${usuario.enabled}" /></td>

199
<td><a href="${editUsuarioUrl}"><img src="${editImgUrl}"></img></a></td>
<!-- Añadimos el evento onclick en el enlace definido, para que llame a nuestro Javascript -->
<td>
<a href="${deleteUsuarioUrl}" onclick = "return confirmar()"><img src="${deleteImgUrl}"></img></a>
</td><!-- Colspan 2 de 2 -->
<td>N/A</td>
<c:url var="addRolUrl" value="/rol/addRol.htm?idusuario=${usuario.idusuario}" />
<td><a href="${addRolUrl}">+</a></td>
<td></td>
<td></td>
</tr>
<!-- Fin del tratamiento de la condición de que la lista de roles de usuario esté vacía -->
</c:if>
<!-- Fin del recorrido de los items que haya en la lista de usuarios -->
</c:forEach>
</tbody>
</table>
<!-- Si la lista de usuarios está vacía mostramos el mensaje correspondiente -->
<c:if test="${empty usuarios}">
No se han encontrado usuarios.
</c:if>
<!-- Mostramos bajo la tabla el enlace para añadir usuarios -->
<p><a href="${addUsuarioUrl}">Crear usuario</a></p>
</body>
</html>

200
Si a continuación ejecutamos nuestra aplicación, y navegamos hasta la página de usuarios, haciendo clic
sobre el botón de eliminación del usuario pitufo, nos deberá aparecer esto:

Si clicamos en la opción Cancelar o directamente cerramos el diálogo, se nos devolverá a la página de


usuarios, sin realizar ninguna modificación. Si clicamos en la opción Aceptar, nos devolverá de nuevo a la
página de usuarios, pero habiendo realizado el borrado del usuario correspondiente de la base de datos, y
podremos ver la página de usuarios actualizada.

En éste punto, sólo nos queda realizar una pequeña modificación en nuestro código para implementar las
funcionalidades CRUD asociadas a los roles de usuario. Yo soy partidario de que cuando se dé de alta un
usuario, se introduzcan todos los datos asociados al mismo, el problema aquí, es que no vamos a complicar
el código para registrar otro validador para el mismo formulario, por lo que implementaremos dos nuevos
formularios addRol.jsp y editRol.jsp, para que incluyan un ComboBox que nos permita seleccionar el tipo de
rol del que dispondrá un usuario. Vamos allá!

201
La validación de éste campo que vamos a añadir, la realizaremos usando el objeto Dto asociado a la clase
entidad, en este caso será RolesDto.java, junto con su RolesDtoValidator.java, el cual inyectaremos como
bean en la clase controladora UsuariosController.java.

Realmente, podríamos evitar tener que registrar otro validador complicando el código, así como
implementar las clases RolesDto, RolesDtoValidator y una nueva clase controladora,
RolesController, simplemente modificando la Clase UsuariosDto y UsuariosDtoValidator,
añadiendo el código de soporte para el manejo del campo "rol", pero vamos a ser "puristas" en
éste sentido, y lo vamos a hacer a la manera tradicional, aunque sí que nos vamos a ahorrar la
clase RolesController, por cuestiones de eficiencia que explicaré un poco más adelante.

Primero creamos la clase RolesDtoValidator.java:

'pitufosapp/src/main/java/com/guarderias/pitufos/servicio/validacion/RolesDtoValidator.java ':
package com.guarderias.pitufos.servicio.validacion;

import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

import com.guarderias.pitufos.servicio.dto.RolesDto;

@Component // Indicamos que será un bean, que después inyectaremos.


public class RolesDtoValidator implements Validator {

/*
* Establecemos unas constantes para el chequeo
* de los roles a seleccionar
*/
private static final String USER_ROL = "ROLE_USER";
private static final String ADMIN_ROL = "ROLE_ADMIN";

@SuppressWarnings("rawtypes")
public boolean supports(Class clazz) {
return RolesDto.class.isAssignableFrom(clazz);
}

public void validate(Object target, Errors errors) {

// Convertimos el objeto sobre el que se va a validar a nuestro Dto.


RolesDto rol = (RolesDto) target;

// Realizamos la validación del tipo de rol


if(!rol.getRol().equalsIgnoreCase(USER_ROL) &&
!rol.getRol().equalsIgnoreCase(ADMIN_ROL)) {

errors.rejectValue("rol", "Rol");
}
}
}

202
Modificamos el archivo messages.properties para añadir la clave "Rol", que contendrá el mensaje a mostrar
en caso de no seleccionar un rol para el usuario:

'pitufosapp/src/main/webapp/WEB-INF/classes/messages.properties ':
............Resto de Claves/Valor............
Rol=Debe seleccionar un rol

A continuación, modificaremos sustancialmente nuestra clase UsuariosController.java, retocando todo el


código y añadiendo las funcionalidades neesarias para el manejo de todo lo referente a los roles de usuarios:

'pitufosapp/src/main/java/com/guarderias/pitufos/web/UsuariosController.java ':
package com.guarderias.pitufos.web;

import java.io.IOException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.encoding.ShaPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import com.guarderias.pitufos.dominio.Roles;
import com.guarderias.pitufos.dominio.Usuarios;
import com.guarderias.pitufos.servicio.dto.RolesDto;
import com.guarderias.pitufos.servicio.dto.UsuariosDto;
import com.guarderias.pitufos.servicio.manager.RolesManager;
import com.guarderias.pitufos.servicio.manager.UsuariosManager;
import com.guarderias.pitufos.servicio.validacion.RolesDtoValidator;
import com.guarderias.pitufos.servicio.validacion.UsuariosDtoValidator;

/**
* Maneja los formularios de usuarios y roles de la app.
*/
@Controller
public class UsuariosController {

protected final Log logger = LogFactory.getLog(getClass());

203
// Declaramos los objetos que actuarán como el modelo de datos.
private UsuariosDto usuarioModel = new UsuariosDto();
private RolesDto rolModel = new RolesDto();

@Autowired
private UsuariosDtoValidator usuariosValidator;

/*
* Inyectamos el recurso de tipo servicio llamado "usuariosService".
* Este recurso es un bean que se declaró mediante la anotación
* @Service("usuariosService"), cuando se desarrolló la clase
* UsuariosManagerImpl.java. Lo que hacemos aquí es usar su interfaz
* para leer los datos.
*/
@Resource(name="usuariosService")
private UsuariosManager usuariosService;

// Inyectamos el validador para los roles.


@Autowired
private RolesDtoValidator rolesValidator;

// Inyectamos el recurso de servicio para los roles.


@Resource(name="rolesService")
private RolesManager rolesService;

/*
* Agregamos un atributo de modelo llamado "Usuario"
* que se inyectará como bean y sobre el que trabajaremos.
*/
@ModelAttribute("Usuario")
public UsuariosDto createUsuarioModel() {
return usuarioModel;
}

/*
* Agregamos un atributo de modelo llamado "Rol"
* que se inyectará como bean y sobre el que trabajaremos.
*/
@ModelAttribute("Rol")
public RolesDto createRolModel() {
return rolModel;
}

// Iniciamos el validador, restrigiéndolo al atributo de modelo "Usuario".


@InitBinder("Usuario")
private void initUsuarioBinder(WebDataBinder binder) {
binder.setValidator(usuariosValidator);
}

// Iniciamos el validador, restrigiéndolo al atributo de modelo "Rol".


@InitBinder("Rol")
private void initRolBinder(WebDataBinder binder) {
binder.setValidator(rolesValidator);
}

204
/**
* Recupera la vista usuarios.
*/
@RequestMapping(value = "/usuarios", method = RequestMethod.GET)
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response, Model model, Principal principal ) throws
ServletException, IOException {

logger.info("Devolviendo la vista de usuarios");

/*
* Recogemos el nombre del usuario validado por spring security
* y lo añadimos al modelo.
*/
String name = principal.getName();
model.addAttribute("username", name);

// Recuperamos la lista de usuarios.


List<Usuarios> usuarios = usuariosService.leerUsuarios();

// Preparamos el objeto que vamos a añadir al modelo.


List<UsuariosDto> usuariosDTO = new ArrayList<UsuariosDto>();

/*
* Preparamos la lista de usuarios mediante un objeto dto
* y la añadimos al objeto que vamos a añadir al modelo
* (que es una lista de objetos dto).
*/
for(Usuarios usuario: usuarios) {
// preparamos un dto para configurar la lista .
UsuariosDto dto = new UsuariosDto();
// Lo rellenamos con los datos de su clase entidad correspondiente.
dto.setIdusuario(usuario.getIdusuario());
dto.setUsuario(usuario.getUsuario());
dto.setPassword(usuario.getPassword());
dto.setEnabled(usuario.getEnabled());
dto.setRolesUsuario(usuario.getRolesUsuario());

// Añadimos el dto a la lista.


usuariosDTO.add(dto);
}

// Añadimos el objeto (la lista de objetos dto), al modelo.


model.addAttribute("usuarios", usuariosDTO);

// Devolvemos la vista y su modelo correspondientes.


return new ModelAndView("usuarios", "model", model);
}

/**
* Recupera la vista añadir usuario "/usuario/addUsuario"
*/
@RequestMapping(value = "/usuario/addUsuario", method = RequestMethod.GET)
public ModelAndView getAddUsuario(Model model, Principal principal )
throws ServletException, IOException {

logger.info("Devolviendo la vista para añadir usuarios");

205
// Creamos un nuevo objeto UsuariosDto y lo añadimos al modelo.
model.addAttribute("Usuario", new UsuariosDto());

// Devolvemos la vista addUsuario.jsp y su modelo correspondiente.


return new ModelAndView("addUsuario", "model", model);
}

/**
* Añade un nuevo Usuario.
*/
@RequestMapping(value = "/usuario/addUsuario", method = RequestMethod.POST)
public String postAddUsuario(
@ModelAttribute("Usuario") @Valid UsuariosDto usuario,
BindingResult result, Principal principal)
throws ServletException, IOException {

logger.info("Añadiendo un nuevo usuario");

// Si hay errores en la validación, nos devuelve al formulario de nuevo.


if(result.hasErrors())
return "addUsuario";

// Encriptamos la contraseña con el algoritmo SHA-1.


ShaPasswordEncoder shaEncoder = new ShaPasswordEncoder();
String passwordEncriptado =
shaEncoder.encodePassword(usuario.getPassword(), null);
logger.info(passwordEncriptado);

// Transferimos los datos del objeto DTO al objeto de Dominio.


Usuarios usr = new Usuarios();
usr.setUsuario(usuario.getUsuario());
usr.setPassword(passwordEncriptado);
usr.setEnabled(usuario.getEnabled());

// Delegamos en el servicio...
usuariosService.crearUsuario(usr);

// Redireccionamos a usuarios.jsp.
return "redirect:/usuarios.htm";
}

/**
* Recupera la vista editar usuario "/usuario/editUsuario"
*/
@RequestMapping(value = "/usuario/editUsuario", method = RequestMethod.GET)
public ModelAndView getEditUsuario(@RequestParam("idusuario") Integer idUsuario,
Model model, Principal principal )
throws ServletException, IOException {

logger.info("Devolviendo la vista para editar usuarios");

// Recuperamos el objeto Usuario por su id.


Usuarios usuario = usuariosService.leerUsuario(idUsuario);

// Creamos un dto del usuario y lo llenamos de los datos correspondientes.


usuarioModel.setIdusuario(usuario.getIdusuario());
usuarioModel.setUsuario(usuario.getUsuario());
usuarioModel.setPassword(usuario.getPassword());

206
usuarioModel.setEnabled(usuario.getEnabled());
usuarioModel.setRolesUsuario(usuario.getRolesUsuario());

// Añadimos el dto al modelo.


model.addAttribute("Usuario", usuarioModel);

// Devolvemos la vista editUsuario.jsp y su modelo correspondiente.


return new ModelAndView("editUsuario", "model", model);
}

/**
* Edita un usuario.
*/
@RequestMapping(value = "/usuario/editUsuario", method = RequestMethod.POST)
public String postEditUsuario(@RequestParam("idusuario") Integer idUsuario,
@ModelAttribute("Usuario") @Valid UsuariosDto usuario,
BindingResult result, Principal principal)
throws ServletException, IOException {

logger.info("Editando usuario");

// Si hay errores en la validación, nos devuelve al formulario de nuevo.


if(result.hasErrors())
return "editUsuario";

// Encriptamos la contraseña con el algoritmo SHA-1.


ShaPasswordEncoder shaEncoder = new ShaPasswordEncoder();
String passwordEncriptado =
shaEncoder.encodePassword(usuario.getPassword(), null);
logger.info(passwordEncriptado);

// Transferimos los datos del objeto DTO al objeto de Dominio.


Usuarios usr = new Usuarios();
usr.setIdusuario(idUsuario);
usr.setUsuario(usuario.getUsuario());
usr.setPassword(passwordEncriptado);
usr.setEnabled(usuario.getEnabled());

// Delegamos en el servicio...
usuariosService.actualizarUsuario(usr);

// Redireccionamos a usuarios.jsp.
return "redirect:/usuarios.htm";
}

/**
* Borra un usuario
*/
@RequestMapping(value = "/usuario/deleteUsuario", method = RequestMethod.GET)
public String getDelete(@RequestParam("idusuario") Integer idUsuario,
Principal principal) throws ServletException, IOException {

logger.info("Borrando usuario " + idUsuario);

// Borramos el usuario.
Usuarios usuario = usuariosService.leerUsuario(idUsuario);
usuariosService.borrarUsuario(usuario);
logger.info("Usuario Borrado");

207
// Redireccionamos a usuarios.jsp.
return "redirect:/usuarios.htm";
}

/**
* Recupera la vista añadir rol "/rol/addRol"
*/
@RequestMapping(value = "/rol/addRol", method = RequestMethod.GET)
public ModelAndView getAddRol(@RequestParam("idusuario") Integer idusuario,
Model model, Principal principal )
throws ServletException, IOException {

logger.info("Devolviendo la vista para añadir roles");

// Recuperamos al usuario por su id.


Usuarios usuario = usuariosService.leerUsuario(idusuario);

// Transferimos el usuario al dto del modelo.


usuarioModel.setIdusuario(usuario.getIdusuario());
usuarioModel.setUsuario(usuario.getUsuario());
usuarioModel.setPassword(usuario.getPassword());
usuarioModel.setEnabled(usuario.getEnabled());
usuarioModel.setRolesUsuario(usuario.getRolesUsuario());

// Añadimos los objetos al modelo.


model.addAttribute("idusuario", idusuario);
model.addAttribute("Rol", new RolesDto());
model.addAttribute("Usuario", usuarioModel);

// Devolvemos la vista addRol.jsp y su modelo correspondiente.


return new ModelAndView("addRol", "model", model);
}

/**
* Añade un nuevo Rol al usuario seleccionado.
*/
@RequestMapping(value = "/rol/addRol", method = RequestMethod.POST)
public String postAddRol(@RequestParam("idusuario") Integer idusuario,
@ModelAttribute("Rol") @Valid RolesDto rol,
BindingResult result, Principal principal)
throws ServletException, IOException {

logger.info("Añadiendo un nuevo rol " + rol.getRol());

// Si hay errores en la validación, nos devuelve al formulario de nuevo.


if(result.hasErrors())
return "addRol";

// Transferimos los datos del objeto DTO al objeto de Dominio.


Roles rl = new Roles();
rl.setRol(rol.getRol());

// Le asociamos al rol, el usuario seleccionado.


rl.setUsuario(usuariosService.leerUsuario(idusuario));

// Delegamos en el servicio...
rolesService.crearRol(rl);

208
// Redireccionamos a usuarios.jsp.
return "redirect:/usuarios.htm";
}

/**
* Recupera la vista editar rol "/rol/editRol"
*/
@RequestMapping(value = "/rol/editRol", method = RequestMethod.GET)
public ModelAndView getEditRol(@RequestParam("pidusuario") Integer idusuario,
@RequestParam("cidrol") Integer idrol,
Model model, Principal principal )
throws ServletException, IOException {

logger.info("Devolviendo la vista para editar roles de usuarios");

// Recuperamos el objeto Usuario por su id.


Usuarios usuario = usuariosService.leerUsuario(idusuario);

// Transferimos el objeto de modelo al dto.


usuarioModel.setIdusuario(usuario.getIdusuario());
usuarioModel.setUsuario(usuario.getUsuario());
usuarioModel.setPassword(usuario.getPassword());
usuarioModel.setEnabled(usuario.getEnabled());
usuarioModel.setRolesUsuario(usuario.getRolesUsuario());

// Creamos un objeto dto para el rol con el que vamos a trabajar.


Roles rol = rolesService.leerRol(idrol);
rolModel.setRol(rol.getRol());
rolModel.setIdrol(rol.getIdrol());
rolModel.setUsuario(rol.getUsuario());

// Añadimos los dto al modelo, junto con los parámetros.


model.addAttribute("cidrol", idrol);
model.addAttribute("pidusuario", idusuario);
model.addAttribute("Usuario", usuarioModel);
model.addAttribute("Rol", rolModel);

// Devolvemos la vista editUsuario.jsp y su modelo correspondiente.


return new ModelAndView("editRol", "model", model);
}

/**
* Edita un rol de usuario.
*/
@RequestMapping(value = "/rol/editRol", method = RequestMethod.POST)
public String postEditRol(@RequestParam("cidrol") Integer idrol,
@RequestParam("pidusuario") Integer idusuario,
@ModelAttribute("Rol") @Valid RolesDto rolDto,
BindingResult result, Principal principal)
throws ServletException, IOException {

logger.info("Editando rol de usuario");

// Si hay errores en la validación, nos devuelve al formulario de nuevo.


if(result.hasErrors())
return "editRol";

// Transferimos los datos del objeto DTO al objeto de Dominio.


Roles rl = rolesService.leerRol(idrol);

209
rl.setRol(rolDto.getRol());

// Delegamos en el servicio...
rolesService.actualizarRol(rl);

// Redireccionamos a usuarios.jsp.
return "redirect:/usuarios.htm";
}

/**
* Borra un rol de usuario
*/
@RequestMapping(value = "/rol/deleteRol", method = RequestMethod.GET)
public String getDeleteRol(@RequestParam("idrol") Integer idrol,
Principal principal) throws ServletException, IOException {

logger.info("Borrando rol " + idrol);

// Borramos el rol de usuario.


Roles rol = rolesService.leerRol(idrol);
rolesService.borrarRol(rol);
logger.info("Rol Borrado");

// Redireccionamos a usuarios.jsp.
return "redirect:/usuarios.htm";
}
}

Como podéis observar, he modificado casi todo el código de la clase. De ésta forma os muestro varias cosas
nuevas e interesantes:

 Hemos declarado dos variables globales de los objetos DTO, que ahora actuarán como atributos de
modelo en toda la clase. Esto nos facilita la existencia de dichos objetos en el modelo, mientras se
muestren los formularios, puesto que hemos añadido también los métodos de creación y asignación
automática al modelo de datos por spring mediante las anotaciones @ModelAttribute("Usuario")
y @ModelAttribute("Rol").
 Hemos declarado el servicio rolesService, que nos inyectará el soporte a las interfaces de
administración de los roles de usuario.
 Hemos declarado los validadores de los formularios nuevamente, pero ésta vez, los hemos iniciado
restringidos a los objetos DTO sobre los que van a actuar, lo que nos permite mezclar ambos DTO en
un formulario, pero validando sólo el objeto DTO que se va a modificar. Para ésto, también se podría
haber utilizado una clase del tipo CompositeValidator que nos permite añadir varios validadores al
mismo formulario, pero no he querido complicar el código.
 Se ha retocado el método getEditUsuario(), adaptándolo a la nueva situación con respecto a la
nueva forma de gestionar los atributos de modelo, mejorando con ello su rendimiento y la fiabilidad .
 Se han añadido los métodos de manejo de la parte del formulario usuarios.jsp, así como los métodos
de manejo de los formularios addRol.jsp y editRol.jsp.
 También se ha añadido el método de borrado de roles de usuario.

210
Bien. Sé que os parecerá un cambio muy brusco en la programación de la aplicación , pero creo que de ésta
forma habéis podido ver por vosotros mismos que hay innumerables formas de desarrollar el controlador
para un mismo conjunto de formularios, a cual mejor. Os dejo la implementación de los cambios realizados
en la vista usuarios.jsp para implementar la funcionalidad Javascript de borrar roles de usuario, con diálogo
de confirmación de borrado:

'pitufosapp/src/main/webapp/WEB-INF/views/usuarios.jsp ':
<!-- Por cada rol, sacamos sus enlaces para su adición, edición y eliminación -->
<td><a href="${addRolUrl}">+</a></td> <!-- Colspan 1 de 3 -->
<td><a href="${editRolUrl}"><img
src="${editImgUrl}"></img></a></td> <!-- Colspan 2 de 3 -->
<!-- Añadimos el evento onclick en el enlace definido, para
que llame a nuestro Javascript -->
<td>
<a href="${deleteRolUrl}" onclick = "return confirmar()">
<img src="${deleteImgUrl}"></img>
</a>
</td> <!-- Colspan 3 de 3 -->
</tr>
<!-- Fin del recorrido de los items que haya en la lista de roles-->
</c:forEach>
<!-- Fin del tratamiento de la condición de que la lista de roles de
usuario no esté vacía -->

Y por último, creamos nuestras vistas addRol.jsp y editRol.jsp:

'pitufosapp/src/main/webapp/WEB-INF/views/addRol.jsp ':
<%@ include file="/WEB-INF/views/include.jsp"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style>
.errorblock {
color: #FF0000;
background-color: #EEFFBA;
border: 2px solid #FF0000;
}
</style>
</head>
<body>
<h1>Crear nuevo Rol de Usuario</h1>
<!-- Establecemos recursos para la página web -->
<c:url var="saveUrl" value="/rol/addRol.htm?idusuario=${Usuario.idusuario}" />
<form:form modelAttribute="Rol" method="POST"
action="${saveUrl}">
<table>
<tr>
<td>Id Usuario:</td>
<td><input type="text" value="${Usuario.idusuario}"
disabled="disabled" /></td>
</tr>
<tr>
<td><form:label path="rol">Rol:</form:label></td>
<td><form:select path="rol">
<form:option value="N/A" label="seleccionar rol" />
<form:option value="ROLE_USER" label="usuario" />

211
<form:option value="ROLE_ADMIN" label="administrador" />
</form:select></td>
<td><form:errors path="rol" cssClass="errorblock" /></td>
</tr>
<tr>
<!--<td><form:errors path="*" cssClass="errorblock" /></td>-->
</tr>
</table>
<input type="submit" value="Guardar" />
</form:form>
</body>
</html>

'pitufosapp/src/main/webapp/WEB-INF/views/editRol.jsp ':
<%@ include file="/WEB-INF/views/include.jsp"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style>
.errorblock {
color: #FF0000;
background-color: #EEFFBA;
border: 2px solid #FF0000;
}
</style>
</head>
<body>
<h1>Editar nuevo Rol de Usuario</h1>
<!-- Establecemos recursos para la página web -->
<c:url var="saveUrlRol"

value="/rol/editRol.htm?pidusuario=${Usuario.idusuario}&cidrol=${Rol.idrol}" />
<form:form modelAttribute="Rol" method="POST" action="${saveUrlRol}">
<table>
<tr>
<td>Id Usuario:</td>
<td><input type="text" value="${Usuario.idusuario}"
disabled="disabled" /></td>
</tr>
<tr>
<td><form:label path="idrol">Id Rol:</form:label></td>
<td><form:input path="idrol" disabled="true" /></td>
</tr>
<tr>
<td><form:label path="rol">Rol:</form:label></td>
<td><form:select path="rol">
<form:option value="N/A" label="seleccionar rol" />
<form:option value="ROLE_USER" label="usuario" />
<form:option value="ROLE_ADMIN" label="administrador" />
</form:select></td>
<td><form:errors path="rol" cssClass="errorblock" /></td>
</tr>
</table>
<input type="submit" value="Guardar" />
</form:form>
</body>
</html>

212
Si os fijáis en la forma de crear el ComboBox, veréis que se ha creado directamente con cadenas
de texto declaradas en el mismo formulario. Lógicamente, también es posible crearlos y
rellenarlos con listas provenientes de talas de bases de datos, para lo cual, dicha lista, debería
ser convenientemente declarada como atributo de modelo en la clase controladora, y
alimentarse de una tabla preexistente en la base de datos que recogiera los registros que
conformarían la lista en cuestión. No lo he hecho así, porque simplemente el diseño de nuestra
bd no ha sido de ésa forma, para no complicar mucho el diseño de nuestra aplicación, cosa que
no ocurriría con otras tablas que sí que ofrecen ésa posibilidad, como por ejemplo la de centros
o cursos, etc.

Para concluír ésta , vamos a realizar la interceptación de seguridad, o mejor dicho, la


securización del acceso a nuestras vistas. Queremos que sólo los usuarios que tengan roles de administrador
puedan acceder a la página de ususarios. No sería de recibo, dejar que cualquier usuario de la aplicación
pudiera crear, editar o eliminar a otros usuarios, por razones obvias. Pues bien, ésta faceta la vamos a
implementar realizando las interceptaciones correspondientes en el archivo spring-security.xml, cuyas
modificaciones correspondientes os dejo a continuación:

'pitufosapp/src/main/webapp/WEB-INF/spring/spring-security.xml':
<!-- Configuración de la interceptación http -->
<http auto-config="true" use-expressions="true"
access-denied-page="/accesoDenegado.htm">

<!-- Establecemos el mapeo para las páginas de login, logout,


redireccionamiento y los roles de acceso -->
<!-- Nota: Se puede sustituir por "hasRole('ROLE_USER')" ó
"hasRole('ROLE_ADMIN')"
si se quiere que sólo un usuario que tenga ése rol tenga acceso a la
página. -->
<intercept-url pattern="/home.htm"
access="hasAnyRole('ROLE_USER', 'ROLE_ADMIN')" />
<intercept-url pattern="/usuarios.htm" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/usuario/addUsuario.htm"
access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/usuario/editUsuario.htm"
access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/usuario/deleteUsuario.htm"
access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/rol/addRol.htm" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/rol/editRol.htm" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/rol/deleteRol.htm"
access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/login*" access="permitAll" />
<form-login login-page="/login.htm" always-use-default-target="true"
default-target-url="/home.htm"
authentication-failure-url="/loginfailed.htm" />
<logout invalidate-session="true" logout-success-url="/logout.htm" />
<remember-me/>
</http>

213
Introducimos los cambios correspondientes en la clase LoginController.java, para añadir el mapeo a nuestra
nueva vista accesoDenegado.jsp y de paso, depuramos un detallito que nos dejamos atrás cuando
configuramos la página de error de autenticación. Le añadimos la causa del error:

'pitufosapp/src/main/java/com/guarderias/pitufos/LoginController.java':
package com.guarderias.pitufos.web;

import java.security.Principal;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class LoginController {

@RequestMapping(value="/login", method = RequestMethod.GET)


public String login(ModelMap model) {

return "login";
}

@RequestMapping(value="/loginfailed", method = RequestMethod.GET)


public ModelAndView loginerror(ModelMap model) {

model.addAttribute("error", "true");
model.addAttribute("causa", "Usted ha escrito incorrectamente su nombre de
usuario o contraseña");
return new ModelAndView("login", "model", model);
}

@RequestMapping(value="/logout", method = RequestMethod.GET)


public String logout(ModelMap model) {

return "login";
}

@RequestMapping(value="/accesoDenegado", method = RequestMethod.GET)


public ModelAndView accesoDenegado(ModelMap model, Principal principal) {

String username = principal.getName();


model.addAttribute("error", "true");
model.addAttribute("mensaje", "Hola, "
+ username + " Usted tiene el acceso denegado a ésta página.");

return new ModelAndView("accesoDenegado", "model", model);


}
}

214
Y por último, creamos nuestra vista accesoDenegado.jsp, para mostrar nuestra página 403 personalizada:

'pitufosapp/src/main/webapp/WEB-INF/views/accesoDenegado.jsp ':
<%@ include file="/WEB-INF/views/include.jsp" %>
<html>
<body>
<h1>HTTP Status 403 - Acceso Denegado.</h1>
<h2>${model.mensaje}</h2>
<a href="<c:url value="/j_spring_security_logout" />"> Volver a intentarlo</a>
</body>
</html>

Ahora sólo basta con ejecutar la aplicación y comprobar dos cosas; Cuando fallamos en la autenticación y
cuando, habiéndonos autenticado con un usuario que sólo tiene el rol de usuario intentamos navegar a la
página de usuarios:

215
En fin, con ésto, terminamos la , de éste curso destinado al desarrollo completo y
funcional de una aplicación web completa usando las tecnologías señaladas al principio. Nos queda mucho, y
en la siguiente parte, ofreceré la posibilidad de configurar spring y spring security usando las nuevas
características de configuración programáticamente, así como finalizaremos el desarrollo de nuestra app.

Recordad, disfrutad tanto haciéndolo como yo he disfrutado escribiéndolo. Un saludo y hasta la próxima!

Autor
Julio Bellón Aguilera (jbafiddle@hotmail.com)

216

También podría gustarte