I. Introduction▲
I-A. Objectif▲
Tout au long de cet article, nous allons voir différents aspects de la réalisation d'une application Web avec le framework Spring. Les différentes étapes sont :
- développer une page Web affichant « Bonjour le monde » : création d'une JSP simple affichant le message et modification de la JSP simple avec Spring (avec et sans paramètre de requête) ;
- développer une page Web affichant les valeurs d'une base de données ;
- développer une page Web permettant de créer des valeurs en base de données ;
- développer une page Web permettant de supprimer des valeurs en base de données ;
- développer une page Web permettant de modifier des valeurs en base de données ;
- unifier ces pages afin d'en faire une application Web regroupant ces fonctionnalités.
Ces étapes sont l'occasion d'approcher diverses techniques : utilisation de ressources JNDI, Spring MVC (contrôleur, formulaire avec validation, mapping, tiles), internationalisation, service et DAO avec JPA.
En plus de Spring, la solution ORM (Object Relational Mapping ou Mapping objet-relationnel) Hibernate sera utilisée pour l'accès aux données en JPA (Java Persistence API) et Apache Tiles sera utilisé pour la mise en place d'un menu unifiant l'application.
Cet article est dans le même esprit qu'un article sur Struts : Application Struts pas à pas avec Eclipse (Web Tools Platform) et Tomcat
Spring (tout comme Struts) permet d'organiser une application Web selon le patron de conception MVC. L'image ci-dessus présente simplement ce patron de conception. Les différents éléments sont :
- Dispatcher Servlet : cette partie est fournie par Spring. La servlet reçoit les requêtes HTTP et dirige le traitement vers le contrôleur correspondant à l'URL de la requête (mapping) ;
- Modèle : il contient la partie « métier ». Dans notre cas, il sera implémenté par des DAO (Data Access Object ou Objet d'accès aux données) ;
- Vue : c'est la partie d'interface avec l'utilisateur. Dans notre cas, il s'agit des JSP (JavaServer Pages) ;
- Contrôleur : il la charge du choix du traitement à déclencher et des informations à afficher en fonction des entrées.
I-B. Prérequis▲
Il est important d'avoir des notions sur Java et les applications Web. Par ailleurs, l'article n'expliquera pas le fonctionnement ou la configuration d'Eclipse, Tomcat, Maven ou Hibernate.
I-C. Ressources▲
En complément à cet article, vous pouvez trouver des informations dans la documentation officielle de Spring ou dans la série d'articles « Spring MVC par l'exemple » de Serge Tahé.
I-D. Versions des logiciels▲
II. Première application : Bonjour le monde !▲
Dans ce chapitre, nous allons créer l'application, la déployer, créer une première page Web (le classique « Bonjour le monde ») puis nous modifierons cette page afin de rendre le message personnalisé en fonction de valeurs dans l'URL.
II-A. Création de l'application▲
Pour le moment, nous allons créer une application Web avec Maven.
Choisir « Maven Project » dans le menu de création.
Laisser les valeurs par défaut et cliquer que le bouton « Next > ».
Choisir l'archétype « maven-archetype-webapp » et cliquer sur le bouton « Next > ».
Entrer les valeurs « Group Id » et « Artifact Id » ; puis cliquer sur le bouton « Finish ».
Modifier le fichier « pom.xml » afin qu'il contienne la dépendance vers « servlet-api » :
<project
xmlns
=
"http://maven.apache.org/POM/4.0.0"
xmlns
:
xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xsi
:
schemaLocation
=
"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
>
<modelVersion>
4.0.0</modelVersion>
<groupId>
com.developpez.rpouiller</groupId>
<artifactId>
tutoriel-web-spring</artifactId>
<packaging>
war</packaging>
<version>
0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>
javax.servlet</groupId>
<artifactId>
servlet-api</artifactId>
<version>
2.5</version>
<scope>
compile</scope>
</dependency>
</dependencies>
</project>
Nous avons maintenant une application Web (pour le moment un peu vide) qui compile.
II-B. Déploiement de l'application▲
Nous allons maintenant voir comment déployer cette application dans un environnement de développement.
Faire un clic droit dans la vue « Servers » et choisir « New » / « Server ».
Choisir « Tomcat v7.0 Server » et cliquer sur « Next > ».
Saisir un dossier où Tomcat est installé et cliquer sur « Finish ».
Une fois le serveur créé, il faut faire un clic droit sur le serveur afin de choisir « Add and Remove… ».
Sélectionner le projet et cliquer sur « Add > » afin de l'ajouter dans le colonne de droite.
Cliquer sur le bouton « Finish ».
Faire un clic droit sur le serveur et choisir « Start ».
Après avoir démarré, ouvrir un navigateur Web à l'adresse : http://localhost:8080/tutoriel-web-spring/.
Pour relancer le déploiement, faire un clic droit sur le serveur et choisir « Restart ».
À partir de maintenant nous savons déployer et redéployer notre application sur le serveur déclaré dans Eclipse.
II-C. Création d'une JSP « bonjour.jsp »▲
Nous allons créer une JSP simple sans utilisation de Spring.
Supprimer le fichier « index.jsp » contenu dans le dossier « src/main/webapp ».
Cette étape consiste simplement à ajouter une JSP dans le projet Web. Pour le moment, il n'y a aucun lien avec Spring.
Créer un dossier « vues » dans le dossier « src/main/webapp ».
Créer un fichier « bonjour.jsp » (avec le contenu ci-dessous) dans ce nouveau dossier.
<%@
page
language
=
"java"
contentType
=
"text/html; charset=ISO-8859-1"
pageEncoding=
"ISO-8859-1"
%>
<!
DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<html>
<head>
<title>
Bonjour</title>
</head>
<body>
Bonjour le monde.
</body>
</html>
Après avoir déployé le projet dans le serveur Tomcat, ouvrir un navigateur Web à l'adresse : http://localhost:8080/tutoriel-web-spring/vues/bonjour.jsp.
Nous avons su créer une JSP (correspondant à la partie vue du patron de conception MVC).
II-D. Utilisation de Spring avec la JSP « bonjour.jsp »▲
Dans ce paragraphe, nous allons modifier l'application afin d'intégrer Spring. Cela reste une utilisation très simple de Spring, car elle se limite à l'utilisation des fichiers d'internationalisation. Toutefois, cela valide le déploiement correct des dépendances Maven ainsi que le chargement du fichier de configuration Spring.
Ajouter la dépendance vers « spring-webmvc » dans le fichier « pom.xml ».
<dependency>
<groupId>
org.springframework</groupId>
<artifactId>
spring-webmvc</artifactId>
<version>
4.0.2.RELEASE</version>
</dependency>
Modifier le fichier « web.xml » comme ci-dessous. Le listener « ContextLoaderListener » charge la configuration Spring à partir de la variable de contexte « contextConfigLocation ».
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd"
>
<web-app>
<context-param>
<param-name>
contextConfigLocation</param-name>
<param-value>
/WEB-INF/dispatcher-servlet.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
Créer le fichier « dispatcher-servlet.xml » dans « WEB-INF » comme ci-dessous. Le bean « messageSource » de classe « ReloadableResourceBundleMessageSource » (depuis Spring 1.0) est déclaré. Il permettra de charger les messages internationalisés dans des fichiers « messages_xx.properties » contenus à la racine du classpath.
<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
:
mvc
=
"http://www.springframework.org/schema/mvc"
xmlns
:
tx
=
"http://www.springframework.org/schema/tx"
xsi
:
schemaLocation
=
"http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"
>
<bean
id
=
"messageSource"
class
=
"org.springframework.context.support.ReloadableResourceBundleMessageSource"
>
<property
name
=
"basename"
value
=
"classpath:messages"
/>
<property
name
=
"defaultEncoding"
value
=
"ISO-8859-1"
/>
</bean>
</beans>
Créer le fichier « messages_fr.properties » dans « src/main/resources » comme ci-dessous. Le dossier peut également contenir, par exemple, des fichiers « messages_en.properties » pour la langue anglaise ou « messages.properties » pour les messages par défaut (quand il n'y a pas de fichier correspondant à la langue).
titre.bonjour
=Bonjour avec Spring
libelle.bonjour.lemonde
=Bonjour le monde avec Spring.
Modifier le fichier « bonjour.jsp » comme ci-dessous. On a ajouté la déclaration de la taglib « spring » et les utilisations du tag « spring:message ».
<%@
page
language
=
"java"
contentType
=
"text/html; charset=ISO-8859-1"
pageEncoding=
"ISO-8859-1"
%>
<%@taglib
uri
=
"http://www.springframework.org/tags"
prefix
=
"spring"
%>
<!
DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<html>
<head>
<title><spring
:
message
code=
"titre.bonjour"
/></title>
</head>
<body>
<spring
:
message
code=
"libelle.bonjour.lemonde"
/>
</body>
</html>
Après déploiement, on obtient le résultat ci-dessous à l'adresse : http://localhost:8080/tutoriel-web-spring/vues/bonjour.jsp.
Nous avons utilisé le système d'internationalisation de Spring et vérifié ainsi que les dépendances s'étaient déployées correctement.
II-E. Passage de donnée à la JSP « bonjour.jsp »▲
Nous allons maintenant modifier cela afin que la JSP affiche le texte en fonction d'une donnée. Pour cela, nous ajoutons un contrôleur qui transmettra une donnée à la JSP.
Pour avoir une présentation de Spring MVC, vous pouvez lire l'article :
- « Spring MVC par l'exemple » de Serge Tahé.
Ajouter la déclaration de la servlet « DispatcherServlet » dans le fichier « web.xml » comme ci-dessous. C'est la servletservlet qui redirigera les requêtes HTTP vers les contrôleurs qui conviennent (elle est équivalente à la « ActionServlet » de Struts).
<!-- Declaration de la servlet de Spring et de son mapping -->
<servlet>
<servlet-name>
servlet-dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>
contextConfigLocation</param-name>
<param-value>
/WEB-INF/dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>
1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>
servlet-dispatcher</servlet-name>
<url-pattern>
/</url-pattern>
</servlet-mapping>
Voici la classe « BonjourController ». Elle est déclarée en tant que contrôleur grâce à l'annotation « @Controller » (elle existe depuis Spring 2.5). L'annotation « @RequestMapping » (elle existe depuis Spring 2.5) indique que le contrôleur traite les requêtes GET dont l'URI est « /bonjour ». On constate que la valeur « Regis » est associée à l'attribut « personne » grâce à la méthode « addAttribute » de « ModelMap ». Ensuite, le contrôleur redirige vers la ressource « bonjour ».
package
com.developpez.rpouiller.controller;
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
@RequestMapping
(
"/bonjour"
)
public
class
BonjourController {
@RequestMapping
(
method =
RequestMethod.GET)
public
String afficherBonjour
(
final
ModelMap pModel) {
pModel.addAttribute
(
"personne"
, "Regis"
);
return
"bonjour"
;
}
}
Dans le fichier « dispatcher-servlet.xml », il faut ajouter les lignes ci-dessous. « component-scan » active la configuration par annotations. La déclaration du bean « InternalResourceViewResolver » (qui existe depuis Spring 1.0) permet d'indiquer où chercher les ressources (ici la ressource « bonjour » indiquée dans le contrôleur sera cherchée avec l'extension « .jsp » dans le dossier « /vues/ ».
<
context
:
component-scan
base-package
=
"com.developpez.rpouiller"
/>
<bean
class
=
"org.springframework.web.servlet.view.InternalResourceViewResolver"
>
<property
name
=
"prefix"
>
<value>
/vues/</value>
</property>
<property
name
=
"suffix"
>
<value>
.jsp</value>
</property>
</bean>
Modifier la valeur ci-dessous dans le fichier « messages_fr.properties » afin de prendre en compte un paramètre dans le texte.
libelle.bonjour.lemonde
=Bonjour {0}
avec Spring.
Modifier le fichier « bonjour.jsp » comme ci-dessous. On a ajouté des Expressions Languages « ${personne} » afin de restituer la donnée.
<%@
page
language
=
"java"
contentType
=
"text/html; charset=ISO-8859-1"
isELIgnored=
"false"
pageEncoding=
"ISO-8859-1"
%>
<%@taglib
uri
=
"http://www.springframework.org/tags"
prefix
=
"spring"
%>
<!
DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<html>
<head>
<title><spring
:
message
code=
"titre.bonjour"
/>
: ${personne}</title>
</head>
<body>
<spring
:
message
code=
"libelle.bonjour.lemonde"
arguments=
"${personne}"
/>
</body>
</html>
Pour avoir une présentation des Expressions Languages (EL), vous pouvez lire l'article :
Après déploiement, on obtient le résultat ci-dessous à l'adresse : http://localhost:8080/tutoriel-web-spring/bonjour.
Nous avons passé une valeur dans un attribut depuis le contrôleur que nous avons affiché dans le JSP grâce aux Expressions Languages (EL).
II-F. Récupération d'un paramètre de la requête HTTP▲
Dans ce chapitre, nous allons récupérer un paramètre de la requête pour le passer à la place de la valeur de l'attribut.
Dans la classe « BonjourController », modifier la méthode « afficherBonjour » comme ci-dessous. Le paramètre « personne » est récupéré de la requête grâce à l'annotation « @RequestParam » (l'annotation existe depuis Spring 2.5 mais l'élément « defaultValue » a été rajouté avec Spring 3.0)
package
com.developpez.rpouiller.controller;
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.bind.annotation.RequestParam;
@Controller
@RequestMapping
(
"/bonjour"
)
public
class
BonjourController {
@RequestMapping
(
method =
RequestMethod.GET)
public
String afficherBonjour
(
final
ModelMap pModel,
@RequestParam
(
value=
"personne"
) final
String pPersonne) {
pModel.addAttribute
(
"personne"
, pPersonne);
return
"bonjour"
;
}
}
Après déploiement, on obtient le résultat ci-dessous à l'adresse : http://localhost:8080/tutoriel-web-spring/bonjour?personne=Spring.
Une alternative possible est d'extraire le paramètre depuis l'URI comme dans l'exemple ci-dessous. Cela est possible en indiquant dans l'annotation « @RequestMapping » qu'il y a une variable (indiqué avec les accolades). Ensuite, l'annotation « @PathVariable » (existant depuis Spring 3.0) permet d'indiquer l'utilisation de cette variable extraite de l'URI.
package
com.developpez.rpouiller.controller;
import
org.springframework.stereotype.Controller;
import
org.springframework.ui.ModelMap;
import
org.springframework.web.bind.annotation.PathVariable;
import
org.springframework.web.bind.annotation.RequestMapping;
import
org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping
(
"/bonjour/{personne}"
)
public
class
BonjourController {
@RequestMapping
(
method =
RequestMethod.GET)
public
String afficherBonjour
(
final
ModelMap pModel,
@PathVariable
(
value=
"personne"
) final
String pPersonne) {
pModel.addAttribute
(
"personne"
, pPersonne);
return
"bonjour"
;
}
}
Après déploiement, on obtient le résultat ci-dessous à l'adresse : http://localhost:8080/tutoriel-web-spring/bonjour/URI.
Nous avons vu comment récupérer un paramètre depuis la requête (dans cet exemple, dans une requête GET, donc dans l'URL, mais le principe est le même pour une requête POST) et comment parser une valeur dans l'URI.
III. Affichage de données en base▲
Dans ce chapitre, nous allons voir comment récupérer et afficher des valeurs contenues dans une base donnée.
III-A. Création de la base▲
Pour les besoins de ce tutoriel, nous utiliserons la base de données HSQLDB (HyperSQL DataBase). Dans ce chapitre, nous allons créer la base de données, une table et remplir cette table avec des valeurs de test.
Pour avoir une présentation de HSQLDB, vous pouvez lire l'article :
Il faut donc rajouter la dépendance Maven suivante :
<dependency>
<groupId>
org.hsqldb</groupId>
<artifactId>
hsqldb</artifactId>
<version>
2.3.1</version>
</dependency>
Voici la table qui sera utilisée en exemple dans ce tutoriel :
Voici son script SQL de création :
CREATE
TABLE
LISTECOURSES(
IDOBJET INTEGER
GENERATED
BY
DEFAULT
AS
IDENTITY
(
START
WITH
1
)
PRIMARY
KEY
,
LIBELLE VARCHAR
(
50
)
NOT
NULL
,
QUANTITE INTEGER
NOT
NULL
)
;
Pour lancer le gestionnaire de base HSQLDB, exécuter la classe « org.hsqldb.util.DatabaseManager ». Voici la connexion à la base de données d'exemple (ici avec l'URL « jdbc:hsqldb:file:C:\hsqldb\data\maBase ») :
Voici le résultat de la création de la table :
INSERT
INTO
LISTECOURSES(
LIBELLE, QUANTITE)
VALUES
(
'Banane'
, 3
)
;
INSERT
INTO
LISTECOURSES(
LIBELLE, QUANTITE)
VALUES
(
'Sucre blanc'
, 75
)
;
INSERT
INTO
LISTECOURSES(
LIBELLE, QUANTITE)
VALUES
(
'Oeuf'
, 1
)
;
INSERT
INTO
LISTECOURSES(
LIBELLE, QUANTITE)
VALUES
(
'Levure'
, 1
)
;
INSERT
INTO
LISTECOURSES(
LIBELLE, QUANTITE)
VALUES
(
'Sel'
, 1
)
;
INSERT
INTO
LISTECOURSES(
LIBELLE, QUANTITE)
VALUES
(
'Farine'
, 150
)
;
INSERT
INTO
LISTECOURSES(
LIBELLE, QUANTITE)
VALUES
(
'Beurre'
, 70
)
;
Voici le résultat de l'insertion de données dans la table :
III-B. Paramétrage du serveur Tomcat▲
Nous allons paramétrer la ressource JDBC dans le serveur Tomcat (depuis Eclipse). C'est cette ressource qui est reliée à la base HSQLDB et qui sera utilisée dans l'application.
Il faut ajouter la ressource ci-dessous dans la partie « GlobalNamingResources ».
<GlobalNamingResources>
...
<Resource
auth
=
"Container"
driverClassName
=
"org.hsqldb.jdbcDriver"
maxActive
=
"100"
maxIdle
=
"30"
maxWait
=
"10000"
name
=
"jdbc/dsMaBase"
password
=
""
type
=
"javax.sql.DataSource"
url
=
"jdbc:hsqldb:file:C:\hsqldb\data\maBase"
username
=
"sa"
/>
...
</GlobalNamingResources>
Dans le fichier « context.xml », il faut associer la ressource que l'on vient d'indiquer dans le fichier « server.xml » avec le nom « jdbc/dsMonApplication » que l'on utilisera dans l'application.
<ResourceLink
name
=
"jdbc/dsMonApplication"
global
=
"jdbc/dsMaBase"
type
=
"javax.sql.DataSource"
/>
III-C. Modification du projet afin d'inclure l'affichage des données▲
Nous allons modifier le projet afin de réaliser l'affichage des données que l'on vient d'ajouter en base de données.
Il faut rajouter la déclaration de la ressource JDBC de l'application dans le fichier « web.xml ».
<!-- Declaration de l'utilisation de la ressource JDBC -->
<resource-ref>
<description>
Ressource JDBC de l'application</description>
<res-ref-name>
jdbc/dsMonApplication</res-ref-name>
<res-type>
javax.sql.DataSource</res-type>
<res-auth>
Container</res-auth>
<res-sharing-scope>
Shareable</res-sharing-scope>
</resource-ref>
Il faut donc rajouter les dépendances Maven suivantes. Les dépendances « spring-orm » et « hibernate-entitymanager » sont pour l'accès aux données. Tandis que la dépendance « jstl » est pour l'utilisation de la JSTL (Java Standard Tag Library) dans la JSP d'affichage.
<dependency>
<groupId>
org.springframework</groupId>
<artifactId>
spring-orm</artifactId>
<version>
4.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>
org.hibernate</groupId>
<artifactId>
hibernate-entitymanager</artifactId>
<version>
4.3.4.Final</version>
</dependency>
<dependency>
<groupId>
javax.servlet</groupId>
<artifactId>
jstl</artifactId>
<version>
1.2</version>
</dependency>
Le fichier « persistence.xml » ci-dessous permet d'indiquer que la persistance est réalisée grâce à Hibernate.
<?xml version="1.0" encoding="UTF-8"?>
<persistence
version
=
"2.0"
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_2_0.xsd"
>
<persistence-unit
name
=
"unit"
>
<provider>
org.hibernate.jpa.HibernatePersistenceProvider</provider>
</persistence-unit>
</persistence>
Il faut rajouter les lignes ci-dessous dans le fichier « dispatcher-servlet.xml ». Le bean « JndiObjectFactoryBean » permet de déclarer l'utilisation de la ressource JDBC. Le bean « LocalContainerEntityManagerFactoryBean » utilise la ressource JDBC et le fichier « persistence.xml » pour aboutir à la création du « EntityManager » qui est utilisé dans la DAO. Le bean « JpaTransactionManager » permet d'instancier le gestionnaire de transaction et lui associer la fabrique de « EntityManager ».
<
tx
:
annotation-driven
transaction-manager
=
"transactionManager"
/>
<bean
id
=
"dataSource"
class
=
"org.springframework.jndi.JndiObjectFactoryBean"
>
<property
name
=
"jndiName"
value
=
"java:comp/env/jdbc/dsMonApplication"
/>
</bean>
<bean
id
=
"entityManagerFactory"
class
=
"org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
>
<property
name
=
"dataSource"
ref
=
"dataSource"
/>
</bean>
<bean
id
=
"transactionManager"
class
=
"org.springframework.orm.jpa.JpaTransactionManager"
>
<property
name
=
"entityManagerFactory"
ref
=
"entityManagerFactory"
/>
</bean>
La classe « Course » est une entité correspondant à la table « LISTECOURSES ».
package
com.developpez.rpouiller.bean;
import
javax.persistence.Column;
import
javax.persistence.Entity;
import
javax.persistence.GeneratedValue;
import
javax.persistence.GenerationType;
import
javax.persistence.Id;
import
javax.persistence.Table;
@Entity
@Table
(
name=
"LISTECOURSES"
)
public
class
Course {
@Id
@GeneratedValue
(
strategy=
GenerationType.IDENTITY)
@Column
(
name=
"IDOBJET"
)
private
Integer id;
private
String libelle;
private
Integer quantite;
public
Integer getId
(
) {
return
id;
}
public
void
setId
(
final
Integer pId) {
id =
pId;
}
public
String getLibelle
(
) {
return
libelle;
}
public
void
setLibelle
(
final
String pLibelle) {
libelle =
pLibelle;
}
public
Integer getQuantite
(
) {
return
quantite;
}
public
void
setQuantite
(
final
Integer pQuantite) {
quantite =
pQuantite;
}
}
Voici les interfaces de la DAO et du service : « IListeCoursesDAO » et « IServiceListeCourses ».
package
com.developpez.rpouiller.dao;
import
java.util.List;
import
com.developpez.rpouiller.bean.Course;
public
interface
IListeCoursesDAO {
List<
Course>
rechercherCourses
(
);
}
package
com.developpez.rpouiller.services;
import
java.util.List;
import
com.developpez.rpouiller.bean.Course;
public
interface
IServiceListeCourses {
List<
Course>
rechercherCourses
(
);
}
La DAO « ListeCoursesDAO » utilise le « EntityManager » pour lister les entités « Course » contenues dans la base de données.
package
com.developpez.rpouiller.dao;
import
java.util.List;
import
javax.persistence.EntityManager;
import
javax.persistence.PersistenceContext;
import
javax.persistence.TypedQuery;
import
javax.persistence.criteria.CriteriaBuilder;
import
javax.persistence.criteria.CriteriaQuery;
import
javax.persistence.criteria.Root;
import
org.springframework.stereotype.Repository;
import
com.developpez.rpouiller.bean.Course;
@Repository
public
class
ListeCoursesDAO implements
IListeCoursesDAO {
@PersistenceContext
private
EntityManager entityManager;
public
List<
Course>
rechercherCourses
(
) {
final
CriteriaBuilder lCriteriaBuilder =
entityManager.getCriteriaBuilder
(
);
final
CriteriaQuery<
Course>
lCriteriaQuery =
lCriteriaBuilder.createQuery
(
Course.class
);
final
Root<
Course>
lRoot =
lCriteriaQuery.from
(
Course.class
);
lCriteriaQuery.select
(
lRoot);
final
TypedQuery<
Course>
lTypedQuery =
entityManager.createQuery
(
lCriteriaQuery);
return
lTypedQuery.getResultList
(
);
}
}
Pour cette méthode, le service « ServiceListeCourses » sert surtout de passe-plat. Il faut noter toutefois que la transaction est indiquée en lecture seule.
package
com.developpez.rpouiller.services;
import
java.util.List;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.stereotype.Service;
import
org.springframework.transaction.annotation.Transactional;
import
com.developpez.rpouiller.bean.Course;
import
com.developpez.rpouiller.dao.IListeCoursesDAO;
@Service
public
class
ServiceListeCourses implements
IServiceListeCourses {
@Autowired
private
IListeCoursesDAO dao;
@Transactional
(
readOnly=
true
)
public
List<
Course>
rechercherCourses
(
) {
return
dao.rechercherCourses
(
);
}
}
Le contrôleur « AfficherListeCoursesController » appelle le service et place le résultat dans l'attribut « listeCourses ».
package
com.developpez.rpouiller.controller;
import
java.util.List;
import
org.springframework.beans.factory.annotation.Autowired;
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
com.developpez.rpouiller.bean.Course;
import
com.developpez.rpouiller.services.IServiceListeCourses;
@Controller
@RequestMapping
(
value=
"/afficherListeCourses"
)
public
class
AfficherListeCoursesController {
@Autowired
private
IServiceListeCourses service;
@RequestMapping
(
method =
RequestMethod.GET)
public
String afficher
(
ModelMap pModel) {
final
List<
Course>
lListeCourses =
service.rechercherCourses
(
);
pModel.addAttribute
(
"listeCourses"
, lListeCourses);
return
"listeCourses"
;
}
}
Il faut ajouter de nouveaux libellés dans le fichier « messages_fr.properties ».
titre.listecourses
=Liste de courses
colonne.identifiant
=IDOBJET
colonne.libelle
=LIBELLE
colonne.quantite
=QUANTITE
La JSP utilise la Java Standard Tag Library (JSTL) pour afficher le résultat.
<%@
page
language
=
"java"
contentType
=
"text/html; charset=ISO-8859-1"
isELIgnored=
"false"
pageEncoding=
"ISO-8859-1"
%>
<%@taglib
uri
=
"http://java.sun.com/jsp/jstl/core"
prefix
=
"c"
%>
<%@taglib
uri
=
"http://www.springframework.org/tags"
prefix
=
"spring"
%>
<!
DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<html>
<head>
<title><spring
:
message
code=
"titre.listecourses"
/></title>
</head>
<body>
<table border=
"1"
>
<thead>
<tr>
<th><spring
:
message
code=
"colonne.identifiant"
/></th>
<th><spring
:
message
code=
"colonne.libelle"
/></th>
<th><spring
:
message
code=
"colonne.quantite"
/></th>
</tr>
</thead>
<tbody>
<c
:
forEach
items=
"${listeCourses}"
var=
"course"
>
<tr>
<td><c
:
out
value=
"${course.id}"
/></td>
<td><c
:
out
value=
"${course.libelle}"
/></td>
<td><c
:
out
value=
"${course.quantite}"
/></td>
</tr>
</c
:
forEach
>
</tbody>
</table>
</body>
</html>
Pour avoir une présentation de la Java Standard Tag Library (JSTL), vous pouvez lire l'article :
Après déploiement, on obtient le résultat ci-dessous à l'adresse : http://localhost:8080/tutoriel-web-spring/afficherListeCourses.
IV. Création de données en base▲
Dans ce paragraphe, nous allons créer de nouvelles données dans la base de données. Pour cela, nous aurons besoin d'un formulaire que sera validé pour vérifier les données le constituant.
Il faut donc rajouter les dépendances Maven suivantes. Les dépendances « validation-api » et « hibernate-validator » permettent la validation du formulaire.
<dependency>
<groupId>
javax.validation</groupId>
<artifactId>
validation-api</artifactId>
<version>
1.1.0.Final</version>
</dependency>
<dependency>
<groupId>
org.hibernate</groupId>
<artifactId>
hibernate-validator</artifactId>
<version>
5.0.1.Final</version>
</dependency>
Il faut rajouter la ligne ci-dessous dans le fichier « dispatcher-servlet.xml ». Elle permet d'activer les annotations de validation de formulaire.
<
mvc
:
annotation-driven />
Il faut ajouter de nouveaux libellés dans le fichier « messages_fr.properties ».
titre.creation.elementcourses
=Création d'élément de la liste de courses
creation.elementcourses.libelle.libelle
=Libellé
creation.elementcourses.libelle.quantite
=Quantité
NotEmpty.creation.libelle
=Le libellé est nécessaire.
NotEmpty.creation.quantite
=La quantité est nécessaire.
Pattern.creation.quantite
=La quantité doit être numérique et positive.
Le formulaire « CreationForm » utilise les annotations « NotEmpty » et « Pattern » pour indiquer les contraintes de validation.
package
com.developpez.rpouiller.controller;
import
javax.validation.constraints.Pattern;
import
org.hibernate.validator.constraints.NotEmpty;
public
class
CreationForm {
@NotEmpty
private
String libelle;
@NotEmpty
@Pattern
(
regexp=
"
\\
d*"
)
private
String quantite;
public
String getLibelle
(
) {
return
libelle;
}
public
void
setLibelle
(
final
String pLibelle) {
libelle =
pLibelle;
}
public
String getQuantite
(
) {
return
quantite;
}
public
void
setQuantite
(
final
String pQuantite) {
quantite =
pQuantite;
}
}
Dans l'interface « IListeCoursesDAO » de la DAO, nous ajoutons la méthode recevant une entité en paramètre.
void
creerCourse
(
final
Course pCourse);
Tandis que dans l'interface « IServiceListeCourses » du service, nous ajoutons la méthode recevant en paramètres les valeurs de l'entité.
void
creerCourse
(
final
String pLibelle, final
Integer pQuantite);
L'implémentation de la méthode de la DAO « ListeCoursesDAO » sauve la nouvelle entité en base.
public
void
creerCourse
(
final
Course pCourse) {
entityManager.persist
(
pCourse);
}
L'implémentation de la méthode du service « ServiceListeCourses » constitue l'entité et l'envoie en paramètre à la DAO. Cette fois-ci la transaction n'est pas en lecture seule.
@Transactional
public
void
creerCourse
(
final
String pLibelle, final
Integer pQuantite) {
final
Course lCourse =
new
Course
(
);
lCourse.setLibelle
(
pLibelle);
lCourse.setQuantite
(
pQuantite);
dao.creerCourse
(
lCourse);
}
Le contrôleur « CreerListeCoursesController » comporte deux méthodes « afficher » et « creer ». La méthode « afficher » place la liste des courses dans l'attribut « listeCourses » et initialise le formulaire « creation » s'il n'est pas déjà présent dans l'attribut « creation ». L'annotation « @ModelAttribute » (existant depuis Spring 2.5) de la méthode « creer » indique que le paramètre « pCreation » est constitué à partir de l'attribut « creation ». L'annotation « @Valid » indique que le formulaire doit être validé grâce aux annotations contenues dans la classe de formulaire « CreationForm ». Ensuite, la méthode « creer » appelle la méthode de création en base de données s'il n'y a pas d'erreurs dans la validation, puis appelle simplement la méthode « afficher » pour l'affichage de la page.
Les messages d'erreur sont présents dans le fichier « messages_fr.properties ». On peut remarquer que dans ce cas les clés des messages sont de la forme « contrainteValidation.nomAttribut.nomChamp » (par exemple « NotEmpty.creation.libelle »).
package
com.developpez.rpouiller.controller;
import
java.util.List;
import
javax.validation.Valid;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.stereotype.Controller;
import
org.springframework.ui.ModelMap;
import
org.springframework.validation.BindingResult;
import
org.springframework.web.bind.annotation.ModelAttribute;
import
org.springframework.web.bind.annotation.RequestMapping;
import
org.springframework.web.bind.annotation.RequestMethod;
import
com.developpez.rpouiller.bean.Course;
import
com.developpez.rpouiller.services.IServiceListeCourses;
@Controller
public
class
CreerListeCoursesController {
@Autowired
private
IServiceListeCourses service;
@RequestMapping
(
value=
"/afficherCreationListeCourses"
, method =
RequestMethod.GET)
public
String afficher
(
final
ModelMap pModel) {
final
List<
Course>
lListeCourses =
service.rechercherCourses
(
);
pModel.addAttribute
(
"listeCourses"
, lListeCourses);
if
(
pModel.get
(
"creation"
) ==
null
) {
pModel.addAttribute
(
"creation"
, new
CreationForm
(
));
}
return
"creation"
;
}
@RequestMapping
(
value=
"/creerCreationListeCourses"
, method =
RequestMethod.POST)
public
String creer
(
@Valid
@ModelAttribute
(
value=
"creation"
) final
CreationForm pCreation,
final
BindingResult pBindingResult, final
ModelMap pModel) {
if
(!
pBindingResult.hasErrors
(
)) {
final
Integer lIntQuantite =
Integer.valueOf
(
pCreation.getQuantite
(
));
service.creerCourse
(
pCreation.getLibelle
(
), lIntQuantite);
}
return
afficher
(
pModel);
}
}
La JSP comporte le formulaire de création d'une nouvelle « course ». Ce formulaire contient également l'affichage des messages d'erreur.
<%@
page
language
=
"java"
contentType
=
"text/html; charset=ISO-8859-1"
isELIgnored=
"false"
pageEncoding=
"ISO-8859-1"
%>
<%@taglib
uri
=
"http://java.sun.com/jsp/jstl/core"
prefix
=
"c"
%>
<%@taglib
uri
=
"http://www.springframework.org/tags/form"
prefix
=
"form"
%>
<%@taglib
uri
=
"http://www.springframework.org/tags"
prefix
=
"spring"
%>
<!
DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<html>
<head>
<title><spring
:
message
code=
"titre.creation.elementcourses"
/></title>
</head>
<body>
<form
:
form
method=
"post"
modelAttribute=
"creation"
action=
"creerCreationListeCourses"
>
<spring
:
message
code=
"creation.elementcourses.libelle.libelle"
/>
<form
:
input
path=
"libelle"
/>
<b><i><form
:
errors
path=
"libelle"
cssclass=
"error"
/></i></b><br>
<spring
:
message
code=
"creation.elementcourses.libelle.quantite"
/>
<form
:
input
path=
"quantite"
/>
<b><i><form
:
errors
path=
"quantite"
cssclass=
"error"
/></i></b><br>
<input type=
"submit"
/>
</form
:
form
>
<table border=
"1"
>
<thead>
<tr>
<th><spring
:
message
code=
"colonne.identifiant"
/></th>
<th><spring
:
message
code=
"colonne.libelle"
/></th>
<th><spring
:
message
code=
"colonne.quantite"
/></th>
</tr>
</thead>
<tbody>
<c
:
forEach
items=
"${listeCourses}"
var=
"course"
>
<tr>
<td><c
:
out
value=
"${course.id}"
/></td>
<td><c
:
out
value=
"${course.libelle}"
/></td>
<td><c
:
out
value=
"${course.quantite}"
/></td>
</tr>
</c
:
forEach
>
</tbody>
</table>
</body>
</html>
Après déploiement, on obtient le résultat ci-dessous à l'adresse : http://localhost:8080/tutoriel-web-spring/afficherCreationListeCourses.
Voici le résultat de la validation lorsque les champs ne sont pas renseignés.
Et le résultat de la validation lorsque le champ « quantité » n'est pas numérique.
Lorsque le formulaire respecte les contraintes de la validation, l'occurrence est créée en base puis est visible au rafraîchissement de la page.
V. Suppression de données en base▲
Dans ce paragraphe, nous allons supprimer des données dans la base de données. Pour cela, nous allons créer des liens avec paramètre qui permettront de déterminer l'occurrence à supprimer.
Il faut ajouter de nouveaux libellés dans le fichier « messages_fr.properties ».
titre.suppression.elementcourses
=Suppression d'élément de la liste de courses
suppression.supprimer.libelle
=Supprimer
Dans l'interface « IListeCoursesDAO » de la DAO, nous ajoutons la méthode de suppression recevant une entité en paramètre.
void
supprimerCourse
(
final
Course pCourse);
Tandis que dans l'interface « IServiceListeCourses » du service, nous ajoutons la méthode recevant en paramètre l'identifiant de l'entité.
void
supprimerCourse
(
final
Integer pIdCourse);
L'implémentation de la méthode de la DAO « ListeCoursesDAO » supprime l'entité en base.
public
void
supprimerCourse
(
final
Course pCourse) {
final
Course lCourse =
entityManager.getReference
(
Course.class
, pCourse.getId
(
));
entityManager.remove
(
lCourse);
}
L'implémentation de la méthode du service « ServiceListeCourses » instancie une entité avec l'identifiant et l'envoie en paramètre à la DAO.
@Transactional
public
void
supprimerCourse
(
final
Integer pIdCourse) {
final
Course lCourse =
new
Course
(
);
lCourse.setId
(
pIdCourse);
dao.supprimerCourse
(
lCourse);
}
Le contrôleur « SupprimerListeCoursesController » comporte deux méthodes « afficher » et « supprimer ». La méthode « afficher » place la liste des courses dans l'attribut « listeCourses ». Ensuite, la méthode « supprimer » utilise le paramètre « idCourse » de la requête pour appeler la méthode « supprimerCourse » du service, puis elle relance l'affichage de la page.
package
com.developpez.rpouiller.controller;
import
java.util.List;
import
org.springframework.beans.factory.annotation.Autowired;
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.bind.annotation.RequestParam;
import
com.developpez.rpouiller.bean.Course;
import
com.developpez.rpouiller.services.IServiceListeCourses;
@Controller
public
class
SupprimerListeCoursesController {
@Autowired
private
IServiceListeCourses service;
@RequestMapping
(
value=
"/afficherSuppressionListeCourses"
, method =
RequestMethod.GET)
public
String afficher
(
final
ModelMap pModel) {
final
List<
Course>
lListeCourses =
service.rechercherCourses
(
);
pModel.addAttribute
(
"listeCourses"
, lListeCourses);
return
"suppression"
;
}
@RequestMapping
(
value=
"/supprimerSuppressionListeCourses"
, method =
RequestMethod.GET)
public
String supprimer
(
@RequestParam
(
value=
"idCourse"
) final
Integer pIdCourse, final
ModelMap pModel) {
service.supprimerCourse
(
pIdCourse);;
return
afficher
(
pModel);
}
}
La JSP affiche un tableau des différents éléments de la liste de courses. Ce tableau comporte une colonne avec un lien pour supprimer.
<%@
page
language
=
"java"
contentType
=
"text/html; charset=ISO-8859-1"
isELIgnored=
"false"
pageEncoding=
"ISO-8859-1"
%>
<%@taglib
uri
=
"http://java.sun.com/jsp/jstl/core"
prefix
=
"c"
%>
<%@taglib
uri
=
"http://www.springframework.org/tags"
prefix
=
"spring"
%>
<!
DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<html>
<head>
<title><spring
:
message
code=
"titre.suppression.elementcourses"
/></title>
</head>
<body>
<table border=
"1"
>
<thead>
<tr>
<th><spring
:
message
code=
"colonne.identifiant"
/></th>
<th><spring
:
message
code=
"colonne.libelle"
/></th>
<th><spring
:
message
code=
"colonne.quantite"
/></th>
<th>
</th>
</tr>
</thead>
<tbody>
<c
:
forEach
items=
"${listeCourses}"
var=
"course"
>
<tr>
<td><c
:
out
value=
"${course.id}"
/></td>
<td><c
:
out
value=
"${course.libelle}"
/></td>
<td><c
:
out
value=
"${course.quantite}"
/></td>
<td>
<c
:
url
value=
"/supprimerSuppressionListeCourses"
var=
"url"
>
<c
:
param
name=
"idCourse"
value=
"${course.id}"
/>
</c
:
url
>
<a href=
"${url}"
>
<spring
:
message
code=
"suppression.supprimer.libelle"
/>
</a>
</td>
</tr>
</c
:
forEach
>
</tbody>
</table>
</body>
</html>
Après déploiement, on obtient le résultat ci-dessous à l'adresse : http://localhost:8080/tutoriel-web-spring/afficherSuppressionListeCourses.
Voici le résultat après avoir cliqué sur le dernier lien de suppression.
VI. Modification de données en base▲
Dans ce paragraphe, nous allons modifier des données dans la base de données. Pour cela, nous allons créer un formulaire avec une liste de valeurs qui seront mises à jour dans la base de données.
Il faut ajouter de nouveaux libellés dans le fichier « messages_fr.properties ».
titre.modification.elementcourses
=Modification d'élément de la liste de courses
Il faut créer un nouveau fichier d'internationalisation « ValidationMessages_fr.properties » (également dans le dossier « src/main/resources »).
modification.course.quantite.notempty
=La quantité est nécessaire.
modification.course.quantite.numerique
=La quantité doit être numérique et positive.
Dans l'interface « IListeCoursesDAO » de la DAO, nous ajoutons la méthode de modification recevant une entité en paramètre.
void
modifierCourse
(
final
Course pCourse);
Tandis que dans l'interface « IServiceListeCourses » du service, nous ajoutons la méthode recevant en paramètres une liste d'entités.
void
modifierCourses
(
final
List<
Course>
pListeCourses);
L'implémentation de la méthode de la DAO « ListeCoursesDAO » supprime l'entité en base. Elle lève une exception uniquement si la requête modifie un nombre d'occurrences différent de 1 (ce qui aura pour conséquence de provoquer un rollback de transaction au niveau du service). Le libellé de l'exception contient le texte de la requête SQL.
Pour avoir plus d'information sur la manière de récupérer la requête SQL, vous pouvez lire l'article :
public
void
modifierCourse
(
final
Course pCourse) {
final
CriteriaBuilder lCriteriaBuilder =
entityManager.getCriteriaBuilder
(
);
final
CriteriaUpdate<
Course>
lCriteriaUpdate =
lCriteriaBuilder.createCriteriaUpdate
(
Course.class
);
final
Root<
Course>
lRoot =
lCriteriaUpdate.from
(
Course.class
);
final
Path<
Course>
lPath =
lRoot.get
(
"id"
);
final
Expression<
Boolean>
lExpression =
lCriteriaBuilder.equal
(
lPath, pCourse.getId
(
));
lCriteriaUpdate.where
(
lExpression);
lCriteriaUpdate.set
(
"quantite"
, pCourse.getQuantite
(
));
final
Query lQuery =
entityManager.createQuery
(
lCriteriaUpdate);
final
int
lRowCount =
lQuery.executeUpdate
(
);
if
(
lRowCount !=
1
) {
final
org.hibernate.Query lHQuery =
lQuery.unwrap
(
org.hibernate.Query.class
);
final
String lSql =
lHQuery.getQueryString
(
);
throw
new
RuntimeException
(
"Nombre d'occurrences ("
+
lRowCount +
") modifiés différent de 1 pour "
+
lSql);
}
}
L'implémentation de la méthode du service « ServiceListeCourses » parcourt les entités de la liste pour les passer en paramètre l'une après l'autre à la DAO.
@Transactional
public
void
modifierCourses
(
final
List<
Course>
pListeCourses) {
for
(
final
Course lCourse : pListeCourses) {
dao.modifierCourse
(
lCourse);
}
}
Le formulaire « ModificationForm » contient une liste qui comporte l'annotation « @Valid ». Cela provoquera la validation de chaque élément de la liste lorsque le formulaire sera validé en entrée de la méthode « modifier » du contrôleur.
package
com.developpez.rpouiller.controller;
import
java.util.List;
import
javax.validation.Valid;
public
class
ModificationForm {
@Valid
private
List<
ModificationCourse>
listeCourses;
public
void
setListeCourses
(
final
List<
ModificationCourse>
pListeCourses) {
listeCourses =
pListeCourses;
}
public
List<
ModificationCourse>
getListeCourses
(
) {
return
listeCourses;
}
}
La classe « ModificationCourse » correspond à un élément de la liste contenue dans le formulaire « ModificationForm ». Nous pouvons remarquer que les annotations « NotEmpty » et « Pattern » comportent des valeurs « message » qui correspondent aux messages internationalisés contenus dans le fichier « ValidationMessages_fr.properties ».
package
com.developpez.rpouiller.controller;
import
javax.validation.constraints.Pattern;
import
org.hibernate.validator.constraints.NotEmpty;
public
class
ModificationCourse {
private
Integer id;
private
String libelle;
@NotEmpty
(
message=
"{modification.course.quantite.notempty}"
)
@Pattern
(
regexp=
"
\\
d*"
, message=
"{modification.course.quantite.numerique}"
)
private
String quantite;
public
Integer getId
(
) {
return
id;
}
public
void
setId
(
final
Integer pId) {
id =
pId;
}
public
String getLibelle
(
) {
return
libelle;
}
public
void
setLibelle
(
final
String pLibelle) {
libelle =
pLibelle;
}
public
String getQuantite
(
) {
return
quantite;
}
public
void
setQuantite
(
final
String pQuantite) {
quantite =
pQuantite;
}
}
Le contrôleur « ModifierListeCoursesController » comporte les méthodes « afficher » et « modifier ». La méthode « afficher » place la liste des courses dans le formulaire et la méthode « modifier » récupère la liste des courses du formulaire pour appeler la méthode de modification du service.
package
com.developpez.rpouiller.controller;
import
java.util.LinkedList;
import
java.util.List;
import
javax.validation.Valid;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.stereotype.Controller;
import
org.springframework.ui.ModelMap;
import
org.springframework.validation.BindingResult;
import
org.springframework.web.bind.annotation.ModelAttribute;
import
org.springframework.web.bind.annotation.RequestMapping;
import
org.springframework.web.bind.annotation.RequestMethod;
import
com.developpez.rpouiller.bean.Course;
import
com.developpez.rpouiller.services.IServiceListeCourses;
@Controller
public
class
ModifierListeCoursesController {
@Autowired
private
IServiceListeCourses service;
@RequestMapping
(
value=
"/afficherModificationListeCourses"
, method =
RequestMethod.GET)
public
String afficher
(
final
ModelMap pModel) {
if
(
pModel.get
(
"modification"
) ==
null
) {
final
List<
Course>
lListeCourses =
service.rechercherCourses
(
);
final
ModificationForm lModificationForm =
new
ModificationForm
(
);
final
List<
ModificationCourse>
lListe =
new
LinkedList<
ModificationCourse>(
);
for
(
final
Course lCourse : lListeCourses) {
final
ModificationCourse lModificationCourse =
new
ModificationCourse
(
);
lModificationCourse.setId
(
lCourse.getId
(
));
lModificationCourse.setLibelle
(
lCourse.getLibelle
(
));
lModificationCourse.setQuantite
(
lCourse.getQuantite
(
).toString
(
));
lListe.add
(
lModificationCourse);
}
lModificationForm.setListeCourses
(
lListe);
pModel.addAttribute
(
"modification"
, lModificationForm);
}
return
"modification"
;
}
@RequestMapping
(
value=
"/modifierModificationListeCourses"
, method =
RequestMethod.POST)
public
String modifier
(
@Valid
@ModelAttribute
(
value=
"modification"
) final
ModificationForm pModification,
final
BindingResult pBindingResult, final
ModelMap pModel) {
if
(!
pBindingResult.hasErrors
(
)) {
final
List<
Course>
lListeCourses =
new
LinkedList<
Course>(
);
for
(
final
ModificationCourse lModificationCourse : pModification.getListeCourses
(
)) {
final
Course lCourse =
new
Course
(
);
lCourse.setId
(
lModificationCourse.getId
(
));
final
Integer lQuantite =
Integer.valueOf
(
lModificationCourse.getQuantite
(
));
lCourse.setQuantite
(
lQuantite);
lListeCourses.add
(
lCourse);
}
service.modifierCourses
(
lListeCourses);
}
return
afficher
(
pModel);
}
}
La JSP affiche un tableau des différents éléments de la liste de courses. La colonne des quantités comporte des champs de saisie permettant de modifier les différentes quantités. Nous remarquons l'utilisation de la variable « status » qui permet de nommer les champs du formulaire et de filtrer les messages d'erreur selon l'occurrence de la liste des courses.
<%@
page
language
=
"java"
contentType
=
"text/html; charset=ISO-8859-1"
isELIgnored=
"false"
pageEncoding=
"ISO-8859-1"
%>
<%@taglib
uri
=
"http://java.sun.com/jsp/jstl/core"
prefix
=
"c"
%>
<%@taglib
uri
=
"http://www.springframework.org/tags/form"
prefix
=
"form"
%>
<%@taglib
uri
=
"http://www.springframework.org/tags"
prefix
=
"spring"
%>
<!
DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<html>
<head>
<title><spring
:
message
code=
"titre.creation.elementcourses"
/></title>
</head>
<body>
<form
:
form
method=
"post"
modelAttribute=
"modification"
action=
"modifierModificationListeCourses"
>
<table border=
"1"
>
<thead>
<tr>
<th><spring
:
message
code=
"colonne.identifiant"
/></th>
<th><spring
:
message
code=
"colonne.libelle"
/></th>
<th><spring
:
message
code=
"colonne.quantite"
/></th>
</tr>
</thead>
<tbody>
<c
:
forEach
items=
"${modification.listeCourses}"
var=
"course"
varStatus=
"status"
>
<tr>
<td>
<c
:
out
value=
"${course.id}"
/>
<input type=
"hidden"
name=
"listeCourses[${status.index}].id"
value=
"${course.id}"
/>
</td>
<td>
<c
:
out
value=
"${course.libelle}"
/>
<input type=
"hidden"
name=
"listeCourses[${status.index}].libelle"
value=
"${course.libelle}"
/>
</td>
<td>
<input type=
"text"
name=
"listeCourses[${status.index}].quantite"
value=
"${course.quantite}"
/><br/>
<b><i><form
:
errors
path=
"listeCourses[${status.index}].quantite"
/></i></b>
</td>
</tr>
</c
:
forEach
>
</tbody>
</table>
<input type=
"submit"
/>
</form
:
form
>
</body>
</html>
Après déploiement, on obtient le résultat ci-dessous à l'adresse : http://localhost:8080/tutoriel-web-spring/afficherModificationListeCourses.
Voici le résultat d'une validation en erreur.
Et celui d'une modification réussie.
VII. Unification de l'application par un menu▲
Dans ce paragraphe, nous allons ajouter aux pages Web précédemment créées un menu qui permettra de naviguer dans l'application. Pour cela, nous allons utiliser les Tiles.
Il faut donc rajouter les dépendances Maven suivantes. Les dépendances « tiles-jsp » et « slf4j-log4j12 » permettent l'utilisation de Tiles.
<dependency>
<groupId>
org.apache.tiles</groupId>
<artifactId>
tiles-jsp</artifactId>
<version>
3.0.3</version>
</dependency>
<dependency>
<groupId>
org.slf4j</groupId>
<artifactId>
slf4j-log4j12</artifactId>
<version>
1.5.11</version>
</dependency>
Il faut ajouter un nouveau libellé dans le fichier « messages_fr.properties ».
titre.application
=Application de liste de courses
Il faut modifier le fichier « dispatcher-servlet.xml » comme ci-dessous. Le bean « InternalResourceViewResolver » (déjà présent dans le fichier) doit se trouver après les nouvelles déclarations (simplement parce que Spring teste les « ViewResolver » dans l'ordre de déclaration, ce qui permet de résoudre une ressource avec « UrlBasedViewResolver » puis d'essayer avec « InternalResourceViewResolver » en deuxième). Le bean « UrlBasedViewResolver » (depuis Spring 1.0) utilise « TilesView » pour traiter les vues. Le bean « TilesConfigurer » charge la configuration dans le fichier « /WEB-INF/tiles.xml ».
<bean
class
=
"org.springframework.web.servlet.view.UrlBasedViewResolver"
>
<property
name
=
"viewClass"
value
=
"org.springframework.web.servlet.view.tiles3.TilesView"
/>
</bean>
<bean
id
=
"tilesConfigurer"
class
=
"org.springframework.web.servlet.view.tiles3.TilesConfigurer"
>
<property
name
=
"definitions"
>
<list>
<value>
/WEB-INF/tiles.xml</value>
</list>
</property>
</bean>
<bean
class
=
"org.springframework.web.servlet.view.InternalResourceViewResolver"
>
<property
name
=
"prefix"
>
<value>
/vues/</value>
</property>
<property
name
=
"suffix"
>
<value>
.jsp</value>
</property>
</bean>
Le fichier « tiles.xml » contient la définition des différents tiles. Nous reprenons les noms des ressources utilisées dans les contrôleurs, ce qui nous évite de modifier les contrôleurs.
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
"http://tiles.apache.org/dtds/tiles-config_3_0.dtd"
>
<tiles-definitions>
<definition
name
=
"listeCourses"
template
=
"/vues/page.jsp"
>
<put-attribute
name
=
"principal"
value
=
"/vues/listeCourses.jsp"
/>
</definition>
<definition
name
=
"creation"
template
=
"/vues/page.jsp"
>
<put-attribute
name
=
"principal"
value
=
"/vues/creation.jsp"
/>
</definition>
<definition
name
=
"suppression"
template
=
"/vues/page.jsp"
>
<put-attribute
name
=
"principal"
value
=
"/vues/suppression.jsp"
/>
</definition>
<definition
name
=
"modification"
template
=
"/vues/page.jsp"
>
<put-attribute
name
=
"principal"
value
=
"/vues/modification.jsp"
/>
</definition>
</tiles-definitions>
La JSP « page.jsp » contient le squelette des pages (le « template » dans le fichier « tiles.xml »). Le tag « tiles:insertAttribute » permet d'inclure les JSP contenant le corps de la page et définis dans le fichier « tiles.xml »
<%@
page
language
=
"java"
contentType
=
"text/html; charset=ISO-8859-1"
isELIgnored=
"false"
pageEncoding=
"ISO-8859-1"
%>
<%@taglib
uri
=
"http://java.sun.com/jsp/jstl/core"
prefix
=
"c"
%>
<%@taglib
uri
=
"http://www.springframework.org/tags"
prefix
=
"spring"
%>
<%@taglib
uri
=
"http://tiles.apache.org/tags-tiles"
prefix
=
"tiles"
%>
<!
DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<html>
<head>
<meta http-equiv=
"Content-Type"
content=
"text/html; charset=ISO-8859-1"
>
<title><spring
:
message
code=
"titre.application"
/></title>
</head>
<body>
<table>
<tbody>
<tr>
<td valign=
"top"
>
<table>
<tbody>
<tr><td>
<c
:
url
value=
"/afficherListeCourses"
var=
"url"
/>
<a href=
"${url}"
>
<spring
:
message
code=
"titre.listecourses"
/>
</a>
</td></tr>
<tr><td>
<c
:
url
value=
"/afficherCreationListeCourses"
var=
"url"
/>
<a href=
"${url}"
>
<spring
:
message
code=
"titre.creation.elementcourses"
/>
</a>
</td></tr>
<tr><td>
<c
:
url
value=
"/afficherSuppressionListeCourses"
var=
"url"
/>
<a href=
"${url}"
>
<spring
:
message
code=
"titre.suppression.elementcourses"
/>
</a>
</td></tr>
<tr><td>
<c
:
url
value=
"/afficherModificationListeCourses"
var=
"url"
/>
<a href=
"${url}"
>
<spring
:
message
code=
"titre.modification.elementcourses"
/>
</a>
</td></tr>
</tbody>
</table>
</td>
<td valign=
"top"
>
<tiles
:
insertAttribute
name=
"principal"
/>
</td>
</tr>
</tbody>
</table>
</body>
</html>
Il faut modifier les JSP « listeCourses.jsp », « creation.jsp », « suppression.jsp » et « modification.jsp » pour ne garder que les parties dans la balise « body ».
<%@
page
language
=
"java"
contentType
=
"text/html; charset=ISO-8859-1"
isELIgnored=
"false"
pageEncoding=
"ISO-8859-1"
%>
<%@taglib
uri
=
"http://java.sun.com/jsp/jstl/core"
prefix
=
"c"
%>
<%@taglib
uri
=
"http://www.springframework.org/tags"
prefix
=
"spring"
%>
<!
DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<table border=
"1"
>
<thead>
<tr>
<th><spring
:
message
code=
"colonne.identifiant"
/></th>
<th><spring
:
message
code=
"colonne.libelle"
/></th>
<th><spring
:
message
code=
"colonne.quantite"
/></th>
</tr>
</thead>
<tbody>
<c
:
forEach
items=
"${listeCourses}"
var=
"course"
>
<tr>
<td><c
:
out
value=
"${course.id}"
/></td>
<td><c
:
out
value=
"${course.libelle}"
/></td>
<td><c
:
out
value=
"${course.quantite}"
/></td>
</tr>
</c
:
forEach
>
</tbody>
</table>
<%@
page
language
=
"java"
contentType
=
"text/html; charset=ISO-8859-1"
isELIgnored=
"false"
pageEncoding=
"ISO-8859-1"
%>
<%@taglib
uri
=
"http://java.sun.com/jsp/jstl/core"
prefix
=
"c"
%>
<%@taglib
uri
=
"http://www.springframework.org/tags/form"
prefix
=
"form"
%>
<%@taglib
uri
=
"http://www.springframework.org/tags"
prefix
=
"spring"
%>
<!
DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<form
:
form
method=
"post"
modelAttribute=
"creation"
action=
"creerCreationListeCourses"
>
<spring
:
message
code=
"creation.elementcourses.libelle.libelle"
/>
<form
:
input
path=
"libelle"
/>
<b><i><form
:
errors
path=
"libelle"
cssclass=
"error"
/></i></b><br>
<spring
:
message
code=
"creation.elementcourses.libelle.quantite"
/>
<form
:
input
path=
"quantite"
/>
<b><i><form
:
errors
path=
"quantite"
cssclass=
"error"
/></i></b><br>
<input type=
"submit"
/>
</form
:
form
>
<table border=
"1"
>
<thead>
<tr>
<th><spring
:
message
code=
"colonne.identifiant"
/></th>
<th><spring
:
message
code=
"colonne.libelle"
/></th>
<th><spring
:
message
code=
"colonne.quantite"
/></th>
</tr>
</thead>
<tbody>
<c
:
forEach
items=
"${listeCourses}"
var=
"course"
>
<tr>
<td><c
:
out
value=
"${course.id}"
/></td>
<td><c
:
out
value=
"${course.libelle}"
/></td>
<td><c
:
out
value=
"${course.quantite}"
/></td>
</tr>
</c
:
forEach
>
</tbody>
</table>
<%@
page
language
=
"java"
contentType
=
"text/html; charset=ISO-8859-1"
isELIgnored=
"false"
pageEncoding=
"ISO-8859-1"
%>
<%@taglib
uri
=
"http://java.sun.com/jsp/jstl/core"
prefix
=
"c"
%>
<%@taglib
uri
=
"http://www.springframework.org/tags"
prefix
=
"spring"
%>
<!
DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<table border=
"1"
>
<thead>
<tr>
<th><spring
:
message
code=
"colonne.identifiant"
/></th>
<th><spring
:
message
code=
"colonne.libelle"
/></th>
<th><spring
:
message
code=
"colonne.quantite"
/></th>
<th>
</th>
</tr>
</thead>
<tbody>
<c
:
forEach
items=
"${listeCourses}"
var=
"course"
>
<tr>
<td><c
:
out
value=
"${course.id}"
/></td>
<td><c
:
out
value=
"${course.libelle}"
/></td>
<td><c
:
out
value=
"${course.quantite}"
/></td>
<td>
<c
:
url
value=
"/supprimerSuppressionListeCourses"
var=
"url"
>
<c
:
param
name=
"idCourse"
value=
"${course.id}"
/>
</c
:
url
>
<a href=
"${url}"
>
<spring
:
message
code=
"suppression.supprimer.libelle"
/>
</a>
</td>
</tr>
</c
:
forEach
>
</tbody>
</table>
<%@
page
language
=
"java"
contentType
=
"text/html; charset=ISO-8859-1"
isELIgnored=
"false"
pageEncoding=
"ISO-8859-1"
%>
<%@taglib
uri
=
"http://java.sun.com/jsp/jstl/core"
prefix
=
"c"
%>
<%@taglib
uri
=
"http://www.springframework.org/tags/form"
prefix
=
"form"
%>
<%@taglib
uri
=
"http://www.springframework.org/tags"
prefix
=
"spring"
%>
<!
DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<form
:
form
method=
"post"
modelAttribute=
"modification"
action=
"modifierModificationListeCourses"
>
<table border=
"1"
>
<thead>
<tr>
<th><spring
:
message
code=
"colonne.identifiant"
/></th>
<th><spring
:
message
code=
"colonne.libelle"
/></th>
<th><spring
:
message
code=
"colonne.quantite"
/></th>
</tr>
</thead>
<tbody>
<c
:
forEach
items=
"${modification.listeCourses}"
var=
"course"
varStatus=
"status"
>
<tr>
<td>
<c
:
out
value=
"${course.id}"
/>
<input type=
"hidden"
name=
"listeCourses[${status.index}].id"
value=
"${course.id}"
/>
</td>
<td>
<c
:
out
value=
"${course.libelle}"
/>
<input type=
"hidden"
name=
"listeCourses[${status.index}].libelle"
value=
"${course.libelle}"
/>
</td>
<td>
<input type=
"text"
name=
"listeCourses[${status.index}].quantite"
value=
"${course.quantite}"
/><br/>
<b><i><form
:
errors
path=
"listeCourses[${status.index}].quantite"
/></i></b>
</td>
</tr>
</c
:
forEach
>
</tbody>
</table>
<input type=
"submit"
/>
</form
:
form
>
Après déploiement, on obtient le résultat ci-dessous à l'adresse : http://localhost:8080/tutoriel-web-spring/afficherListeCourses.
Les liens dans le menu à gauche permettent de naviguer entre les pages.
VIII. Conclusion▲
Attention, cet article n'a pour but que de présenter différentes techniques. Il n'est absolument pas rigoureux dans son code : par exemple, il n'y a aucune gestion des exceptions qui peuvent être levées dans les différentes couches.
Mais, cet article aura permis de voir les techniques principales liées aux applications Web avec Spring (contrôleurs, formulaires, internationalisation).
Nous avons également utilisé la gestion de la transaction avec l'annotation « @Transactional », l'injection de dépendances et le support des ressources JDBC et d'Hibernate.
Pour aller plus loin, je vous invite par exemple à vous renseigner sur les différentes « View » possibles. Parmi ces « View », on trouve « JasperReportsPdfView » qui permet de générer des rapports au format PDF.
Les sources de cet article sont présentes sur GitHub : https://github.com/regis1512/tutoriel-web-spring.
IX. Remerciements▲
Je remercie très sincèrement :
- www.developpez.com qui me permet de publier cet article ;
- Nono40 et djibril pour leurs outils ;
- Yann Caron pour sa relecture technique ;
- Philippe DUVAL pour sa relecture orthographique rigoureuse.