Java EE

Transcription

Java EE
Java EE
Les applications Web JAVA
Les objectifs
●
Avoir une vision globale du développement
d'application Java EE
●
Mettre en œuvre des exemples simples
●
Comprendre et éviter les pièges du débutant
●
Pouvoir aller de l'avant par soi-même pour
approfondir chaque brique technique
Les applications multi-tiers
●
●
●
Le client (Web ou
Lourd) appelle un
service hébergé sur
un serveur
●
Le serveur Web gère
les parties IHM et
présentation
Le ou les serveurs de
traitement exécute(nt)
la logique métier ainsi
que la synchronisation
des données
La base de données
s'occupe de stocker
les données
Le jeux de piste
●
●
●
●
●
●
HTML/XML/CSS
JavaScript
Applet Java
●
●
●
Servlet
JSP
JSF
Rest
●
●
●
●
●
Navigateur
JVM
Tomcat
GlassFish …
EJB
JPA (+ORM) :
●
Hibernate
●
EclipseLink
JTA
JMS
JDBC
JNDI
JAAS
Tomcat
GlassFish …
●
●
SQL
« Proc-Stock »
Oracle
MySQL
JavaDB
Applet
●
C'est quoi ?
–
Du code Java SE traditionnel qui s'exécute dans un contexte
particulier d'accès limité aux ressources
–
La classe « main » de démarrage doit étendre l'une des classes
suivantes :
java.applet.Applet
● javax.swing.JApplet
Le cycle de vie du code correspond à l'exécution des méthodes
●
–
init() : appelée 1 fois après son chargement
● start() : appelée lorsque l'applet est visible (votre main)
● stop() : appelée lorsque l'applet n'est plus visible
● destroy() : appelée 1 fois après sa destruction
Le reste du code est le même que pour une application Java
traditionnelle « awt » ou « swing »
●
–
Applet
●
Où cela s'exécute ?
–
Dans une JVM démarrée par le navigateur client suite à
la lecture d'une balise HTML« <applet> »
–
Mais attention le code n'accède pas à toutes les
ressources locales (fichiers et réseaux) ●
●
●
Par défaut le code s'exécute dans une SandBox
Signature du code : possibilité d'accéder aux ressources locale
Possibilité de gérer l'accès aux ressources avec des fichiers
« policy »
<applet name="FirstAnimation"
codebase="class/"
code="FirstAnimation.class"
height="300px" width="300px"
archive="plugin.jar">
<param name="message" value="Message pour les ZérOs">
</applet>
grant Principal Administrateur "root" {
permission java.util.PropertyPermission "java.home", "read";
permission java.util.PropertyPermission "user.home", "read";
permission java.io.FilePermission "c:\\foo.txt", "write,read";
};
grant Principal Etudiant {
permission java.io.FilePermission "c:\\foo.txt", "read";
};
Servlet
●
C'est quoi ?
–
Du code Java qui répond aux requêtes HTTP GET, POST,
PUT, DELETE, HEAD, TRACE
–
La classe « main » de démarrage doit étendre la classe
javax.servlet.http.HttpServlet
nb : il peux y avoir plusieurs classes « main »
–
Le cycle de vie du code dépend des méthodes HTTP
écoutées par la Servlet en surchargeant une ou plusieurs
méthodes de la classe HttpServlet :
●
doGet : point d'entrée d'une requête GET
●
doPost : point d'entrée d'une requête POST
●
doPut : : point d'entrée d'une requête PUT
●
●
●
doDelete : point d'entrée d'une requête DELETE
doHead : point d'entrée d'une requête HEAD
doTrace : point d'entrée d'une requête TRACE
Servlet
package com.sdzee.servlets;
import java.io.IOException;
import
import
import
import
javax.servlet.ServletException;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
public class Test extends HttpServlet {
public void doGet( HttpServletRequest request, HttpServletResponse response )
throws ServletException, IOException{
response.setContentType("text/html");
response.setCharacterEncoding( "UTF-8" );
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<meta charset=\"utf-8\" />");
out.println("<title>Test</title>");
out.println("</head>");
out.println("<body>");
out.println("<p>Ceci est une page générée depuis une servlet.</p>");
out.println("</body>");
out.println("</html>");
}
}
Servlet
●
Où cela s'exécute ?
–
Sur un Conteneur Java EE WEB qui est lui même démarré
dans une JVM
Servlet
●
Quel est le rôle du conteneur Web ?
–
Mettre à disposition des outils qui gèrent les flux
HTTP (écoute ports système, sécurité, gestion de
cache, compression, ...)
–
Héberger, déployer, supprimer des applications
Web Java
–
Gérer le cycle de vie des Servlets
–
Diriger les requêtes HTTP vers la bonne
application
Servlet – Arbo livré
●
Comment s'effectue la gestion de
l'hébergement applicatif ?
–
Toute application Web Java doit être une archive
« war » dont l'arborescence est normalisée !
Servlet – Arbo source
●
Attention : Arborescence de livraison (ou de
packaging) n'est pas arborescence des
sources !
Servlet
●
A quoi sert le « web.xml » ?
–
Ce fichier est obligatoire, il s'appelle le descripteur
de déploiement
–
Comme son nom l'indique, il fait comprendre au
conteneur Web
●
●
●
Quel est le nom de l'application
Où sont situées les Servlets
Comment configurer leur cycle de vie :
–
–
–
–
Paramètres d'exécution non passés par l'utilisateur
Association entre URL et Servlet
Filtres écouteurs à exécuter avant la Servlet
...
Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
Version="3.0">
<!-- Description application -->
<display-name>Application de test</display-name>
<description>Ma première application</description>
<!-- déclarations des servlets -->
<servlet>
<servlet-name>MaServlet</servlet-name>
<servlet-class>mon.package.servlets.MaServletTest</servlet-class>
<init-param>
<param-name>UnParametre</param-name>
<param-value>La Valeur Associé</param-value>
</init-param>
</servlet>
<!-- correspondance servlets / URL -->
<servlet-mapping>
<servlet-name>MaServlet</servlet-name>
<url-pattern>/MonUrlTest/*</url-pattern>
</servlet-mapping>
</web-app>
Servlet
●
Le fichier web.xml est obligatoire mais les
informations de description des Servlets
peuvent maintenant être remplacées par des
annotations directement dans les Servlets
package mon.package.servlets;
...
@WebServlet(name="MaServlet"urlPatterns="/MonUrlTest/*", initParams={@WebInitParam(name="UnParametre",value="La
Valeur associée")})
public class MaServlet extends HttpServlet {
...
}
Servlet
●
Quel est le cycle de vie ?
init()
destroy()
doGet()
Client 3
Il n'y a qu'une seule instance qui répond en parallèle à toutes les requêtes !
DoGet()
Client 1
doPost()
Client 2
Chargement de la servlet :
Démarrage du conteneur Web
Ou premier appel de la servlet
doGet()
Client n
Extinction du conteneur Web
Servlet
●
Javax.servlet.HttpServletRequest
–
getAttribute() : les attributs sont positionnés par le
conteneur ou de manière programmatique
–
getParameter() : les paramètres sont positionnés par la
requête (query string ou post paramètre)
–
getSession() : recupère un objet qui stocke les données
de session (permet également de créer
automatiquement une session)
–
getServletContext() : récupère un objet qui gère le
contexte d'exécution de la servlet et permet de
récupérer par exemple des paramètres d'initialisation
–
getRequestDispatcher() : récupère un objet permettant
de faire suivre le traitement vers une autre Servlet
Servlet
●
Javax.servlet.HttpServlerResponse
–
getOutputStream() / getWriter() : récupère un flux
pour écrire la page réponse à la requête
–
setContentType() : permet de positionner le type
MIME de la réponse
–
setHeader() : permet de positionner un header
particulier
–
setStatus() : permet de positionner un code HTTP
particulier pour la réponse
–
addCookie() : permet d'ajouter un cookie
–
sendRedirect() : permet de générer une réponse de
redirection
Servlet
●
Javax.servlet.Filter :
–
L'implémentation de cette interface permet de modifier la requête et / ou la
réponse à la volée.
–
Les filtres sont gérés par le conteneur de la même manière que la Servlet et
possède le même cycle de vie avec déclaration XML ou annotation.
●
●
●
init()
destroy()
doFilter() @WebFilter(filterName="LogFilter", urlPatterns={"/*"},
initParams={@WebInitParam(name="test-param",value="MaValeur")})public class
LogFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
String ipAddress = request.getRemoteAddr();
<filter>
<filter-name>LogFilter</filter-name>
<filter-class>
net.viralpatel.servlet.filters.LogFilter
</filter-class>
<init-param>
<param-name>test-param</param-name>
<param-value>MaValeur</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>LogFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
System.out.println("IP "+ipAddress + ", Time "
+ new Date().toString());
chain.doFilter(req, res);
}
public void init(FilterConfig config) throws ServletException {
String testParam = config.getInitParameter("test-param");
System.out.println("Test Param: " + testParam);
}
public void destroy() {
//add code to release any resource
}
}
JavaBean
●
C'est quoi ?
–
Une simple classe contenant des propriétés et des accesseurs, pas de code métier !
–
Cela permet de stocker des informations structurées et de les transférer aux différentes classes
traitant une requête
–
Attention JavaBean n'est pas EJB !
–
On appelle également cela un POJO
public class Person {
private String name;
private String email;
private long phoneNo;
public Person() {
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setEmail(String email) {
this.email = email;
}
public String getEmail() {
return email;
}
public void setPhoneNo(long phoneNo) {
this.phoneNo = phoneNo;
}
public long getPhoneNo() {
return phoneNo;
}
}
public class BeanInServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
Person p = new Person();
p.setName("Sam Dalton");
p.setEmail("[email protected]");
p.setPhoneNo(1111111111);
req.setAttribute("person", p);
RequestDispatcher rd = req.getRequestDispatcher("/jsp/beandata.jsp");
rd.forward(req, res);
}
}
JSP
●
C'est quoi ?
–
Une servlet qui prend directement la forme d'une page HTML:
●
●
–
Dans une Servlet on écrit la page HTML dans un flux de sortie Java
Dans une JSP on écrit du code Java au sein d'une page HTML
Pas besoin de déclarer leur présence (que ce soit dans le web.xml ou par
annotation)
<%-- Ceci est un commentaire JSP --%>
<%@page contentType="text/html"%>
<%@page errorPage="erreur.jsp"%>
<%-- Importation d'un paquetage (package) --%>
<%@page import="java.util.*"%>
<html>
<head><title>Page JSP</title></head>
<body>
<%-- Déclaration d'une variable globale à la classe --%>
<%! int nombreVisites = 0; %>
<%-- Définition de code Java --%>
<% //Il est possible d'écrire du code Java ici
Date date = new Date();
// On peut incrémenter une variable globale pour compter le nombre
// d'affichage, par exemple.
nombreVisites++;
%>
<h1>Exemple de page JSP</h1>
<%-- Impression de variables --%>
<p>Au moment de l'exécution de ce script, nous sommes le <%= date %>.</p>
<p>Cette page a été affichée <%= nombreVisites %> fois !</p>
</body>
</html>
JSP
●
Quel est le cycle de vie ?
–
La JSP est traduite en Servlet puis elle est compilée avant d'être chargée en mémoire
et exécutée et gérée comme toutes les autres Servlets.
–
Si une mise à jour de la JSP est détectée alors elle est re-traduite puis re-compilée ...
JSP
●
Directives
<%@ directive %>
●
Déclarations
<%! déclaration %>
●
Expressions
<%= expression %>
●
Fragments de code/scriptlet
<% fragment de code %>
●
Commentaires
<%-- commentaire --%>
JSP
●
Utiliser/Créer un JavaBean avec une portée
« request », « session » ou « application »
<%-- Utiliser/Créer un JavaBean placé comme attribute de session --%>
<jsp:useBean id="produit" scope="session" class="ma.jsp.Produit" >
<p>Nouveau <%-- Ceci est un commentaire JSP --%>
produit !</p>
</jsp:useBean>
<%-- Afficher une propriété de ce JavaBean --%>
<p>Nom: <%= produit.getNom() %></p>
<%-- Afficher une propriété de ce JavaBean --%>
<p>Nom:
<jsp:getProperty
name="produit" property="nom"/> </p>
<%-- Positionner la valeur d'une propriété du JavaBean directement --%>
<jsp:setProperty
name="produit"
property="nom"
value="Voiture" />
<%-- Positionner la valeur d'une propriété du JavaBean par lecture d'un paramètre --%>
<jsp:setProperty
name="produit"
property="prix"
param="prix" />
JSP
●
<jsp:forward page="URL relative">
–
●
<jsp:include page="/myFooter.jsp">
–
●
Définit des propriétés propres à la page mais aussi à la réponse
<%@ taglib uri="http://jakarta.apache.org/taglibs/datetime" prefix="dt" %>
<p>La date en millisecondes est <dt:currentTime/></p>
<p>En francais<dt:format pattern="MM/dd/yyyy hh:mm"><dt:currentTime/></dt:format></p>
–
●
Permet d'inclure le résultat d'une Servlet ou d'une JSP dynamiquement à l'exécution de la JSP
<%@ page import="java.io.*;java.sql.*"
session="true"
isThreadSafe="true"
errorPage="bug/erreur.html"
isErrorPage="false"
contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"
language="java"
%>
–
●
Permet de faire suivre la requête vers une autre page JSP ou HTML ou Servlet
Permet d'utiliser des tags particuliers qui proviennent d'une librairie JSTL"
<%@param name="monParametre" value="uneValeur" />
–
Permet de passer à une page appelée par include ou forward, un paramètre complémentaire à
la requête
JSP
●
Les objets implicitement utilisables dans une page JSP
–
HttpServletRequest request
–
HttpServletResponse response
–
javax.servlet.http.HttpSession session
–
javax.servlet.ServletContext application
–
javax.servlet.jsp.PageContext pageContext
–
javax.servlet.ServletConfig config
–
javax.servlet.jsp.JspWriter out
JSTL
●
Des librairies utilisables dans les JSP afin de standardiser et de réutiliser
un ensemble de comportements d'usage courant
–
La taglib « core » permet de manipuler des variables de créer des comportements
conditionnels, des boucles
●
–
La taglib Xml permet de panipuler des données XML afin de les « parser », de les
« transformer » ou de les « afficher »
●
–
<fmt:message />, <fmt:setLocale />, <fmt:formatDate />, <fmt:formatNumber />, ...
La taglib SQL permet d'effectuer des requêtes SQL, de gérer des transactions
directement dans la JSP
●
–
<x:parse />, <x:transform />, ...
La taglib FMT permet gérer des sites multi-lingue afin de faciliter le formatage
chaînes et nombres en fonction de la locale ou encore l'accès aux bundles de
traduction
●
–
<c:if />, <c:when />, <c:foreach />, ...
<sql:query />, <sql:transaction />, <sql:upate />, ...
La taglib FUNCTION permet de manipuler facilement des chaînes textes
●
<fn:contains />, <sql:endsWidth />, <sql:indexOf />, <sql:toLowerCase />, ...
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<c:forEach var="etudiant" begin="0" items="etudiantList">< %= etudiant.nom %></c:forEach>
Framework MVC – Struts
●
●
Créer par C. Mc Clanahan puis devenu le projet Jakarta
d'Apache en 2000
Basé sur le modèle MVC2 qui contient un contrôleur
principal redirigeant les requêtes vers des contrôleurs
secondaires
Framework MVC – Struts
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.0//EN" "http://jakarta.apache.org/struts/dtds/strutsconfig_1_2.dtd ">
<struts-config>
<form-beans type="org.apache.struts.action.ActionFormBean">
<form-bean name="loginForm" type="com.jmd.test.struts.data.LoginForm" />
</form-beans>
<action-mappings type="org.apache.struts.action.ActionMapping">
<action path="/login" parameter="" input="/index.jsp" scope="request" name="loginForm" type="com.jmd.test.struts.controleur.LoginAction">
<forward name="succes" path="/accueil.jsp" redirect="false" />
<forward name="echec" path="/index.jsp" redirect="false" />
</action-mappings>
</struts-config>
Framework MVC – Struts 2
●
Ce n'est pas une nouvelle version de Strusts 1.x mais une
fusion de ce dernier avec Webwork
Néanmoins les principes restent proches
Framework MVC
http://static.raibledesigns.com/repository/
presentations/ComparingJVMWebFramew
orks-ApacheConUS2007.pdf
Framework MVC – JSF
●
●
●
●
Les créateurs : Sun, Oracle, Borland, BEA, IBM
Oracle qui est maintenant éditeur de Java EE
promeut l'utilisation de JSF comme framework de
référence
Version actuelle de ce framework : 2.1
Attention ce framework qui est décrit, spécifié et
dont les annotations existent dans Java EE est
uniquement utilisable si on met en œuvre son
implémentation (il en existe donc plusieurs :
MyFaces d'Apache, Mojarra d'Oracle/Sun)
JSF
●
Comment cela fonctionne
Navigateur
Conteneur Java EE
Lecture du web.xml, la requette est dirigée vers la Servlet du moteur JSF
javax.faces.webapp.FacesServlet
Une page appelle :
MaPage.jsf
HTTP
La vue est affiché
Moteur JSF
1) Restaure ou Crée une vue demandée par le client
2) Applique les valeurs de la requête
Attention : pas totalement, juste la représentation
textuelle dans des composants visuels
3) Valide les valeurs appliquées
4) Applique les valeurs aux Beans Java
5) Invoque la logique applicative (la méthode appelée
explicitement ou par écoute d’événement)
6)La vue est envoyé à l'utilisateur avec les paramètres
qui doivent être affichées et l'état de la vue est
sauvegardé
Le cycle de vie détaillé
JSF
●
Voici un découpage « à grosse maille » des
éléments constitutifs
–
En bleu ce qui est totalement JSF
–
En vert ce qui n'est pas directement JSF mais qui est
fortement recommandé d'utiliser en complément
VUE : XHTML
CONTROLEUR : Managed Bean
METIER : EJB / JTA
MODELE : JPA
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<meta http-equiv="Content-Type"
content="text/html; charset=ISO-8859-1" />
</h:head>
<h:body>
Hello <h:outputLabel value="#{helloWorldBean.nom}" />!
<h:messages />
<h:form>
<h:panelGrid columns="2">
<h:outputText value="Saisir votre nom"></h:outputText>
<h:inputText value="#{helloWorldBean.nom}"></h:inputText>
</h:panelGrid>
<h:commandButton
value="Afficher
nom" action="#{helloWorldBean.maFonction}" />
</h:form>
<h:link value="Recharger page" action="HelloWorld.jsf" />
</h:body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<faces-config
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-facesconfig_2_1.xsd"
version="2.1">
<managed-bean>
<managed-bean-name>helloWorldBean</managed-bean-name>
<managed-bean-class>com.test.HelloWorldBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
</faces-config>
package com.test;
import javax.validation.constraints.Size;
public class HelloWorldBean {
@Size(min=2)
private String nom;
// Accesseur obligatoire ...
public void maFonction(){
nom=nom.toUpperCase();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>MaBanque</display-name>
<welcome-file-list>
<welcome-file>HelloWorld.jsf</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servletclass>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
...
</web-app>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<meta http-equiv="Content-Type"
content="text/html; charset=ISO-8859-1" />
</h:head>
<h:body>
Hello <h:outputLabel value="#{helloWorldBean.nom}" />!
<h:messages />
<h:form>
<h:panelGrid columns="2">
<h:outputText value="Saisir votre nom"></h:outputText>
<h:inputText value="#{helloWorldBean.nom}"></h:inputText>
</h:panelGrid>
<h:commandButton
value="Afficher
nom" action="#{helloWorldBean.maFonction}" />
</h:form>
<h:link value="Recharger page" action="HelloWorld.jsf" />
</h:body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<faces-config
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-facesconfig_2_1.xsd"
version="2.1">
</faces-config>
package com.test;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.validation.constraints.Size;
@ManagedBean
@RequestScoped
public class HelloWorldBean {
@Size(min=2)
private String nom;
// Accesseur obligatoire ...
public void maFonction(){
nom=nom.toUpperCase();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>MaBanque</display-name>
<welcome-file-list>
<welcome-file>HelloWorld.jsf</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servletclass>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
...
</web-app>
JSF
Saisir
Soumettre formulaire
Cliquer lien
Créer un projet Eclipse JSF/JPA
●
●
●
Nous allons travailler avec l'IDE Eclipse qui est l'un des deux
meilleurs IDE pour faire du Java EE
Nous allons également travailler avec GlassFish le serveur
d'application livré avec Java EE
–
Conteneur Java EE Web
–
Conteneur EJB
Nous allons également utiliser les implémentations suivantes :
–
JSF : Mojarra
–
Facelet : PrimeFaces
–
JPA : EclipseLink
Structure du projet JSF/JPA
(sous Eclipse !)
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>Test</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<context-param>
<description>State saving method: 'client' or 'server' (=default). See JSF Specification 2.5.2</description>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<context-param>
<param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
<param-value>resources.application</param-value>
</context-param>
<listener>
<listener-class>com.sun.faces.config.ConfigureListener</listener-class>
</listener>
</web-app>
face-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<faces-config
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-facesconfig_2_1.xsd"
version="2.1">
</faces-config>
persistence.xml
<?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/XMLSchemainstance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="Test">
</persistence-unit>
</persistence>
Connecter votre projet au serveur
Les vue Facelet / XTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<meta http-equiv="Content-Type"
content="text/html; charset=ISO-8859-1" />
</h:head>
<h:body>
Hello <h:outputLabel value="#{helloWorldBean.nom}" />!
<h:messages />
<h:form>
<h:panelGrid columns="2">
<h:outputText value="Saisir votre nom"></h:outputText>
<h:inputText value="#{helloWorldBean.nom}"></h:inputText>
</h:panelGrid>
<h:commandButton
value="Afficher
nom" action="#{helloWorldBean.maFonction}" />
</h:form>
<h:link value="Recharger page" action="HelloWorld.jsf" />
</h:body>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
Les vue Facelet / XTML
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<ui:composition template="/template/monTemplate.xhtml">
<ui:define name="content">
<h:form id="form">
<p:dataTable id="ListeComptes" var="unCompte" value="#{compteControlerBean.listeCompte}"
selectionMode="single" selection="#{ihmSelectionControlerBean.compte}" rowKey="#{unCompte.idCompte}">
<p:column>
<f:facet name="header">
<h:outputText value="Libellé" />
</f:facet>
<h:outputText value="#{unCompte.libelle}" />
</p:column>
<p:column>
<f:facet name="header">
<h:outputText value="Identifiant" />
</f:facet>
<h:outputText value="#{unCompte.identifiant}" />
</p:column>
<p:column>
<f:facet name="header">
<h:outputText value="Valeur actuelle" />
</f:facet>
<h:outputText value="#{unCompte.total}" />
</p:column>
<f:facet name="footer">
<h:commandButton id="voirTransaction" value="Voir Transactions"
action="#{compteControlerBean.voirTransaction}" />
<h:commandButton id="editerCompte" value="Editer Compte"
action="#{compteControlerBean.editerCompte}" />
<h:commandButton id="supprimerCompte" value="Supprimer Compte"
action="#{compteControlerBean.supprimerCompte}" />
<h:commandButton id="ajouterCompte" value="Ajouter Compte"
action="#{compteControlerBean.ajouterCompte}" />
</f:facet>
</p:dataTable>
</h:form>
</ui:define>
</ui:composition>
</html>
Les balises JSF
●
●
●
●
Les balises JSF « facelets » ou « ui » servent à la composition des pages et à la création
des modèles de page (templates)
http://javaserverfaces.java.net/nonav/docs/2.1/vdldocs/facelets/ui/tld-summary.html
Les balises composant JSF « h » servent à afficher des composants graphiques HTML
tels que boutons, liens, champs de saisies, formulaires, …
http://javaserverfaces.java.net/nonav/docs/2.1/vdldocs/facelets/h/tld-summary.html
Les balises des actions JSF « f » servent à gérer des actions tel que Ajax, ActionListener,
la validation des champs saisis de manière indépendante du fournisseur de rendu
http://javaserverfaces.java.net/nonav/docs/2.1/vdldocs/facelets/f/tld-summary.html
Les balises Primefaces « p » servent à afficher des composants graphiques plus évolués
que « h »
http://primefaces.org/
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
Les balises JSTL
●
●
Les balises JSTL « core » « c » servent à insérer des contrôles de flux (if, forEach,
otherwise, ...)
http://javaserverfaces.java.net/nonav/docs/2.1/vdldocs/facelets/c/tld-summary.html
Les balises JSTL « Functions » ou « fn » servent à manipuler des chaînes de caractères
(indexOf, contains, trim, ...)
http://javaserverfaces.java.net/nonav/docs/2.1/vdldocs/facelets/fn/tld-summary.html
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsp/jstl/core"
xmlns:h="http://java.sun.com/jsp/jstl/functions">
XHTML - Template
●
XHTML et Template
–
Permet de créer des éléments de vue réutilisables comme le bandeau et le bas de
page qui sont en règle générale toujours avec les mêmes informations
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1transitional.dtd">
<h:html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title><ui:insert name="title">Default title</ui:insert></title>
</h:head>
<h:body>
<div id="header">
<ui:insert name="header">
<ui:include src="/template/bandeau.xhtml"/>
</ui:insert>
</div>
<div id="content">
<ui:insert name="content">
</ui:insert>
</div>
<div id="footer" style="width:100%;font-size:36px;lineheight:48px;background-color:navy;color:white">
<ui:insert name="footer">
Pied de page par défaut
<br />
<h:graphicImage name="glassfish.png" library="images"/>
<h:graphicImage url="tomcat.gif"/>
</ui:insert>
</div>
</h:body>
</h:html>
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:body>
<div style="width:100%;font-size:36px;line-height:48px;backgroundcolor:navy;color:white">
Ceci est un texte provenant du bandeau
</div>
<p:messages />
</h:body>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<ui:composition template="/template/template.xhtml">
<ui:define name="title">
HelloWorld 2 avec Template
</ui:define>
<ui:define name="content">
Hello <h:outputLabel value="#{helloWorldBean.nom}" />!
<h:messages />
<h:form>
<h:panelGrid columns="2">
<h:outputText value="Saisir votre nom"></h:outputText>
<h:inputText
value="#{helloWorldBean.nom}"></h:inputText>
</h:panelGrid>
<h:commandButton value="Afficher nom"
action="#{helloWorldBean.maFonction}" />
</h:form>
<h:link value="Recharger page" action="HelloWorld.jsf" />
</ui:define>
</ui:composition>
</html>
Les vue Facelet / XTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<head>
<title>Default title</title></head><body>
<div id="header"><?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:p="http://primefaces.org/ui"><body>
<div style="width:100%;font-size:36px;line-height:48px;background-color:navy;color:white">
Ceci est un texte provenant du bandeau
</div>
<p:messages></p:messages></body>
</html>
</div>
<div id="content">
Hello <label>
</label>!
<form id="j_idt19" name="j_idt19" method="post" action="/Test/HelloWorld2.jsf" enctype="application/x-www-form-urlencoded">
<input type="hidden" name="j_idt19" value="j_idt19" />
<table>
<tbody>
<tr>
<td>Saisir votre nom</td>
<td><input type="text" name="j_idt19:j_idt22" /></td>
</tr>
</tbody>
</table>
<input type="submit" name="j_idt19:j_idt23" value="Afficher nom" /><input type="hidden" name="javax.faces.ViewState"
id="javax.faces.ViewState" value="4365122227466430620:1843683584491520865" autocomplete="off" />
</form><a href="/Test/HelloWorld2.jsf">Recharger page</a>
</div>
<div id="footer">
Pied de page par défaut
<img src="/Test/javax.faces.resource/glassfish.png.jsf?ln=images" /><img src="tomcat.gif" />
</div></body>
XHTML - Template
●
ui:insert
–
●
ui:include
–
●
Sert à inclure du code XHTML en provenance d'une autre
page (pareil que pour les JSP)
ui:composition
–
●
Permet de définir un morceau du template qui sera remplacé
(ou pas) par une « instance » du template
Sert à définir qu'une page va utiliser un template
ui:define
–
Sert à définir la portion du template à remplacer
XHTML – Les pièges
●
●
●
Toujours utiliser « h : » devant « body », « head » et « html »
Depuis JSF 2.0 un répertoire « resources » créé dans le
WebContent permet de stocker et d'accéder facilement à des
ressources particulières comme les images, feuilles de style ou
JavaScripts
Attention quand on essaye de définir un lien relatif car il sera
toujours relatif à la page appelé même s'il est définit dans un
template !
–
Hypertext links: <a href="#{request.contextPath}/…"/>
–
Images: <h:graphicImage name="…" library="…"/>
–
CSS: <h:outputStylesheet name="…" library="…"/>
–
Scripts: <h:outputScript name="…" library="…"/>
XHTML – Les pièges
●
Un certains nombre d'objets peuvent être appelés directement
dans une page XHTML
–
application / applicationScope / session / sessionScope / view
/ viewScope / request / requestScope :
Environnement et Attributs des différentes potées applicatives
dans JSF (application, session, vue, requête)
–
cookie : Tous les cookies de l'application
–
header / headerValues : les éléments de l'entête HTTP
–
param / paramValues : les paramètres de la requête
–
initParam : les paramètres d'initialisation
–
facesContext : Le contexte JSF instancié pour la requête
–
component : Le composant courant de type UIComponent
–
compositeComponent : Le composite personnalisé courant
XHTML et EL
●
EL : Expression Language
ou le moyen d'accéder à des Java Beans,
leurs propriétés et leurs fonctions directement
dans le code XHTML
–
"#{nomDeBean.nomDeProp...}"
–
"#{nomDeBean.nomDeFonction}"
<h:body>
Hello <h:outputLabel value="#{helloWorldBean.nom}" />!
<h:messages />
<h:form>
<h:panelGrid columns="2">
<h:outputText value="Saisir votre nom"></h:outputText>
<h:inputText value="#{helloWorldBean.nom}"></h:inputText>
</h:panelGrid>
<h:commandButton value="Afficher nom" action="#{helloWorldBean.maFonction}" />
</h:form>
<h:link value="Recharger page" action="HelloWorld.jsf" />
</h:body>
</html>y
XHTML et EL
●
●
"#{...}" est une expression dite différée car son évaluation a lieu au
moment où le rendu est effectué. Ce type d'expression est utilisé
avec JSF car dans le cycle de vie le rendu est exécuté après la
validation et l'invocation applicative.
"${...}" est une expression dite Immédiate car son évaluation est
immédiatement. Ce type d'expression est utilisé avec JSP où
l'évaluation a lieu après la compilation. Avec les JSF, leur utilisation
est possible. Cela peut servir à effectuer des calculs ou accéder à
des valeurs en lecture seule.
–
${100 div 5}
–
${!empty uneVariable}
–
${pageContext.request.contextPath}
Les EL permettent donc à la vue de
dialoguer avec quoi ?
VUE : XHTML
CONTROLEUR : Managed / Backing Bean
METIER : EJB / JTA
MODELE : JPA
Backing Bean
●
C'est quoi ?
–
Un Java Bean géré par le contexte d'exécution JSF
–
Il possède une portée ou « scope » (Application, Session, Vue, Requête)
dans le but de sauvegarder ces attributs
–
Il permet de relier les valeurs portées par les composants graphiques de la
vue XHTML aux attributs des « Managed Bean »
–
Il existe lorsqu'on utilise les EL
–
L'étape de validation / conversion utilise les valeurs portées par les
composants pour créer des objets dans le « Managed Bean »
<h:body>
Hello <h:outputLabel value="#{helloWorldBean.nom}" />!
<h:messages />
<h:form>
<h:panelGrid columns="2">
<h:outputText value="Saisir votre nom"></h:outputText>
<h:inputText value="#{helloWorldBean.nom}"></h:inputText>
</h:panelGrid>
<h:commandButton value="Afficher nom" action="#{helloWorldBean.maFonction}" />
</h:form>
<h:link value="Recharger page" action="HelloWorld.jsf" />
</h:body>
</html>y
Managed Bean
●
C'est quoi ?
–
Un Java Bean géré par le contexte d'exécution JSF
–
Il possède une portée ou « scope » (Application, Session, Vue, Requête)
dans le but de sauvegarder ces attributs
–
Il possède des méthodes qui sont appelées par les vues XHTML
–
Il est déclaré par annotation ou par fichier xml
–
Il permet d'implémenter la couche contrôleur (et pas la couche métier)
package com.test;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.validation.constraints.Size;
@ManagedBean
@RequestScoped
public class HelloWorldBean {
@Size(min=2)
private String nom;
// Accesseur obligatoire ...
public void maFonction(){
nom=nom.toUpperCase();
}
}
Managed Bean
●
●
Le nom :
–
Il faut toujours suffixer le nom de la classe par « Bean »
–
par défaut le nom d'accès au « bean » est le nom de la classe
avec une minuscule en première lettre
Son instanciation et son enregistrement se fera :
–
Au premier appel du Bean (par défaut)
–
Au démarrage de l'application lorsque le scope est Application
(eager=true)
@ManagedBean(eager=true,name="helloWorldBean")
@ApplicationScoped
public class HelloWorldBean {
Managed Bean
●
Les Scopes ou portée applicatives (usuelles)
–
javax.faces.bean.SessionScoped : les attributs du Bean sont
conservés le temps de la session
–
javax.faces.bean.RequestScoped : les attributs du Bean ne sont
jamais conservés entre deux appels
–
javax.faces.bean.ApplicationScoped : les attributs du Bean sont
partagés pour tous les appels à l'application (quelque soit le client)
–
javax.faces.bean.ViewScoped : les attributs du Bean sont
conservés pour un client tant que la vue est visible
Managed Bean
●
Initialisation/Destruction d'une instance
–
Juste après la création d'une instance, il est possible
d'appeler automatiquement une méthode qui peut
initialiser celle-ci avec l'annotation @PostConstruct
–
Le « Managed Bean » est ensuite associé à son scope
–
Avant d'être détruite une instance peut également
appeler automatiquement une méthode qui peut libérer
des ressources avec l'annotation @PreDestroy
@PostConstruct
public void init(){
// init variable
}
@PreDestroy
public void destroy(){
// relache ressource
}
Managed Bean
●
Les attributs
–
●
JavaBean, donc tous les attributs doivent avoir des accesseurs
La validation des attributs :
–
Plusieurs méthodes
●
●
●
●
●
–
Librarie XHTML « f » des actions JSF
Implémenter la méthode suivante dans un « Managed Bean »
public void maMethodeValidation(FacesContext c, UIComponent u, Object value)
Implémentation de l'interface « javax.faces.Validator »
Implémentation de l'interface « ActionListener »
Usage des tags de validation des Bean
L'étape 3 du cycle de vie « Process Validation » permet de valider les champs
saisis par l'utilisateur avant qu'ils soient affectés dans le « Managed Bean »
●
●
●
Si une erreur de validation apparaît alors une exception est levée et un message
FacesMessage est ajouté dans le FacesContexte
La balise <h : messages /> permet d'afficher les messages ajoutés
Le formulaire de saisie est re-affiché avec les valeurs renseignées
Rappel sur le cycle de vie
Managed Bean
●
Validation avec une méthode particulière dans un « Managed
Bean »
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
@ManagedBean()
@RequestScoped
public class HelloWorldBean {
private String nom;
...
public void maMethodeValidation(FacesContext c, UIComponent u, Object value){
String saisie = (String)value;
if(saisie.length()<2){
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "La saisie est invalide ...", null);
throw new ValidatorException(message);
}
}
}
<h:panelGrid columns="2">
<h:outputText value="Saisir votre nom"></h:outputText>
<h:inputText value="#{helloWorldBean.nom}"
validator="#{helloWorldBean.maMethodeValidation}">
</h:inputText>
</h:panelGrid>
Managed Bean
●
Validation avec l'implémentation de l'interface
« javax.faces.Validator »
import
import
import
import
import
import
javax.faces.application.FacesMessage;
javax.faces.component.UIComponent;
javax.faces.context.FacesContext;
javax.faces.validator.FacesValidator;
javax.faces.validator.Validator;
javax.faces.validator.ValidatorException;
@FacesValidator(value="monValidator")
public class MonTextValidateur implements Validator{
@Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
String saisie = (String)value;
if(saisie.equals("LaValeurSaisie")){
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "La saisie est invalide ...", null);
throw new ValidatorException(message);
}
}
}
<h:outputText value="Saisir votre nom"></h:outputText>
<h:inputText value="#{helloWorldBean.nom}">
<f:validator validatorId="monValidator"/>
</h:inputText>
Managed Bean
●
Validation avec l'usage des tags (@Email, @Size, @NotNull, @Pattern ...)
–
L’inconvénient est que la valeur doit déjà avoir été affectée à l'attribut !
–
Les avantages sont :
●
simplicité
●
répond à une spécification Java (JSR-303)
●
import
import
import
import
les messages par défaut peuvent être surchargés dans le fichier ValidationMessages.properties
à la racine du projet
javax.faces.application.FacesMessage;
javax.faces.bean.ManagedBean;
javax.faces.bean.RequestScoped;
javax.validation.constraints.Size;
@ManagedBean
@RequestScoped
public class HelloWorldBean {
@Size(min=2)
private String nom;
<h:outputText value="Saisir votre nom"></h:outputText>
<h:inputText value="#{helloWorldBean.nom}"></h:inputText>
Managed Bean
●
●
Les valeurs saisies étant des chaînes textes, elle sont nécessairement
converties vers le type cible dans le « Managed Bean ».
L'une des manières est l'implémentation de l'interface
« javax.faces.convert.Converter»
import
import
import
import
javax.faces.component.UIComponent;
javax.faces.context.FacesContext;
javax.faces.convert.Converter;
javax.faces.convert.FacesConverter;
@FacesConverter(value="monConverter")
public class MonConverteur implements Converter {
@Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
// TODO Ecrire du code qui convertie la chaîne en éntrée vers un objet
return null;
}
@Override
public String getAsString(FacesContext context, UIComponent component,
Object value) {
// TODO Ecrire le code de conversion inverse
return null;
}
}
<h:outputText value="Saisir votre nom"></h:outputText>
<h:inputText value="#{helloWorldBean.nom}">
<f:converter converterId="monConverter"/>
</h:inputText>
Managed Bean
●
Un autre moyen d'effectuer de la conversion est d'utiliser les
convertisseurs déjà codés :
–
<f:convertNumber currencySymbole="$" type="currency">
–
<f:convertDateTime pattern="MM/dd/yy">
<h:outputText value="Saisir votre prix"></h:outputText>
<h:inputText value="#{undBean.prix}">
<f:convertNumber currencySymbole="€" type="currency"/>
</h:inputText>
Managed Bean
●
Navigation statique entre les pages, l'attribut « action »
permet de spécifier un nom de page ou un mot clé défini
dans faces-config.xml
// Depuis la vue login.xhtml
<h:commandButton value="Submit Values" action="loginWelcome"/>
// Depuis le fichier faces-config.xml
<navigation-rule>
<description></description>
<from-view-id>/login.xhtml</from-view-id>
<navigation-case>
<from-outcome>loginWelcome</from-outcome>
<to-view-id>/loginWelcome.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
Managed Bean
●
Navigation dynamique entre les pages, l'attribut
« action » permet de spécifier un appel de méthode et le
retour de celle-ci va définir un nom de page destinatrice
ou un mot clé défini dans faces-config.xml
// Depuis la vue login.xhtml
<h:commandButton value="Submit Values" action="#{monBean.nextPage}"/>
// De puis le code de monBean
public String nextPage()
{
if (username.equals("guest") &&
password.equals("guest"))
{
return "loginSuccess";
}
return "loginFailure";
}
<navigation-rule>
<description></description>
<from-view-id>/login.xhtml</from-view-id>
<navigation-case>
<from-outcome>loginSuccess</from-outcome>
<to-view-id>/loginSuccess.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>loginFailure</from-outcome>
<to-view-id>/loginFailure.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
Managed Bean
●
Navigation & Redirection :
–
Par défaut lorsqu'un formulaire navigue vers une autre page alors l'URL du
navigateur n'est pas modifiée.
a.xhtml → b.xhtml mais le navigateur affiche toujours comme URL a.xhtml
–
Dans une balise <navigation-case> il est possible d'ajouter la balise
<redirect/> afin que l'URL affichée dans le navigateur suive la redirection
–
L'autre manière d'effectuer cette redirection dans l'URL est d'utiliser un
paramètre d'URL dans le retour d'une méthode Java ou de l'action du
formulaire.
Ex : «return "loginSuccess?faces-redirect=true" »
Managed Bean
●
●
●
Mais les Managed Bean sont seulement des contrôleurs et
doivent donc communiquer avec des objets « métier » comme
les EJB ou encore avec d'autres contrôleurs
Les attributs des Managed Bean seront donc pas seulement
des champs de l'IHM mais aussi de Java Bean (Entreprise ou
Managed)
L'initialisation des attributs « métier » est usuellement fait par
le conteneur Web et EJB qui pratique l'injection de
dépendances
@ManagedBean
@RequestScoped
public class CompteControlerBean {
@EJB
private CompteEJB compteEjb;
@ManagedProperty(value="#{ihmSelectionControlerBean}")
private IhmSelectionControlerBean ihmSelectionControlerBean;
Passons à la couche métier !
VUE : XHTML
CONTROLEUR : Managed / Backing Bean
METIER : EJB / JTA
MODELE : JPA
EJB : Entreprise JavaBean
●
Le cycle de vie est géré par un conteneur dédié
●
Ils servent à coder le métier et le contrôle de persistance
●
Comme tout JavaBean, les attributs doivent avoir des accesseurs publiques
●
L'accès aux EJB peut être :
●
–
Distant : Le conteneur EJB s'exécute sur une autre JVM que le code client
–
Local : Le conteneur EJB s'exécute sur la même JVM que le code client
Un « lookup » JNDI permet toujours d'accéder à un EJB car le conteneur les inscrit
dans l'annuaire du serveur d'application
NB : depuis Java EE v6 les noms JNDI ont été standardisés et sont donc les mêmes quelque soit le
serveur d'application java:global/[nom-app]/[nom-module]/[nom-bean][!nom-interface-qualifie]
●
●
Par facilité nous noterons simplement EJB les EJB dit Session car nous ne verrons
pas les autres
Ils existent plusieurs type d'EJB
–
Statefull : conservent leur état entre deux appels
–
Stateless : ne conservent aucun état
–
Singleton : l'état est partagé pour tous les appels
EJB accès Distant
●
●
●
●
●
Il faut créer et implémenter une interface qui est annoté
javax.ejb.Remote
Le client doit avoir connaissance de cette interface car il
utilise ce type pour accéder à l'EJB
La récupération d'un EJB dans un client se fait par injection
de dépendance avec l'annotation @EJB ou par « lookup »
JNDI
La communication entre le client et le conteneur EJB
s'effectue sur le protocole RMI/IIOP
L'utilisation de cette technique d'accès est généralement
faite pour les client lourd (ou Riche) déployé ou pas avec
JWS
Mais cela peux également être deux conteneurs d'EJB distant qui
communiquent entre eux
EJB accès Distant
Main
« use »
lookup JNDI
« interface »
MonEJBRemote
« implement »
MonEJB
MonEJB_2
« implement »
MaServlet
« use »
« interface »
MonEJBLocal
lookup JNDI
« use »
accès direct
EJB accès Local
●
Si l'EJB est uniquement accédé localement alors il n'y
a aucune obligation de définir une interface, il suffit de
l'annoter @LocalBean !
(Il peut par contre y avoir un besoin conceptuel ou de bonne
pratique. Pour cela il y a @javax.ejb.Local)
●
●
Le client (un autre EJB ou un Managed Bean ou une
Servlet …) fait simplement une déclaration d'injection
de dépendance avec l'annotation @EJB
Pas de protocole particulier pour la communication
entre le client et l'EJB puisque l'on travaille dans une
même JVM
EJB accès Local
import
import
import
import
import
import
javax.ejb.LocalBean;
javax.ejb.Stateless;
javax.persistence.EntityManager;
javax.persistence.NoResultException;
javax.persistence.PersistenceContext;
javax.persistence.Query;
import mabanque.donnees.Compte;
/**
* Session Bean implementation class CompteEJB
*/
@Stateless
@LocalBean
public class CompteEJB {
@PersistenceContext(unitName="MaBanque_PU")
private EntityManager em;
@SuppressWarnings("unchecked")
public List<Compte> recupererTousLesComptes(){
Query query=em.createNamedQuery("listeCompte");
List<Compte> retour=(List<Compte>)query.getResultList();
em.flush();
for(int i=0;i<retour.size();i++)
retour.get(i).setTotal(calculateTotal(retour.get(i)));
em.clear();
return retour;
}
...accesseurs...
@ManagedBean
@RequestScoped
public class CompteControlerBean {
@EJB
private CompteEJB compteEjb;
@ManagedProperty(value="#{ihmSelectionControlerBean}")
private IhmSelectionControlerBean ihmSelectionControlerBean;
private List<Compte> listeCompte;
@PostConstruct
public void initControler(){
listeCompte=compteEjb.recupererTousLesComptes();
}
...accesseurs...
}
EJB accès local
●
●
●
@EJB(...
–
beanInterface : nom de l'interface désignant la vue métier que l'on souhaite
–
beanName : nom de référence de l'EJB (uniquement disponible si client et
EJB sont sur un même environnement)
–
name : nom avec lequel l'EJB sera recherché
–
lookup : un nom JNDI portable complet
@Stateless(...
–
name : le nom de l'EJB (par défaut le nom de la classe)
–
mappedName : le nom sous lequel l'EJB sera référencé par JNDI
@Statefull et @Singleton
–
Les attributs sont les même
EJB accès particulier
●
●
Certains framework (Struts2) peuvent avoir
besoin de créer des classes de récupération en
masse et de référencement pour offrir un
service d'injection d'EJB. Ce modèle de
conception est souvent appelé
« ServiceLocator »
Il est également possible d'accéder à des EJB
au sein d'un programme PHP via le « lookup »
JNDI
EJB : Stateless
●
●
Un EJB est dit « Stateless » lorsqu'il ne
conserve aucune données entre les requêtes
Il faut le déclarer sur la classe EJB par
l'annotation @Stateless
–
Généralement ces EJB sont placées dans un pool
d'instances et une requête utilise un élément du
pool.
–
Ce type d'EJB est simple et permet d'améliorer la
réactivité du serveur d'application
EJB : Stateless
import
import
import
import
import
import
javax.ejb.LocalBean;
javax.ejb.Stateless;
javax.persistence.EntityManager;
javax.persistence.NoResultException;
javax.persistence.PersistenceContext;
javax.persistence.Query;
import mabanque.donnees.Compte;
/**
* Session Bean implementation class CompteEJB
*/
@Stateless
@LocalBean
public class CompteEJB {
@PersistenceContext(unitName="MaBanque_PU")
private EntityManager em;
@SuppressWarnings("unchecked")
public List<Compte> recupererTousLesComptes(){
Query query=em.createNamedQuery("listeCompte");
List<Compte> retour=(List<Compte>)query.getResultList();
em.flush();
for(int i=0;i<retour.size();i++)
retour.get(i).setTotal(calculateTotal(retour.get(i)));
em.clear();
return retour;
}
...accesseurs...
@ManagedBean
@RequestScoped
public class CompteControlerBean {
@EJB
private CompteEJB compteEjb;
@ManagedProperty(value="#{ihmSelectionControlerBean}")
private IhmSelectionControlerBean ihmSelectionControlerBean;
private List<Compte> listeCompte;
@PostConstruct
public void initControler(){
listeCompte=compteEjb.recupererTousLesComptes();
}
...accesseurs...
}
EJB : Stateless
●
Le cycle de vie et les méthodes de rappels
1.L'EJB n'existe pas
2.Un client appelle une référence sur l'EJB
3.Le conteneur créé l'instance et réalise les injections de dépendance de cet EJB
4.Le conteneur appelle les méthode annotées @PostConstruct
5.L'instance traite l'appel et reste en attente d'autres requêtes dans le pool
6.Lorsque le conteneur s'éteint, il appelle les méthodes annotées @PreDestroy
EJB : Statefull
●
Un EJB est dit « Statefull » lorsqu'il conserve l'état
de la conversation entre chaque appel d'un même
client
–
●
Le mécanisme traditionnel de session est utilisé pour
conserver cet état
Il faut le déclarer sur la classe EJB par l'annotation
@Statefull
–
Attention ce type d'objet est plus compliqué et moins
performant pour la réactivité du conteneur Web car il
faut gérer sa création, sa destruction, sa persistance
dans la session
–
De plus il est consommateur de mémoire
EJB : Statefull
●
Le cycle de vie et les méthodes de rappels
1.L'EJB n'existe pas
2.Un client appelle une référence sur l'EJB
3.Le conteneur créé l'instance et réalise les injections de dépendance de cet EJB
4.Le conteneur appelle les méthode annotées @PostConstruct
5.L'instance traite l'appel et reste dans en mémoire en attendant les autres requête du client
6.Si le client attend un certains temps avant de refaire une requête alors le conteneur appelle la méthode
annotée @PrePassivate puis l'EJB est sérialisé sur un stockage permanent
7.Lorsque le client refait une requête alors le conteneur recharge en mémoire l'EJB et appelle la méthode
annotée @PostActivate
8.Si le client invoque une méthode annotée @Remove alors le conteneur appelle également la méthode
@PreDestroy puis supprimer l'EJB
EJB : Singleton
●
●
Un EJB est dit « Singleton » lorsqu'il conserve
un état de la conversation partagé entre tous
les clients de l'application
Il faut le déclarer sur la classe EJB par
l'annotation @Singleton
–
Il existe une seule instance créée dans le
conteneur EJB et tous les « threads » accèdent de
manière concurrente à l'EJB
–
La concurrence d'accès est gérée via le contexte
JTA qui est initié dans le conteneur et paramétré
par l'application
EJB : Singleton
●
Le cycle de vie et les méthodes de rappels sont identique aux Stateless
1.L'EJB n'existe pas
2.Un client appelle une référence sur l'EJB
3.Le conteneur créé l'instance et réalise les injections de dépendance de cet EJB
4.Le conteneur appel les méthode annotées @PostConstruct
5.L'instance traite l'appel et reste en attente d'autres requêtes
6.Lorsque le conteneur s'éteint il appelle les méthodes annotées @PreDestroy
EJB : Singleton
●
La concurrence des accès peut être gérée selon 3
types grâce à l'annotation @ConcurrencyManagement:
–
CMC : Container-Managed Concurrency (type par défaut)
–
L'annotation @Lock(LockType.WRITE) permet de
spécifier qu'un seul client peut accéder à l'EJB et/ou à
la méthode taguée, les autres attendent leur tour
● Alors que l'annotation @Lock(LockType.READ) autorise
tous les accès en concurrence
BMC : Bean-Managed Concurrency
●
Le programme doit coder la gestion de la concurrence
avec les instructions Java « synchronized » et
« volatile »
Interdite : Lève l'exception ConcurrentAccessException en
cas de tentative d'accès concurrent
●
–
EJB - Packaging
EJB - Packaging
EJB – Elements particuliers
●
●
L'appel de méthodes de manière asynchrone peut maintenant
se faire sans utiliser JMS, en annotant simplement une
méthode @Asynchronous
Il est possible de définir un fichier optionnel ejb-jar.xml qui
définit des ressources permettant l'initialisation d'attributs d'un
EJB par exemple
–
L'attribut ou son « setter » est annoté @Ressource
@Ressource
public void setMonAttributChaine(String arg){
this.monAttributChaine=arg ;
}
–
Le fichier ejb-jar.xml possède des balises
suivantes
<env-entry>
<env-entry-name>monAttributChaine</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>Ma chaine par défaut</env-entry-value>
</env-entry>
EJB – Elements particuliers
●
●
ServiceTimer : ce service a fortement été amélioré depuis la
version 3.1. Il permet à un EJB Stateless d'être rappelé au
moment voulu sans être sollicité par une requête (comme un
cron appelle un exécutable).
–
@Schedules({@Schedule(hour=2 , dayOfWeek=...), …) –
@Timeout
Il est également possible de définir des méthodes qui seront
systématiquement appelées dès qu'une autre méthode du
Bean sera appelée avec l'usage de l'annotation
@AroundInvoke
EJB
●
Il existe d'autres EJB que les EJB Session
–
Les EJB Entité : ils sont remplacés par l'usage de
la spécification JPA
–
Les Message Driven Bean (MDB) qui sont utilisés
pour travailler avec les files d'attente
conformément à la spécification JMS
EJB & JTA
●
L'usage des transactions doit garantir que les manipulations
des données seront
–
Fiable
–
Robuste
–
Conservatrice les propriétés ACID ●
●
●
●
●
Atomicité : plusieurs opérations sont regroupés dans une unité de
traitement tel que l'échec de l'une implique l'échec de toutes
Cohérence : A la fin du traitement, les données sont dans un état cohérent
Isolement : L'état intermédiaire de traitement des données n'est pas
visible en dehors de l'unité de traitement
Durée : Lorsque la transaction est validée alors les données sont visibles
de tous
L'application s'applique à gérer si on doit valider ou invalider
une transaction. Toute la mécanique de gestion de la
ressource vis-à-vis de l'annulation est effectuée
automatiquement par le gestionnaire de transaction JTA.
EJB & JTA
●
Les transactions locales
JTA
JTA
EJB & JTA
●
Les transactions distribués
Two phase commit : prepare & validate
JTA
JTA / XA
JTS / OTS
IIOP
EJB / JTA
●
●
CMT : Container Managed Transaction
–
C'est le conteneur qui décide quand commence et
quand finit une transaction. C'est ce qu'on appelle
la démarcation.
–
C'est la stratégie par défaut pour toute méthode
appartenant à un EJB
BMT : Bean Managed Transaction
–
C'est l'application qui décide où s'effectue la
démarcation
EJB / JTA
●
●
CMT : Container Managed Transaction
–
C'est le conteneur qui gère le début et la fin d'une
transaction. C'est ce qu'on appelle la démarcation.
–
Par défaut, toute méthode d'un EJB s'exécute dans une
transaction
–
L'annotation @TransactionAttribute permet de spécifier au
conteneur une stratégie de démarcation particulière à
appliquer sur une méthode ou tout un EJB
BMT : Bean Managed Transaction
–
C'est l'application qui décide où s'effectue la démarcation
tx.begin(), tx.commit(), tx.rollback()
–
L'EJB ou sa méthode doit être explicitement annoté avec le
tag @TransactionManagement(...)
EJB / CMT
REQUIRED
SUPPORTED
MANDATORY
REQUIRES_NEW
NOT_SUPPORTED
NEVER
EJB / CMT
CLient
Conteneur EJB
1 : ejb1.methodeX()
EJB1
EJB2
2 : begin
4 : ejb2.methodeY()
5 : commit ou rollback
TransactionAttributeType.REQUIRED
Transaction
EJB / CMT
●
●
Quand se déclenche l'annulation d'une transaction ?
–
Lorsqu'une exception système est levée dans la méthode :
JVM, JNDI, SGBD
–
Lorsqu'une exception de type « Runtime » est levée
Que se passe-t-il si la méthode lève une exception
applicative héritant de la classe Exception ?
–
L'exception est retournée mais il n'y a pas de Rollback !
Sauvegardons les données !
VUE : XHTML
CONTROLEUR : Managed / Backing Bean
METIER : EJB / JTA
MODELE : JPA
EJB & JPA
●
●
Le conteneur d'EJB est capable d'initialiser une unité de
persistance automatiquement et de l'injecter dans les EJB qui en
ont besoin
L'objectif est de laisser le conteneur gérer le cycle de vie de l'unité
de persistance et de simplement se concentrer sur les services
qu'elle propose
<?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="MaBanque_PU">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/BanqueBdd</jta-data-source>
<class>mabanque.donnees.Compte</class>
<class>mabanque.donnees.Transaction</class>
<properties>
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
<property name="eclipselink.ddl-generation.output-mode" value="both"/>
<property name="eclipselink.jdbc.batch-writing" value="JDBC"/>
</properties>
</persistence-unit>
</persistence>
import
import
import
import
import
import
javax.ejb.LocalBean;
javax.ejb.Stateless;
javax.persistence.EntityManager;
javax.persistence.NoResultException;
javax.persistence.PersistenceContext;
javax.persistence.Query;
import mabanque.donnees.Compte;
/**
* Session Bean implementation class CompteEJB
*/
@Stateless
@LocalBean
public class CompteEJB {
@PersistenceContext(unitName="MaBanque_PU")
private EntityManager em;
JPA
●
●
●
●
Cette spécification (JSR-317) a pour but de standardiser la persistance
des objets Java au sein des BBD Relationnelles
Actuellement nous sommes à la version 2.0 de JPA
Attention cette spécification n'est pas implémentée dans Java, il faut
utiliser un ORM (EclipseLink, Hibernate, …)
L'utilisation des annotations a permis de faciliter l'écriture du code alors
qu'avant nous étions obligé de tout écrire dans des fichiers XML et des
erreurs étaient vues de manières tardives
JPA
JPA
●
On trouve dans JPA des annotations qui permettent :
–
De lier une classe Java à une table du SGBD
les attributs seront les colonnes de cette table
● les instances seront les tuples
De lier les associations et agrégations entre classes aux relations entre tables
(clés primaires, clés étrangères, table de jointure, ...)
●
–
–
●
●
De spécifier des contraintes purement SGBD sur des artefacts Java (type des
colonnes, taille, non nulle, …) et d'effectuer de la validation
On trouve également dans JPA le moyen d'effectuer des requêtes :
–
API Criteria : La structure de la requête est construite avec des objets Java
–
API JPQL : ressemble à du code SQL mais permet l'usage des type Java
–
SQL : Requêtes de base SQL
Un ORM implémente les mécanismes derrières JPA
@Entity et @Table
●
Permet de lier une classe à une table et lier des
attributs à des colonnes
–
Les annotations de définition des colonnes peuvent être
mises sur les attributs mais également sur les « getter » !
import
import
import
import
import
javax.persistence.Entity;
javax.persistence.FetchType;
javax.persistence.Id;
javax.persistence.Basic;
javax.persistence.Table;
@Entity
@Table(name="PERSONNE")
public class Personne {
@Id
@Column(name="ID_PERSONNE")
private Long idPersonne;
@Column(name="NOM", nullable=false, length=512)
private String nomPersonne;
@Basic(fetch=FetchType.LAZY,optional=true)
private String prenom;
public Long getIdPersonne() {
return idPersonne;
}
...accesseurs...
import
import
import
import
import
javax.persistence.Entity;
javax.persistence.FetchType;
javax.persistence.Id;
javax.persistence.Basic;
javax.persistence.Table;
@Entity
@Table(name="PERSONNE")
public class Personne {
private Long idPersonne;
@Column(name="NOM", nullable=false, length=512)
private String nomPersonne;
@Basic(fetch=FetchType.LAZY,optional=true)
private String prenom;
@Id
@Column(name="ID_PERSONNE")
public Long getIdPersonne() {
return idPersonne;
}
...accesseurs...
@Id - @GeneratedValue
●
●
Un tuple d'une table est généralement toujours identifié par une clé primaire (not null, unique)
–
Clé simple : un seul et unique attribut de la table
–
Clé composé : un ensemble d'attributs
Il est possible (et même conseillé) de laisser générer automatiquement une clé primaire simple
–
Le type doit être : Byte, Integer, Short, Long, Character, String, BigInteger, Date
–
Le moyen :
@GeneratedValue(strategy=GenerationType.<type>)
●
●
●
●
SEQUENCE : Demande au SGBD de créer un objet séquence qui gérera
l'incrémentation automatique de la valeur
IDENTITY : Créer une colonne avec un type spécifique au fournisseur qui permet
l'incrémentation
TABLE : L'auto-incrémentation est géré dans une table
AUTO : Laisse libre choix au SGBD de choisir le gestionnaire d'incrémentation le plus
adapté au type de la colonne
@Entity
@Table(name="PERSONNE")
public class Personne {
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE)
@Column(name="ID_PERSONNE")
private Long idPersonne;
@EmbeddedId
●
Il est également possible d'avoir des clés composites :
@EmbeddedId
–
Cette classe composite ne doit pas avoir de @Id
–
Cette classe composite doit surcharger la méthode
equals()
–
Cette classe composite doit surcharger la méthode
hashCode()
@Entity
@Entity
@Table(name="PERSONNE")
public class Personne {
@EmbeddedId
private PersonneId personneId;
public class PersonneId {
private String nom;
private String prenom;
public int hashCode() {
return (nom + "_" + prenom).hashCode();
}
public boolean equals(Object obj) {
... == ... instanceof ...
}
NB : Une autre annotation (moins utilisée) permet de définir une classe comme un
identifiant : @ClassId
@Column
●
Ce tag sert à caractériser une colonne de la table :
–
name : le nom de la colonne
–
unique : true ou false, doit-elle être unique ?
–
nullable : true ou false, peut-elle être nulle ?
–
insertable : true ou false, doit-elle être prise en compte dans l'insertion/création ?
–
updatable : true ou false, doit-elle être prise en compte dans une mise à jour ?
–
columnDefinition : donne des précisions sur la création de la colonne (
–
Length : s'applique uniquement sur le type chaîne et précise la longueur de la
chaîne
–
precision/scale : s'appliquent uniquement sur des types numériques afin de définir
respectivement le nombre de chiffres total qui représentent ce numérique ainsi que
le nombre de chiffres total autorisés à droite de la virgule
@Column(columnDefinition = "CHAR(40)")
private String name;
// Par défaut String = varchar2(255)
@Column(name="MON_TEST", nullable=false, columnDefinition="boolean default false")
public Boolean getBoleanValue() {
return test;
}
@Basic
●
Permet de préciser 2 caractéristiques basiques sur la colonne :
–
La première porte sur la nullité potentielle :
optional = (true | false)
–
La seconde porte sur le chargement immédiat ou différé de l'objet
fetch = ( FetchType.EAGER | FetchType.LAZY )
import
import
import
import
import
javax.persistence.Entity;
javax.persistence.FetchType;
javax.persistence.Id;
javax.persistence.Basic;
javax.persistence.Table;
@Entity
@Table(name="PERSONNE")
public class Personne {
private Long idPersonne;
@Column(name="NOM", nullable=false, length=512)
private String nomPersonne;
@Basic(fetch=FetchType.LAZY,optional=true)
private String prenom;
@Id
@Column(name="ID_PERSONNE")
public Long getIdPersonne() {
return idPersonne;
}
...accesseurs...
@Temporal
●
Ce tag permet de spécifier le contenu et le type du stockage
sur la colonne ayant un type temporel :
–
@Temporal(TemporalType.DATE) : s'applique sur un attribut
java.util.Date désignant une date dans le temps
–
@Temporal(TemporalType.TIME) : s'applique sur un attribut de
type java.util.Date désignant un horaire
–
@Temporal(TemporalType.TIMESTAMP) : s'applique sur un
attribut de type java.util.Date désignant une date et un horaire
@Entity
public class Personne {
...
@Temporal(TemporalType.TIME)
private Date heureDeNaissance;
...
@Enumerated
●
Depuis peu, il est possible de définir des
colonnes ayant des valeurs déterminées à
l'avance telles que sont les énumérations !
@Entity
public class Personne {
public enum Genre {
HOMME,
FEMME,
INCONNU
}
...
@Enumerated(EnumType.STRING)
private
...
Genre
genre;
@Lob
●
Permet de définir des colonnes de type « gros
objet »
–
CLOB : Character Large Object (fichiers textes, ...)
–
BLOB : Binary Large Object (images, ...)
@Transient
●
Permet de définir un attribut qui ne sera pas
représenté par une colonne
–
Une entité peut avoir besoin d'avoir un attribut qui
ne doit pas forcément être traité par la couche
persistance.
–
L'attribut en question sera valué de manière
programmatique au bon moment
@Embedded
●
Lorsque qu'une classe doit être persisté dans
les colonnes d'une autre entité :
@Entity
public class Personne {
...
private String adresseMail;
private String fournisseur;
@Embedded
private
...
Mail
public class Mail {
eMail;
...
@version
●
Cette annotation permet de définir un attribut qui sera géré
automatiquement par la couche de persistance afin de
positionner un numéro de version à l'entité
–
Lors des accès concurrent à une même entité si l'on essaye de
mettre à jour une entité qui n'a pas le même numéro de version
dans le SGBD alors une exception est levée
@Entity
public class Person {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private int salary;
@Version
private long version;
// ...getters and setters
}
@OneToOne
import
import
import
import
import
import
import
import
import
import
import
javax.persistence.Column;
javax.persistence.Entity;
javax.persistence.FetchType;
javax.persistence.Id;
javax.persistence.Basic;
javax.persistence.JoinColumn;
javax.persistence.ManyToMany;
javax.persistence.ManyToOne;
javax.persistence.OneToMany;
javax.persistence.OneToOne;
javax.persistence.Table;
@Entity
@Table(name="PERSONNE")
public class Personne {
@Id
@Column(name="ID_PERSONNE")
private Long id;
@OneToOne
private PermisDeConduire permis;
import
import
import
import
import
import
javax.persistence.Column;
javax.persistence.Entity;
javax.persistence.Id;
javax.persistence.OneToOne;
javax.persistence.Temporal;
javax.persistence.TemporalType;
@Entity
public class PermisDeConduire {
@Id
@Column(name="ID_PERMIS")
String numero;
@Temporal(TemporalType.DATE)
Date dateDelivrancePermisA;
@Temporal(TemporalType.DATE)
Date dateDelivrancePermisB;
// Bidirectionnelle
@OneToOne(mappedBy="permis")
Personne beneficiaire;
// Unidirectionnelle
@OneToOne
Personne delivrerar;
@OneToMany
@Entity
@Table(name="PERSONNE")
public class Personne {
@Id
@Column(name="ID_PERSONNE")
private Long id;
@OneToOne
private PermisDeConduire permis;
@OneToMany(mappedBy="proprietaire")
private List<Voiture> mesVoitures;
@ManyToMany
private Set<Personne> collegues;
@Entity
public class Voiture {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="ID_VOITURE")
Long id;
String marque;
String modele;
@Temporal(TemporalType.DATE)
Date dateAchat;
@ManyToOne
Personne proprietaire;
@ManyToOne
@Entity
@Table(name="PERSONNE")
public class Personne {
@Id
@Column(name="ID_PERSONNE")
private Long id;
@OneToOne
private PermisDeConduire permis;
@OneToMany(mappedBy="proprietaire")
private List<Voiture> mesVoitures;
@ManyToMany
private Set<Personne> collegues;
@Entity
public class Voiture {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="ID_VOITURE")
Long id;
String marque;
String modele;
@Temporal(TemporalType.DATE)
Date dateAchat;
@ManyToOne
Personne proprietaire;
@ManyToMany
@Entity
@Table(name="PERSONNE")
public class Personne {
@Id
@Column(name="ID_PERSONNE")
private Long id;
@OneToOne
private PermisDeConduire permis;
@OneToMany(mappedBy="proprietaire")
private List<Voiture> mesVoitures;
@ManyToMany
private Set<Personne> collegues;
Création automatique d'un table d'association
Héritage
@Entity
@Table(name="PERSONNE")
public class Personne {
@Id
@Column(name="ID_PERSONNE")
private Long id;
@OneToOne
private PermisDeConduire permis;
@OneToMany(mappedBy="proprietaire")
private List<Voiture> mesVoitures;
@ManyToMany
private Set<Personne> collegues;
@Entity
public class Etudiant extends Personne {
String numeroEtudiant;
Héritage
@Entity
@Table(name="PERSONNE")
@Inheritance(strategy=InheritanceType.JOINED)
public class Personne {
@Id
@Column(name="ID_PERSONNE")
private Long id;
@OneToOne
private PermisDeConduire permis;
@OneToMany(mappedBy="proprietaire")
private List<Voiture> mesVoitures;
@ManyToMany
private Set<Personne> collegues;
@Entity
public class Etudiant extends Personne {
String numeroEtudiant;
Dialoguer avec le SGBD
●
●
Nous savons maintenant mapper les classes
« entités » sur des tables de SGBD !
Mais comment accède-t-on à ces entités ?
–
Pour cela on utilise un EntityManager, il orchestre tous
les échanges avec la ressources SGBD
●
●
●
–
Enregistrement
Requêtes de récupération
Mise à jour sur et depuis le stockage
Il y a plusieurs moyen d'instancier un EntityManager
(Persistence Manager) mais dans le cadre de ce cours
nous allons laisser soin au conteneur EJB de gérer
cela pour nous !
●
●
Nous allons simplement demander au conteneur de nous fournir un
EntityManager via le nom JNDI d'une unité de persistance
Puis nous allons utiliser les méthodes qui s'offrent à nous
Dialoguer avec le SGBD
●
Il faut d'abord créer une ressource de données
dans le serveur d'application et lui donner un
nom JNDI
Dialoguer avec le SGBD
●
Il faut ensuite lier votre projet à cette source
de données avec le fichier « persistence.xml »
<?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/persistence_2_0.xsd">
<persistence-unit name="Test">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/BanqueBdd</jta-data-source>
<properties>
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
<property name="eclipselink.ddl-generation.output-mode" value="both"/>
<property name="eclipselink.jdbc.batch-writing" value="JDBC"/>
</properties>
</persistence-unit>
</persistence>
Dialoguer avec le SGBD
●
Il reste juste à injecter un EntiteManager dans
l'EJB
@Stateless
public class PersonneEJB {
@PersistenceContext(unitName="Test")
private EntityManager em;
public void createPersonne(Personne p){
em.persist(p);
}
Dialoguer avec le SGBD
●
Les méthodes de « javax.persistence.EntityManager » :
–
void persist(Object) : insère les valeurs de l'instance Java dans le SGBD
–
void refresh(Object) : applique à l'instance Java les valeurs stockées en
bases (quelque soit les modifications effectuées sur l'instance Java)
–
Object find(Class, Object) : récupère un tuple du SGBD
–
Object merge(Object) : retourne une instance Java dont les valeurs ont
été fusionnées avec celle du SGBD
–
remove(Object) : supprime l'entité du SGBD
–
clear()/detach(Object) : détache toutes les/une entité(s) de l'EM
–
createNameQuery : requête jPQL / SQL
–
createNativeQuery : requête SQL
–
createQuery : requête Criteria / jPQL
–
lock : pose un verrou dans le SGBD
Dialoguer avec le SGBD
●
Le cycle de vie d'une entité et ses méthodes
de rappels définis par annotation
@PreUpdate
@PostUpdate
@PostLoad
@PrePersist
@PostPersist
@PreRemove
@PostRemove
@PreUpdate
@PostUpdate
@PostLoad
Dialoguer avec le SGBD
Requêtes nommées : jPQL
@Entity
@NamedQuery(name="findEtudiantFromNumero", query="SELEST e FROM Etudiant e WHERE e.numeroEtudiant LIKE ?1")
public class Etudiant extends Personne {
@Stateless
public class EtudiantDAO {
@PersistenceContext(unitName="Test")
private EntityManager em;
public EntityManager getEm() {
return em;
}
public void setEm(EntityManager em) {
this.em = em;
}
@SuppressWarnings("unchecked")
public List<Etudiant> findEtudiantFromNumero(String debutDuNumero){
Query q=em.createNamedQuery("findEtudiantFromNumero");
q.setParameter(1,debutDuNumero+"%");
q.setMaxResults(10);
return (List<Etudiant>)q.getResultList();
}
Dialoguer avec le SGBD
Requêtes nommées : jPQL
@Entity
@Table(name="PERSONNE")
@NamedQueries({
@NamedQuery(name="findAllPersonnes", query="SELECT p FROM Personne P"),
@NamedQuery(name="findPersonneFromPermis", query="SELECT p FROM Personne p WHERE p.permis = :permis")
})
public class Personne {
@Id
@Column(name="ID_PERSONNE")
private Long id;
@Stateless
public class PersonneDAO {
@PersistenceContext(unitName="Test")
private EntityManager em;
@SuppressWarnings("unchecked")
public List<Personne> findAllPersonne(){
Query query=em.createNamedQuery("findAllPersonnes");
return (List<Personne>)query.getResultList();
}
@SuppressWarnings("unchecked")
public List<Personne> findPersonneFromPermis(PermisDeConduire permis){
Query query=em.createNamedQuery("findPersonneFromPermis");
query.setParameter("permis", permis);
return (List<Personne>)query.getResultList();
}
Dialoguer avec le SGBD
Requête native nommée : SQL
@Entity
@Table(name="PERSONNE")
@NamedNativeQuery(name="findAll", query="select * from PERSONNE")
public class Personne {
@Id
@Column(name="ID_PERSONNE")
private Long id;
@Stateless
public class PersonneDAO {
@PersistenceContext(unitName="Test")
private EntityManager em;
@SuppressWarnings("unchecked")
public List<Personne> findAll(){
Query query=em.createNamedQuery("findAll",Personne.class);
return (List<Personne>)query.getResultList();
}
Dialoguer avec le SGBD
●
Les requêtes dynamiques :
–
em.createQuety(<jPQSL String>)
–
em.createQuety(CriteriaQuery)
–
em.createNativeQuery(<SQL String>)
Dialoguer avec le SGBD
API Criteria
@Entity
@NamedQuery(name="findEtudiantFromNumero", query="SELEST e FROM Etudiant e WHERE e.numeroEtudiant LIKE ?1")
public class Etudiant extends Personne {
@Stateless
public class EtudiantDAO {
@PersistenceContext(unitName="Test")
private EntityManager em;
@SuppressWarnings("unchecked")
public List<Etudiant> findEtudiantFromNumero(String debutDuNumero) {
Query q=em.createNamedQuery("findEtudiantFromNumero");
q.setParameter(1,debutDuNumero+"%");
q.setMaxResults(10);
return (List<Etudiant>)q.getResultList();
}
@SuppressWarnings("unchecked")
public List<Etudiant> findEtudiantFromNumeroWithCriteria(String debutDuNumero) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Etudiant> query= cb.createQuery(Etudiant.class);
Root<Etudiant> etudiant = query.from(Etudiant.class);
ParameterExpression<String> param = cb.parameter(String.class);
Expression<String> path = etudiant.get("numeroEtudiant");
query.select(etudiant).where(cb.like(path, param));
TypedQuery<Etudiant> typedQuery = em.createQuery(query);
typedQuery.setParameter(param,debutDuNumero+"%");
typedQuery.setMaxResults(10);
return (List<Etudiant>)typedQuery.getResultList();
}
Dialoguer avec le SGBD
Verrou sur les données
●
Pourquoi mettre un verrou ?
–
●
●
●
Gérer la concurrence des accès
Quelles méthodes peuvent poser un verrou
–
find
–
refresh
–
lock
Les verrous peuvent être optimistes, la donnée est relue pour être verrouillée avant le
commit :
–
LockModeType.OPTIMISTIC
–
LockModeType.OPTIMISTIC_FORCE_INCREMENT
–
Une entité avec un @Version utilise par défaut un verrou optimiste
Les verrous peuvent être pessimiste, la donnée est verrouillée dès son accès :
–
LockModeType.PESSIMISTIC_READ
–
LockModeType.PESSIMISTIC_WRITE
–
LockModeType.PESSIMISTIC_FORCE_INCREMENT
Le code c'est < 20 % !
●
●
Que doit-on mettre en place autour de la production de code ?
–
Un environnement de gestion de sources
–
Un environnement de développement
–
Un environnement d'intégration continue
–
Un environnement de test pour développeur
–
Un environnement de test unitaire
–
Un environnement de test fonctionnel
–
Un environnement de test syntaxique
Oui mais il y a aussi toutes les autres étapes d'un projet
–
Définition du besoin
–
Planification
–
Spécification
–
Recette
–
Déploiement
–
Formation
–
Exploitation
–
Maintenance
–
Évolutions
Et la mobilité ?
Les références
●
http://www.siteduzero.com/
●
http://www.primefaces.org/
●
http://jean-luc.massat.perso.luminy.univmed.fr/ens/jee/servlet.html
●
http://www.jmdoudoux.fr/java/dej/chap-jsp.htm
●
http://kmdkaci.developpez.com/
●
http://www.mkyong.com
●
http://javaweb.developpez.com/faq/jsf/
●
http://blog.ippon.fr/2011/10/11/jpa-une-magie-qui-se-merite-retour-aux-sources-de-jpa/
●
●
●
●
Développement n-tiers avec Java EE
Jérôme LAFOSSE
Java EE6 et GlassFish 3
Antonio Goncalves