Développement d'applications Web & Distribuées avec JavaEE
Utilisation des technologies de la plateforme JavaEE : Servlets, JSP, JTSL, EJB, JPA
- Introduction
- Créer un projet JakartaEE avec IntelliJ
- Base des applications JavaEE
- Applications JavaEE Avancées
Introduction
Prérequis
Installation de Jetbrains IntelliJ IDEA et Jetbrains Datagrip
Licence
IntelliJ et Datagrip sont des outils professionnels sous licence. Heureusement, Jetbrains permet aux étudiants de bénéficier de licences gratuites pour tous ses outils. Elles sont accessible via le Github Student Pack. Une fois le student pack en main, rdv ici pour créer un compte Jetbrains en utilisant son compte Github.
Avec JetBrain Toolbox
Jetbrains Toolbox est un petit utilitaire très pratique qui permet d'installer et mettre à jours les logiciels JetBrains en un clic.
Installer Jetbrains Toolbox
Téléchargez et installez Jetbrains Toolbox
Installer IntelliJ
Exécutez JetBrains Toolbox puis faites un clic droit sur son icône dans la barre d'état système de Windows. Trouvez ensuite IntelliJ et Datagrip dans la liste des applications proposée. Cliquez sur "Install" et attendez la fin du téléchargement.
Sans JetBrains ToolBox
Téléchargez IntelliJ via ce lien et datagrip via ce lien, puis exécutez l'installateur. Suivez ensuite les instructions d'installation.
Installer une base de donnée MySQL
Laragon est un outil qui package plusieurs outils pour le développement web. Nous allons l'utiliser la base de donnée MySQL. Téléchargez Laragon via ce lien,puis exécutez l'installateur. Suivez ensuite les instructions d'installation.
Configurer Laragon
Lancez Laragon puis faites clic droit sur le fond > MySQL > Change root password. Mettez un mot de passe et conservez le bien il servira à se connecter à la base de donnée. Lancez ensuite le serveur en faisant clic droit sur le fond > MySQL > Start MySQL
Créer une base de donnée
Vous pouvez créer une base de donnée pour chacun de vos projet en faisant : clic droit sur le fond > MySQL > Create Database, puis donnez lui un nom.
Installer une base de donnée Oracle
- Télécharger Oracle
- Extraire
- Exécuter et suivre le programme d'installation -> Attention bien se rappeller le mot de passe admin qu'on a miss
- Ouvrir SQL plus
- Se connecter le compte system et le mot de passe que vous avez setup dans l'installation
- Exécuter la commande suivante :
Exec DBMS_XDB.SETHTTPPORT(3010);
- Créer votre compte utilisateur :
CREATE USER nom_utilisateur IDENTIFIED BY mot_de_passe;
GRANT DBA TO nom_utilisteur;
Se connecter à une base de donnée avec Datagrip
Lancer Datagrip puis cliquer sur le bouton "+" :
Et choisissez "Datasource > MariaDB" :
Au bas du formulaire cliquez sur "Download missing driver file". Remplissez ensuite le formulaire comme suit :
- Name : nom de votre projet
- Host : laissez
localhost
- port : laissez
3306
- User :
root
- Password : Le mot de passe que vous avez configuré sur la base de donnée MySQL de Laragon
- Database : Le nom de la base de donnée que vous avez créé sur Laragon
Faites ensuite "Test Connection" afin de voir si tout est bien configuré, puis fait "Apply". Votre base de donnée est apparue dans la liste des bases de donnée à gauche de l'interface. Pour ouvrir une session SQL faites Clic droit sur le nom de votre base > New > Query Console :
Vous avez donc une console dans laquelle vous pouvez écrire des scripts SQL. CTRL + ENTER
pour exécuter votre script.
Conteneur de Servlets Apache Tomcat
Apache Tomcat est un conteneur de servlets. Il sert à exécuter des composants de serveur HTTP écrits en Java, les servlet, sur lesquels nous reviendront plus tard.
- Télécharger Apache Tomcat 10 via ce lien (64-bits windows ZIP)
- Extraire l'archive
- Stocker dans son dossier d'outils (ex : "C://tools/)
Créer un projet JakartaEE avec IntelliJ
Créer le projet
Créez un nouveau projet en utilisant le template "Java Entreprise" :
Explications :
- Project Template : type de projet, ici une application web "à pages" basée sur Servlets et JSP
- Build System : outils de build utilisé, nous allons utiliser Maven
- Test Framework : bibliothèque de tests
- Group : GroupId de votre artéfact, par convention, votre domaine à l'envers
- Application server : serveur d'application sur lequel votre application sera déployée
Ajouter un serveur d'application
Pour ajouter un nouveau serveur d'application sur votre IntelliJ, cliquez sur "New" et séléctionner le type de server d'application que vous voulez utiliser. Dans ce tutoriel, nous allons utiliser Tomcat dans un premier temps, puis Wildfly dans la partie avancée.
Ensuite renseignez simplement le répertoire de votre serveur d'application :
Faites "suivant".
Dépendances JakartaEE
Sur l'écran suivante, dans les dépendances, nous devons choisir quelles spécifications de la plateforme JakartaEE notre application va utiliser. Il faut aussi se référer pour la version à prendre, à la version de notre serveur d'application :
Par exemple, pour les versions 10.0.x (ce qui est mon cas à l'heure où j'écris ces lignes), il faut prendre la spécification de servlet en version 5.0.
Pour accèder à la dépendance de la Servlet API 5.0, vous devez positionner la version de JakartaEE à 9 :
Ces dépendances ne vont télécharger ques les interfaces de la spécification dans votre projet (pour pouvoir les utiliser dans le code), les implémentations seront fournies par votre serveur d'application.
Tester le projet
IntelliJ vous a généré un projet Maven avec les dépendances JakartaEE demandées ainsi, qu'une configuration de déploiement pour votre serveur d'application. Vous pouvez simplement lancer votre projet en cliquant sur le boutun "Run" et la page racine de votre application devrait s'ouvrir :
Base des applications JavaEE
Développer des applications Web simple avce Servlets, JSP et JDBC
Java EE
Qu'est ce que JavaEE ?
Java EE ou J2EE ou Jakarta EE est un ensemble de spécifications de différentes API orientée pour le développement d'applications professionnelles et d'entreprise. Chacune de ces spécifications d'API répond à un besoin courant de ce type d'application, comme par exemple, accèder à une base de donnée, répondre à des requêtes HTTP, etc ...
Les Serveurs d'applications
Les serveurs d'application sont les environnement d'exécutions pour les applications JavaEE. Ils implémentes certaines ou toutes les spécifications d'API. Quelques exemples :
- Référence : Oracle Glassfish
- Commerciaux :
- WebSphere (IBM)
- Weblogic (Oracle)
- Open source :
- Tomcat (TomEE)
- JBoss/Wildfly
Ainsi, les applications JavaEE ne sont pas exécutées comme les applications JavaSE via une méthode main
. Elles doivent être packagées puis déployées sur un serveur d'application. Heureusement, notre IDE rend cette tâche triviale.
Servlet API
Les servlets
La Servlet API est une brique fondamentale de la spécification JavaEE. Elle permet de développer des composants gérant des requêtes HTTP. L'instanciation des servlets n'est pas gérées par le programmeur, mais par ce que l'ont appelle le conteneur de servlets. Il s'agit d'une partie du serveur d'application qui orchestre le cycle de vie des servlets ainsi que les threads nécessaires à la gestion des requêtes.
Pour créer une servlet, il suffit de créer une classe qui étend la classe HttpServlet
et de lui affecter l'annotation @WebServlet
avec en paramètre la route qui est être gérée par la servlet.
@WebServlet("/helloworld")
public class HelloWorldServlet extends HttpServlet {
}
Dans une servlet vous pouvez redéfinir certaines méthodes, notamment doGet
et doPost
.
Ces méthodes sont appelées par le conteneur lorsqu'une requête HTTP est faite sur la route de la servlet.
doGet
gère les requête avec la méthode HTTP GET et doPost
gère les requêtes avec la méthode HTTP POST.
@WebServlet("/helloworld")
public class HelloWorldServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
Les paramètres de cetes méthodes, représentent respectivement la requête HTTP et la réponse HTTP.
Objet requête
Paramètres
L'objet requête permet de récupérer des paramètres soit passés dans l'URL de la requête dans le cas d'une requête HTTP GET, ou paramètres d'un formulaire (passés dans le Body de la requête au format FormData) :
String monParam = request.getParameter("nomDuParam");
Session
L'objet requête permet également de récupérer la session du client. Dans la session, il est possible de stocker des variables et de les récupérer. La session est propre à un client.
// Récupérer la session
HttpSession session = request.getSession();
// Ecrire une variable dans la session
session.setAttribute("profil", profil);
// Récupérer une variable de session
ProfilUtilisateur profil = (ProfilUtilisateur) session.getAttribute("profil");
Objet réponse
L'objet réponse permet d'écrire du texte dans le Body de la réponse HTTP :
response.getOutputStream().println("Hello world");
On peut également faire une redirection vers une autre route de l'application :
response.sendRedirect("/helloworld");
Première Servlet
Vous pouvez désormais créer une servlet "Hello world" :
@WebServlet("/helloworld")
public class HelloWorldServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getOutputStream().println("Hello world");
}
}
Ensuite lancez le projet, une page web dans votre navigateur devrait apparaître. Ajoutez /helloworld
à la fin de l'URL et vous devriez voir le message hello world :
Récupération d'un paramètre GET
Pour récupérer un paramètre GET, on utilise la méthode getParameter
de la requête :
@WebServlet("/helloworld")
public class HelloWorldServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name = request.getParameter("name");
response.getOutputStream().println("Hello " + name + "!");
}
}
Relancez votre projet et ajoutez un paramètre GET dans l'URL !
Soumission d'un formulaire
Nous allons maintenant voir comment gérer une soumission de formulaire avec une servlet.
Dans le dossier webapp
, vous devriez trouver une page index.jsp
. Considérer là comme une simple page HTML, nous verrons les JSP dans la partie d'après. Si elle n'est pas présente créez simplement une page index.html
. Ecrivez maintenant un formulaire POST simple :
<form method="post" action="/helloworld">
<input type="text" name="monParam"/>
<button type="submit">Envoyer</button>
</form>
Dans ce formulaire, l'action est mappée sur l'URL de notre servlet. Il faut donc que notre servlet implémente la méthode doPost
pour gérer les requêtes POST. On peut ensuite récupérer le paramètre POST de la même façon que les paramètres GET :
@WebServlet("/helloworld")
public class HelloWorldServlet extends HttpServlet {
...
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name = request.getParameter("monParam");
response.getOutputStream().println("Hello " + name + "!");
}
}
JSP/JSTL
Les Java Server Pages
Ecrire une interface HTML dans une string Java n'est pas très pratique. C'est pourquoi il est possible d'utiliser pour ce faire les JSP. Les Java Server Pages sont des templates de vues qui sont rendus coté serveur. Lorsque vous renvoyez une JSP depuis une Servlet, elle va être interprétée et le code HTML résultant sera placé dans le Body de la réponse HTTP. Ces vues sont templatables par du code Java mais c'est une mauvaise pratique car du code Java exprimant de la logique ne devrait pas résider dans une vue. C'est pourquoi il est recommandé d'utilise JSTL, la JSP Standard Tag Library.
Pour créer une JSP qui sera renvoyées par une servlet, créer un fichier .jsp
à dans le dossier src/main/webapp/WEB-INF
. Les fichiers de styles et de scripts sont à mettre directement dans le dossier src/main/webapp
et sont accessibles à partir de l'URL racine du projet.
Il est possible de passer des objets Java à une JSP depuis une servlet :
request.setAttribute("nom", monObjet);
Pour renvoyer une JSP depuis une servlet, utilisez le code suivant :
this.getServletContext().getRequestDispatcher("/WEB-INF/maJsp.jsp").forward(request, response);
JSP Standard Tag Library
Installation
Pour installer JSTL, ajouter la dépendance au pom.xml
de votre projet :
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>jakarta.servlet.jsp.jstl</artifactId>
<version>2.0.0</version>
</dependency>
Pour importer la JSTL dans une JSP utilisez les balises suivantes :
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
Balise de la JSTL
Afficher une variable
La balise <c:out>
permet d'afficher une variable passée par la Servlet :
<c:out value="${ variable }">Valeur par défaut si la variable est null</c:out>
Cette balise évalue en fait une expression écrite dans un langage appelé EL pour Expression Language. C'est un langage simple qui permet de faire des petites opérations sur les variables. Il permet d'accèder par leur nom aux variables passés à la JSP par la servlet.
Il permet également d'accèder à un champs d'un objet Java si celui ci possède un Getter correctement nommé en utilisant l'opérateur .
. EL supporte également les opérateurs arithmétiques et logique ainsi que les expressions ternaires. Le mot clé empty
permet de vérifier si une valeur est nulle.
Itération sur une liste
La balise <c:forEach>
permet d'itérer sur les éléments d'une liste :
<c:forEach items="${ maListe }" var="monElement" varStatus="status">
<p>N°<c:out value="${ status.count }" /> : <c:out value="${ titre }" /> !</p>
</c:forEach>
Ici la variable monElement
dansl'attribut var
de la boucle correspond à l'élément courant de la liste. Quant à la variable status
(optionnelle) définie dans l'attribut varStatus
elle permet d'obtenir des informations sur l'élément courant de la boucle et possède des propriétés comme :
-
index
: l'indice de l'élément -
first
: vrai si l'élément est le premier -
last
: vrai si l'élément est le dernier
Rendu conditionnel
La balise <c:if>
permet de faire du rendu conditionnel :
<c:if test="${ variable == '1' }">
C'est vrai !
</c:if>
La contenu de la balise n'est rendu que si l'expression EL dans l'attribut test
est évaluée à true
.
Rendu conditionnel à choix multiple
La balise <c:choose>
permet de faire du rendu conditionnel à choix multiples :
<c:choose>
<c:when test="${ variable = '1' }">C'est égal à 1</c:when>
<c:when test="${ variable = '2' }">C'est égal à 2</c:when>
<c:when test="${ variable = '3' }">C'est égal à 3</c:when>
<c:otherwise></c:otherwise>
</c:choose>
JDBC
JDBC pour Java DataBase Connectivity est une spécification d'API pour accèder à une base de donnée relationnelle depuis une application Java. Pour se connecter à une base de donnée, il faut un Driver, qui implémente JDBC pour un moteur de base de donnée relationnelle donné. Par exemple il en existe un pour Oracle, un pour MySQL, pour PostgreSQL, etc ...
Installation du Driver
Pour installer le Driver JDBC pour MySQL, ajoutez la dépendance suivante dans votre pom.xml
:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
Pour Oracle utilisez cette dépendance :
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>21.1.0.0</version>
</dependency>
N'oubliez pas refresh vos dépendances Maven après avoir modifié votre pom.xml
!
Connexion à la base de donnée
Pour se connecter à une base de donnée utilisez la méthode static DriverManager.getConnexion()
en passant en paramètre la chaine de caractère de connexion ainsi que les identifiants.
Pour MySQL :
Class.forName("com.mysql.cj.jdbc.Driver")
Connection connexion = DriverManager.getConnection("jdbc:mysql://localhost:3306/nomDeLaBase","root", "mot de passe");
Si MySQL vous lance une erreur de TimeZone, ajoutez ceci à la fin de l'URL JDBC : ?useLegacyDatetimeCode=false?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
.
Pour Oracle :
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection connexion = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe","root", "mot de passe");
Il est recommandé de faire un singleton de l'instance de la connexion à la base de donnée.
Interactions avec la base de donnée
Requêtes
L'objet Statement
permet d'exécuter des requêtes SQL :
Statement statement = connexion.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM Utilisateurs");
Le ResultSet
est un itérateur sur les enregistrements contenues dans la réponse à la requête. On peut l'exploiter en itérant dessus grâce à la méthode next()
:
while (rs.next()) {
Utilisateur utilisateur = new Utilisateur();
utilisateur.setPrenom(rs.getString("PRENOM"));
utilisateur.setNom(rs.getString("NOM"));
listeUtilisateur.add(utilisateur);
}
On peut en ensuite récupérer le contenu de l'enregistrement courant avec des méthodes comme getString()
, getInteger()
, etc ... en leur le passant le nom de la colonne dans la base.
Les classes Statement
et ResultSet
doivent toutes les deux êtres fermées après leur utilisation car il s'agit de ressources non managées.
Pour une syntaxe concise on peut utiliser le "Try With Resource" :
try (Statement statement = this.connection.createStatement();
ResultSet result = statement.executeQuery("SELECT * FROM Utilisateurs");) {
while (result.next()) {
Utilisateur utilisateur = new Utilisateur();
utilisateur.setPrenom(rs.getString("PRENOM"));
utilisateur.setNom(rs.getString("NOM"));
listeUtilisateur.add(utilisateur);
}
} catch (SQLException e) {
System.err.println("Erreur à l'exécution de la requête SQL");
}
Requêtes DML
Pour modifier des données dans la base avec des commandes INSERT
,UPDATE
ou DELETE
on utilise la méthode executeUpdate()
de la classe Statement
:
int modifiedRows = statement.executeUpdate("DELETE FROM Utilisateurs WHERE NOM = 'Shepard'");
executeUpdate()
retourne le nombre de lignes modifiées par la requête.
SQL Dynamique
Il est possible de faire des requêtes préparées en utilisant la classe PreparedStatement
:
PreparedStatement preparedStatement = connexion.prepareStatement("DELETE FROM Utilisateurs WHERE NOM = ?");
On peut ensuite affecter des paramètres :
preparedStatement.setString(1,"Shepard");
Enfin, on exécute le PreparedStatement
en utilise soit executeUpdate()
soit executeQuery()
selon qu'il s'agisse d'une commande DML ou d'une requête.
Filtres
Les filtres permettent d'intercepter des requêtes HTTP avant qu'elles arrives à leur Servlet afin d'exécuter de la logique. Cela est notamment utile pour restreindre l'accès à certaines routes à des utilisateurs non authentifiés ou ne possèdant pas le bon rôle.
Créer un filtre
Pour créer un filter, il faut créer une classe qui implémente l'interface Filter
et annotées avec @WebFilter
. En paramètre de l'annotation, il faut passer le paramètre urlPatterns, pour préciser les routes qui seront filtrée par ce filtre.
@WebFilter(urlPatterns = "/admin/*")
public class AdminFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
}
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
}
Par exemple ce filtre va intercepter les requêtes sur toutes les routes qui commencent par "/admin/".
Actions dans un filtre
Dans un filtre on peu récupérer la requête et la réponse HTTP par un simple cast :
var request = (HttpServletRequest) req;
var response = (HttpServletResponse) resp;
On peut ensuite accèder à la session pour vérfier des informations ou faire une redirection. Pour autoriser la poursuite de la requête normalement utilisez :
chain.doFilter();
Et pour retourner un code HTTP particulier (401 Unauthorized ou 403 Forbidden) et arrêter là la requête :
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
Applications JavaEE Avancées
Développement d'applications plus orientées entreprise avec l'utilisation des Entreprise Java Beans, de la Java Persistence API et en utilisant le serveur d'application JBoss WildFly
Serveur d'application Wildfly
Jusqu'ici, nous n'avions besoin que d'un conteneur de servlet pour exécuter nos applications, c'est pourquoi Tomcat nous suffisait. Cependant, pour utiliser les spécifications JakartaEE que nous allons étudier dans cette partie, il faut disposer d'un serveur d'application JakartaEE complet. Nous allons ici utiliser Wildfly (évolution de JBoss).
Installation
Commencez donc par télécharger Wildfly 23 sur ce lien. Décompressez ensuite le ZIP téléchargé et placez le à l'emplacement où vous rangez vos outils.
Créer un Projet avec WildFly
Pour créer un nouveau projet sur votre nouveau serveur Wildfly, utilisez le menu "New..." :
Indiquez ensuite le dossier d'installation de votre serveur :
Vous pouvez désormais le séléctionner dans le menu déroulant :
Enterprise Java Beans
Les EJB pour Enterprise Java Beans sont un standard de la pateforme JakartaEE pour créer des composants serveur. Cette architecture propose un conteneur pour créer des composants Java distribués hébergés sur un serveur d'application.
Il existe plusieurs types d'EJB.
Entity
Les EJB Entity permettent de représenter les données manipulées par l'application. On en parlera plus en détail dans le chapitre suivant sur JPA.
Message
Les EJB Message Driven servent à accomplir des tâches de manière asynchone en utilisant des mécanismes de file de message.
Session
Les EJB session permettent de proposer des services avec ou sans état. Il pour être injectés par le conteneur EJB à un service local (comme une servlet par exemple), mais il peut aussi être appelé par un client distant, via le protocole RMI (Remote Method Invocation).
EJB Session
Interface
Pour définir un EJB session, il faut définir une interface qui définir les méthodes accessibles par le code client. Il faut y ajouter une interface pour signaler au conteneur EJB qu'il s'agit d'une interface d'EJB ; @Remote
pour un accès par un client RMI distant, et @Local
pour un accès par un composant local déployé sur le serveur également.
@Remote
public interface HelloWorldRemote {
String helloWorld(String name);
}
@Local
public interface HelloWorldLocal {
String helloWorld(String name);
}
Implémentation
On peut ensuite créer une classe EJB qui implémente cette interface. On doit utiliser une annotation pour signaler au conteneur qu'il s'agit d'une classe d'implémentation EJB. On utilise @Stateless
pour un composant qui sera instancié à chaque demande. Le paramètre d'annotation mappedName
permet de préciser un nom dans l'annuaire JNDI qui va référencer tous les EJB sur serveur.
@Stateless(mappedName="HelloWorld")
public class HelloWorld implements HelloWorldRemote {
public String helloWorld(String name) {
return "Hello, " + name;
}
}
Pour avoir toujours la même instance du composant fournie par le conteneur pour une session de client donnée, on peut utiliser à la place @Stateful
.
@Stateful(mappedName="HelloWorld")
public class HelloWorld implements HelloWorldRemote {
public String helloWorld(String name) {
return "Hello, " + name;
}
}
Utilisation
Pour demander au conteneur d'EJB d'injecter un EJB dans un autre composant, comme un autre EJB, ou une Servlet par exemple, utilisez l'annotation @EJB
sur un attribut du type d'une interface EJB locale :
@EJB
private HelloWorldLocal helloWorld;
Client RMI
On peut accèder aux EJB remote via la protocole RMI depuis un client distant Java. Après avoir lancé le serveur Wildfly qui possède des EJB Remote, on peut regarder ensuite les logs du serveur Wildfly dans l'interface d'IntelliJ IDEA, et y voir une ligne affirmant que l'EJB a bien été detecté et ajouté à l'annuaire JNDI. Copiez l'URL qui commence par EJB et gardez là dans un bloc note, elle sera utile plus tard.
Configuration du projet Client RMI
Pour le client EJB, créez un nouveau projet IntelliJ, cette fois en utilisant le template Maven et en utilisant l'archétype Quickstart. Une fois votre projet généré, rajoutez la dépendance suivante dans votre fichier pom.xml
:
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-ejb-client-bom</artifactId>
<version>21.0.0.Final</version>
<type>pom</type>
</dependency>
Mettez à jours vos dépendances Maven, puis ajoutez une Run Config d'application Java normale.
Récupérer les EJB
Dans un premier temps, il faut copier le package qui contient les interfaces dans le projet client (les classes doivent avoir le même nom et le même package dans le projet client). Retirez dans le projet client les annotations @Remote
des interfaces.
Dans la méthode main
ajoutez d'abord le code suivant, afin de configurer la connexion EJB :
final Hashtable<String, String> jndiProperties = new Hashtable<>();
jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
if(Boolean.getBoolean("http")) {
jndiProperties.put(Context.PROVIDER_URL,"http://localhost:8080/wildfly-services");
} else {
jndiProperties.put(Context.PROVIDER_URL,"remote+http://localhost:8080");
}
final Context context = new InitialContext(jndiProperties);
Ensuite pour récupérer votre EJB, ajouter ce code, en modifiant la string passée à la méthode lookup()
par l'URL ejb que nous avons noté tout à l'heure :
final GestionContactRemote ejb = (GestionContactRemote) context.lookup("ejb:/tuto-jakarta-1.0-SNAPSHOT/HelloWorld!fr.arsenelapostolet.tuto_jakarta.HelloWorldRemote");
Vous pouvez enfin tester votre EJB :
System.out.println(ejb.helloWorld("John Shepard"));
Si tout s'est bien passé, vous devriez observer les logs du client EJB dans votre console ainsi que votre afichage :
Persistance avec JPA
JPA pour Java Persistance API est la spécification ORM de la plateforme JavaEE. Un ORM, pour Object Relationnal Mapper, est un composant logiciel chargé de faire le lien entre le modèle objet et le modèle relationnel et permet ainsi de persister des classes Java dans une système de gestion de base de donnée relationnel sans écrire tout le code JDBC directement. Le moteur vas ainsi établir et exécuter les opérations JDBC nécéssaires. JPA étant une spécification, on a besoin de choisir une implémentation pour l'utiliser. Nous allons utiliser la plus connue, Hibernate, mais il en existe d'autres, comme EclipseLink, TopLink, etc ...
Dépendances
Pour utiliser JPA avec Hibernate, il faut rajouter ces dépendances à notre projet :
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.22.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.22.Final</version>
</dependency>
Data Source
Pour pouvoir accèder à une base donnée avec JPA, il faut configurer une Datasource sur notre serveur d'application. Pour cela, nous allons devoir accèder à l'interface d'administration de notre serveur Wilfly.
Créer un utilisateur d'Administration sur Wildfly
Rendez vous dans WILDFLY_HOME/bin
et exécutez :
./add-user.bat
Suivez ensuite les instruction pour créér un utilisateur. Lorsqu'on l'on vous demande le type d'utilisateur choisissez Utilisateur d'administration.
Vous pouvez ensuite accèder au panneau d'administration Wildfly via ce lien (Le serveur Wildfly doit être démarré). Les identifiants pour se connecter sont ceux donnés à la création de l'utilisateur.
Configurer le Driver
Commencez par télécharger le Driver JDBC MySQL sur ce lien, décompressez le et récupérez le fichier JAR.
Ouvrez maintenant l'interface d'administration à http://localhost:9990
et rendez vous dans l'onglet Deployement. Ajoutez ensuite un nouveau déploiement en cliquant sur le bouton + puis Upload Deployment:
Dans la fenêtre qui s'ouvre, uploadez votre JAR JDBC et validez. Si tout ce passe bien il devrait être répertorié ici dans les déploiements :
Ainsi qu'ici dans l'onglet Configuration sous Configuration -> Subsystems -> Datasources & Drivers -> JDBC Drivers.
Configurez la source de donnée
Sur l'interface d'administration de Wildfly, allez à Configuration -> Subsystems -> Datasources & Drivers -> Datasources et utilisez le boutons + et New Datasource*. Suivez ensuite les instructions du wizard en renseignant les informations.
Dans attributes, vous pouvez choisir un nom pour votre Datasource ainsi que son adresse dans l'annuaire JNDI.
Choisir le driver que nous avons déployé :
Pour les identifiants de votre base de donnée voici comment les renseigner :
- URL :
jdbc:mysql://localhost:3306/<nom de la base>
- Username : votre login mysql
- Password : votre mot de passe mysql
Vous pouvez utiliser le bouton Test Connection pour vérifier que les information son correctes.
Il m'est déjà arrivé que le bouton Test Connection ne marche pas mais que la datasource soit tout de même bien configurée et fonctionnelle
Si au moment du déploiment de votre artéfact sur le serveur MySQL vous lance une erreur de TimeZone, ajoutez ceci à la fin de l'URL JDBC :
?useLegacyDatetimeCode=false?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
. Vous pouvez modifier votre datasource existante en allant dans Configuration -> Subsystems -> Datasources & Drivers -> Datasources Et en faisant "View" sur votre datasource, puis onglet "Connection" puis "Edit"
Utiliser le nouvelle source de donnée
Dans votre projet, sous le dossier resources
, créez un dossier, META-INF
et dans ce dernier un fichier, persistence.xml
, et entrer ce contenu pour référencer votre datasource dans votre projet :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd"
version="2.2">
<persistence-unit name="default">
<jta-data-source>java:/MySqlDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MariaDBDialect"/>
</properties>
</persistence-unit>
</persistence>
Voilà, votre datasource est configurée, nous allons pouvoir maintenant étudier plus en détail les outils proposés par JPA.
EJB Entity
Les EJB Entity sont des EJB particuliers qui servent à représenter des données. Pour créer un EJB entité, il faut créer une classe annotée avec l'annotation @Entity
. Une classe entité doit avoir un attribut annoté avec @Id
qui correspondra à la clé primaire dans la table de la base de donnée. Chaque propriété de l'entité sera mappée à la colonne de la base de donnée.
Les autres champs de la classe entités sont persistés automatiquement. Pour qu'un champs ne soit pas persisté, il faut l'annoter avec @Transient
.
Exemple d'entité :
@Entity
public class Todo {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String title;
private String description;
... constructeur & getters & setters ...
}
Ici l'annotation @GeneratedValue(strategy= GenerationType.AUTO)
permet de générer automatiquement l'id avec un auto-incrément.
On peut aussi définir des contraintes sur les colonnes en rajoutant l'annotation @Column
sur le champs et en lui passant des paramètres, par exemple :
@Column(unique = true, length = 32)
Cela permet de mettre la contraint "unique" sur le champs et d'imposer une longueur maximum de 32 caractère.
Entity Manager
L'entity manager est une interface qui permet d'intéragir avec les entités :
- Les persister
- Les supprimer
- Faire des requêtes
Pour l'obtenir dans une classe, demandez le avec l'annotation @PersistenceContext
:
public class TodoRepository {
@PersistenceContext
private EntityManager entityManager;
}
L'entity manager vas vous permettre de faire toutes les opérations nécessaires sur les Entity.
Persister une Entity
Utilisez la méthode persist()
:
entityManager.persist(new Todo("test title", "test content"));
Supprimer une Entity
Utilisez la méthode remove()
:
entityManager.remove(myEntity);
Récupérer une Entity
Pour récupérer une entité à partir de son Id :
Todo todo = entityManager.find(Todo.class, todoId);
Créer une Requête JPQL
JPQL pour Java Persistence Query Language est un langage de requête orienté objet qui permet de créer des requêtes sur des entités. Par exemple :
List<Todo> todos = entityManager.createQuery("SELECT t FROM Todo t", Todo.class).getResultList();
Ou alors une requête paramètrée :
String title = "test title";
List<Todo> todos = entityManager.createQuery("SELECT t FROM Todo t WHERE t.title = :title", Todo.class)
.setParameter("title", title)
.getResultList();
Relations
Les relations permettent de faire des liens entre les entités. Il existe quatres types de relations, définies par des annotations :
-
@ManyToOne
: plusieurs instances de cette classe entité sont en relation avec une unique instance d'une autre (ex: Pages d'un livre - plusieurs pages sont reliées à un unique livre) -
OneToMany
: une unique instance de cette classe entité est en relations avec plusieurs instances d'une autre (ex: Livre qui contient des pages - un unique livre est relié à plusieurs pages) -
@ManyToMany
: plusieurs instance de cette classe entité sont en relations avec plusieurs isntance d'une autre (ex: Classes et Professeurs - Les professeurs ont plusieurs classes et les classes ont plusieurs professeurs. -
@OneToOne
: une unique instance de cette classe entités est reliée à une unique instance d'une autre (ex: Dircteur et Ecole - une directeur dirige une seule école et une école est dirrigée par un seul directeur).
Exemple :
@Entity
public class Todo {
... autres propriétés ...
@ManyToOne
private ApplicationUser owner;
... getters & setters ....
}
@Entity
public class ApplicationUser {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private String id;
private String name;
@OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
private Set<Todo> todos;
... getters & setters ...
}
Les annotations de relation possèdes plusieurs paramètres utiles à connaitres :
-
fetch
:-
FetchType.EAGER
: charge les données de la relation directement (défaut pour@ManyToOne
et@OneToOne
) -
FtechType.LAZY
: charge les données de la relation que quand le getter est appelé (défaut pour@OneToMany
et@ManyToMany
)
-
-
orphanRemoval
:true
oufalse
, détermine dans un@OneToMany
si les enfant doivent être supprimés quand le parent est supprimé → un enfant ne peut exister sans parent. -
cascade
: permet de dire quelles opérations de persistance sont propagées du parent vers l'enfant
WebServices JAX-RS
g