Dans cet article nous allons voir comment créer une page JSF en relation avec une base de données MySQL. Le serveur utilisé est Glassfish.
Nous nous intéressons à Java EE7.
Les notions suivantes seront abordées :
- CDI (Context Dependency Injection)
- page JSF
- controller pour respecter le modèle MVC
- EJB session
- Entity avec JPA.
CDI (Context Dependency Injection)
CDI est un mécanisme permettant d'injecter un objet dans un autre. Au lieu de réaliser new VotreObjet(), le container Java réalise cette action à votre place.
Ce mécanisme sera utilisé dans des pages JSF faisant appel à des classes Java.
Ce mécanisme sera utilisé dans des classes Java utilisant des objets Java qui seront injectés.
JSF
Il s'agit de la technologie de Java EE7 permettant décrire des pages web dynamiques.
EJB Session
Il s'agit d'objet Java représentant des traitements, ces objets ayant une durée de vie ne dépassant pas le GET et sa réponse.
Entity avec JPA et son impémentation EclipseLink
Il s'agit de réaliser un mapping entre un objet Java et la base de données.
L'exemple que nous allons traiter
Une page web permettant de définit un projet avec un nom.
Le fichier pom.xml du projet
<?xml version="1.0" encoding="UTF-8"?> <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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany.prj1</groupId> <artifactId>Prj1TestJsf</artifactId> <version>1.0</version> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.16</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> </dependencies> <build> <finalName>jsf</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.4</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins> </build> </project>
L'entity bean project
Il s'agit d'un simple POJO avec des annotations pour la base de données.
Les annotations utilisées sont les suivantes :
- @Entity : indispensable, pour déclarer que cette classe doit être gérée par JPA
- @Table pour préciser le nom de la table. Par défaut, la table prend le nom de la classe Java, donc on aurait pu l'omettre
- @Id pour indiquer quelle est la clé primaire de la table
- @GeneratedValue : complète le tag précédent pour indiquer comment id sera calculé. Ici, la base de données utilisera une séquence
- @Column : pour préciser le nom de la colonne qui représentera l'attribut. Par défaut, le nom d'une colonne est égal au nom de son attribut. Dans notre exemple, a colonne NAME est unique
package com.mycompany.prj1.beans; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Table; /** * * @author thierry */ @Entity @Table(name = "PROJECT") @NamedQueries({ @NamedQuery(name = Project.FIND_ALL_PROJECTS, query = "SELECT dto FROM Project dto"), @NamedQuery(name = Project.FIND_BY_NAME, query = "SELECT dto FROM Project dto where upper(dto.name) like upper (:pName)") }) public class Project { public static final String FIND_ALL_PROJECTS = "PROJECT_FIND_ALL"; public static final String FIND_BY_NAME = "PROJECT_FIND_BY_NAME"; public Project() { } @Id @Column(name = "PROJECT_ID") @GeneratedValue(strategy = GenerationType.AUTO) private Long projectId; @Column(name = "NAME", unique = true) private String name; public Long getProjectId() { return projectId; } public void setProjectId(Long projectId) { this.projectId = projectId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return "Project : name=" + name; } }
L'interface métier à implémenter
L'interface métier se limite pour l'instant à la création d'un projet en base de données et à la récupération de la liste des projets en base de données.
package com.mycompany.prj1.inter; import com.mycompany.prj1.beans.Project; /** * * @author thierry */ public interface InterfaceProjectDao { public Project insertProject(Project dto);
public List<Project> getAllProjects(); }
L'EJB session implémentant cette interface
Quelques explications :
- notre classe est un simple POJO annotée EJB stateless
- notre classe sera injectée via CDI dans le controller, d'où l'annotation LocalBean
- notre classe référence l'unité de persistence à utiliser. L'unité de persistence désigne une source de données qui a été préalablement déclaré dans le serveur Glassfish. Ces informations sont définies dans le fichier persistence.xml.
package com.mycompany.prj1.ejb; import com.mycompany.prj1.beans.Project; import com.mycompany.prj1.inter.InterfaceProjectDao; import java.util.List; import javax.ejb.LocalBean; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.apache.log4j.Logger; /** * * @author thierry */ @Stateless @LocalBean public class ProjectDaoBean implements InterfaceProjectDao { private static final Logger logger = Logger.getLogger(ProjectDaoBean.class); @PersistenceContext(unitName = "puBdProject") private EntityManager entityManager; public Project insertProject(Project project) { logger.info("Sauvegarde du projet : " + project. toString()); if (project.getProjectId() == null) { saveNewProject(project); } else { updateProject(project); } return project; } public List<Project> getAllProjects() { List<Project> list = null; try { list = entityManager.createNamedQuery(Project.FIND_ALL_PROJECTS).getResultList(); } catch (Exception e) { logger.error("Exception, message d'erreur : " + e.toString()); e.printStackTrace(); } return list; } private void updateProject(Project project) { logger.info("In updateProject"); entityManager.merge(project); } private void saveNewProject(Project project) { logger.info("In saveNewProject"); entityManager.persist(project); } }
Le fichier persistence.xml
Il doit se trouver dans le répertoire META-INF. Ci-dessous ce fichier :
<?xml version="1.0" encoding="UTF-8"?> <persistence version="1.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_1_0.xsd"><persistence-unit name="puBdProject" transaction-type="JTA"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <jta-data-source>jdbc/datasourceBdProject</jta-data-source> <properties> <property name="javax.persistence.schema-generation.database.action" value="create"/> </properties> </persistence-unit> </persistence>
Premier écran : un écran pour saisir un projet, un autre écran affichant le résultat
Le controler JSF ProjectController de la page insert_project.xhtml
Ce controler JSF sera déclaré dans la page JSF et fera le lien avec notre précédent EJB. Ce controller est donc la partie contrôle du modèle MVC.
Il s'agit d'un bean nommé, c'est à dire un bean géré par la servlet JSF. Ce bean sera appelable depuis une page JSF. Un "named bean" est un "managed bean" mais il est géré plus précisément par la servlet JSF. Un named bean doit être annoté par @Named et par le scope.
package com.mycompany.prj1.jsf; import com.mycompany.prj1.beans.Project; import com.mycompany.prj1.ejb.ProjectDaoBean; import java.util.ArrayList; import java.util.List; import javax.ejb.EJB; import javax.enterprise.context.RequestScoped; import javax.inject.Named; import org.apache.log4j.Logger; /** * * @author thierry */ @Named @RequestScoped public class ProjectController { private static final Logger logger = Logger.getLogger(ProjectController.class); private Project project; private List<Project> projects; public ProjectController() { logger.info("In ProjectController"); } @PostConstruct public void postConstruct() { project = new Project(); logger.info("In ProjectController postConstruct"); } @EJB private ProjectDaoBean projectDaoBean; public String saveProject() { String returnValue = "display_project"; //le controller renverra vers la page display_project.xhtml logger.info("Sauvegarde du projet : " + project. toString()); try { projectDaoBean.insertProject(project); } catch (Exception e) { e.printStackTrace(); returnValue = "error_saving"; return returnValue; } // on récupère la liste des projets //returnValue = getAllProjects(); return returnValue; } public String getAllProjects() { String returnValue = "display_projects"; try { projects = projectDaoBean.getAllProjects(); if (projects != null) { logger.info("Taille de projects=" + projects.size()); } else { logger.info("Taille de projects=0"); } } catch (Exception e) { e.printStackTrace(); returnValue = "error_saving"; } return returnValue; } public List<Project> getProjects() { return projects; } public void setProjects(List<Project> projects) { this.projects = projects; } public Project getProject() { return project; } public void setProject(Project project) { this.project = project; projects = new ArrayList<>(); } }
La page JSF permettant de sauvegarder notre projet (insert_project.xhtml)
Cette page utilise le controller de nom projectController. C'est pourquoi la classe ProjectController contenant l'attribut @Named.
<?xml version='1.0' encoding='UTF-8' ?> <!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"> <h:head> <title>Enter Project Data</title> </h:head> <h:body> <h:outputStylesheet library="css" name="styles.css"/> <h:form id="projectForm"> <h:messages/> <h:panelGrid columns="2" columnClasses="rightAlign,leftAlign"> <h:outputLabel for="name" value="Nom du projet:"> </h:outputLabel> <h:inputText id="name" label="Nom du projet" value="#{projectController.project.name}" required="true"> <f:validateLength minimum="2" maximum="30"> </f:validateLength> </h:inputText> <h:panelGroup></h:panelGroup> <h:commandButton value="Save" action="#{projectController.saveProject}"/> </h:panelGrid> </h:form> </h:body> </html>
La page JSF affichant le résultat de la sauvegarde (display_project.xhtml)
Elle utilise comme la précédente page le bean de nom projectController.
<?xml version='1.0' encoding='UTF-8' ?> <!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"> <h:head> <title>Project Information</title> </h:head> <h:body> <h:panelGrid columns="2"> <h:outputLabel for="firstName" value="Project name : "/> <h:outputText id="firstName" value="#{projectController.project.name}"/> </h:panelGrid> </h:body> </html>
La page JSF indiquant une erreur lors de la sauvegarde du projet (error_saving.xhtml)
<?xml version='1.0' encoding='UTF-8' ?> <!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"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>Error Saving</title> </head> <body> <p>Error Saving</p> </body> </html>
Deuxième écran : un écran pour saisir un projet, un autre écran affichant la liste des projets en base de données
Le controler JSF ProjectControllerV2 de la page insert_project_then_display_projects.xhtml
package com.mycompany.prj1.jsf; import com.mycompany.prj1.beans.Project; import com.mycompany.prj1.ejb.ProjectDaoBean; import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; import javax.ejb.EJB; import javax.enterprise.context.RequestScoped; import javax.inject.Named; import org.apache.log4j.Logger; /** * * @author thierry */ @Named @RequestScoped public class ProjectControllerV2 { private static final Logger logger = Logger.getLogger(ProjectControllerV2.class); private Project project; private List<Project> projects; public ProjectControllerV2() { logger.info("In ProjectController"); } @PostConstruct public void postConstruct() { project = new Project(); logger.info("In ProjectController postConstruct"); } @EJB private ProjectDaoBean projectDaoBean; public String saveProject() { String returnValue = "display_project"; logger.info("Sauvegarde du projet : " + project. toString()); try { projectDaoBean.insertProject(project); } catch (Exception e) { e.printStackTrace(); returnValue = "error_saving"; return returnValue; } returnValue = getAllProjects(); logger.info("returnValue : " + returnValue); return returnValue; } public String getAllProjects() { String returnValue = "display_projects"; try { projects = projectDaoBean.getAllProjects(); if (projects != null) { logger.info("Taille de projects=" + projects.size()); } else { logger.info("Taille de projects=0"); } } catch (Exception e) { e.printStackTrace(); returnValue = "error_saving"; } return returnValue; } public List<Project> getProjects() { return projects; } public void setProjects(List<Project> projects) { this.projects = projects; } public Project getProject() { return project; } public void setProject(Project project) { this.project = project; projects = new ArrayList<>(); } }
La page JSF permettant la déclaration du projet à créer (insert_project_then_display_projects.xhtml)
Cette page utilise le controller de nom projectControllerV2.
<?xml version='1.0' encoding='UTF-8' ?> <!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"> <h:head> <title>Enter Project Data</title> </h:head> <h:body> <h:outputStylesheet library="css" name="styles.css"/> <h:form id="projectForm"> <h:messages/> <h:panelGrid columns="2" columnClasses="rightAlign,leftAlign"> <h:outputLabel for="name" value="Nom du projet:"> </h:outputLabel> <h:inputText id="name" label="Nom du projet" value="#{projectControllerV2.project.name}" required="true"> <f:validateLength minimum="2" maximum="30"> </f:validateLength> </h:inputText> <h:panelGroup></h:panelGroup> <h:commandButton value="Save" action="#{projectControllerV2.saveProject}"/> </h:panelGrid> </h:form> </h:body> </html>
La page JSF affichant le résultat de la sauvegarde (display_projects.xhtml)
Elle utilise comme la précédente page le bean de nom projectControllerV2.
<?xml version='1.0' encoding='UTF-8' ?> <!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://xmlns.jcp.org/jsf/core"> <h:head> <title>Project Information</title> </h:head> <h:body> <h:panelGrid columns="2"> <h:dataTable var="x" value="#{projectControllerV2.projects}" summary="Liste des projets" border="1"> <f:facet name="header"><h:outputText value="Liste des projets"></h:outputText></f:facet> <h:column> <f:facet name="keader"><h:outputText value="Nom"></h:outputText></f:facet> <h:outputText value="#{x.name}"></h:outputText> </h:column> </h:dataTable> </h:panelGrid> </h:body> </html>
La page JSF indiquant une erreur lors de la sauvegarde du projet (error_saving.xhtml)
<?xml version='1.0' encoding='UTF-8' ?> <!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"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>Error Saving</title> </head> <body> <p>Error Saving</p> </body> </html>
Troisième écran : un seul écran pour saisir un projet et voir la liste des projets
Le controler JSF ProjectControllerV3 de la page project.xhtml
package com.mycompany.prj1.jsf; import com.mycompany.prj1.beans.Project; import com.mycompany.prj1.ejb.ProjectDaoBean; import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; import javax.ejb.EJB; import javax.enterprise.context.RequestScoped; import javax.inject.Named; import org.apache.log4j.Logger; /** * * @author thierry */ @Named @RequestScoped public class ProjectControllerV3 { private static final Logger logger = Logger.getLogger(ProjectControllerV3.class); private Project project; private List projects; public ProjectControllerV3() { logger.info("In ProjectController"); } @PostConstruct public void postConstruct() { logger.info("In ProjectControllerV3 postConstruct"); project = new Project(); getAllProjects(); } @EJB private ProjectDaoBean projectDaoBean; public String saveProject() { String returnValue = "project"; logger.info("Sauvegarde du projet : [" + project.getName() + "]"); try { projectDaoBean.insertProject(project); } catch (Exception e) { e.printStackTrace(); returnValue = "error_saving"; return returnValue; } project.setName(""); returnValue = getAllProjects(); logger.info("returnValue : " + returnValue); return returnValue; } public String getAllProjects() { String returnValue = "project"; try { projects = projectDaoBean.getAllProjects(); if (projects != null) { logger.info("Taille de projects=" + projects.size()); } else { logger.info("Taille de projects=0"); } } catch (Exception e) { e.printStackTrace(); returnValue = "error_saving"; } return returnValue; } public List getProjects() { return projects; } public void setProjects(List projects) { this.projects = projects; } public Project getProject() { return project; } public void setProject(Project project) { this.project = project; projects = new ArrayList<>(); } }
La page JSF permettant d'insérer un nouveau projet et de voir la liste des projets (project.xhtml)
<?xml version='1.0' encoding='UTF-8' ?> <!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"> <h:head> <title>Enter Project Data</title> </h:head> <h:body> <h:outputStylesheet library="css" name="styles.css"/> <h:form id="projectForm"> <h:messages/> <h:panelGrid columns="2" columnClasses="rightAlign,leftAlign"> <h:outputLabel for="name" value="Nom du projet:"> </h:outputLabel> <h:inputText id="name" label="Nom du projet" value="#{projectControllerV3.project.name}" > </h:inputText> <h:panelGroup></h:panelGroup> <h:commandButton value="Ajouter" action="#{projectControllerV3.saveProject}"/> </h:panelGrid> </h:form> <h:panelGrid columns="2"> <h:dataTable var="x" value="#{projectControllerV3.projects}" summary="Liste des projets" border="1"> <f:facet name="header"><h:outputText value="Liste des projets"></h:outputText></f:facet> <h:column> <f:facet name="keader"><h:outputText value="Id"></h:outputText></f:facet> <h:outputText value="#{x.projectId}"></h:outputText> </h:column> <h:column> <f:facet name="keader"><h:outputText value="Nom"></h:outputText></f:facet> <h:outputText value="#{x.name}"></h:outputText> </h:column> </h:dataTable> </h:panelGrid> </h:body> </html>