Introduction à JPA, application au chargement de données depuis une base MySQL

Thierry Leriche-Dessirier

Ce miniarticle montre (par l'exemple) comment charger des données depuis une base MySQL, à l'aide de JPA (Java Persistence API), en quelques minutes. Commentez ♪

- Titre : Introduction à JPA, application au chargement de données depuis une base MySQL

- Auteur : Thierry Leriche-Dessirier

- Parution : 28 mars 2012

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2011 Thierry Leriche-Dessirier. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- I-A. À propos

- I-B. Avant de commencer

- II. Découverte du projet d'exemple

- II-A. Télécharger, installer et importer le projet d'exemple

- II-B. Ce que fait déjà le projet

- II-C. Avant de commencer

- III. Action

- III-A. Ajout des dépendances

- III-B. Compléter le modèle

- III-C. persistence.xml

- III-D. Créer un DAO utilisant JPA

- III-E. NamedQuery

- IV. Conclusion

- V. Remerciements

- VI. Annexes

- VI-A. Annotations

- VI-B. Liens

- VI-C. Les fichiers importants en entier

I - Introduction

Dans ce document, nous allons voir comment charger des données depuis une base MySQL à l'aide de JPA (Java Persistence API) en nous limitant aux fonctionnalités simples.

Le précédent article (de la série « en 5 minutes ») expliquait comment réaliser ce chargement avec JDBC directement, ce qui est moins pratique (et demande plus de travail) qu'avec JPA.

I-A - À propos

Découvrir une technologie n'est pas chose facile. En aborder plusieurs d'un coup l'est encore moins. Partant de ce constat, cet article a été écrit pour aller à l'essentiel. Les points importants sont présentés dans le corps de l'article et les éléments secondaires sont expliqués en annexes.

La techno JPA (Java Persistence API) est relativement complexe. Dans cet article, nous n'abordons que les points faciles. Ceci n'est donc pas un tutoriel complet, mais une introduction rapide. Pour vraiment appréhender le sujet, il faudrait voir les mécanismes de transaction, les conteneurs EJB, les événements, etc. Plusieurs liens sont proposés en annexe pour aller plus loin dans la découverte de JPA.

I-B - Avant de commencer

Pour écrire ce tutoriel, j'ai utilisé les éléments suivants :

- Java JDK 1.6.0_24-b07 ;

- Eclipse Indigo 3.7 JEE 64b ;

- Maven 3.0.3 ;

- MySQL 5.1.52 (avec EasyPHP 5.3.3.1) ;

- EclipseLink 2.3.2.

Ici, j'utilise EclipseLink, car c'est l'implémentation de référence de JPA. J'aurais aussi pu utiliser Hibernate, qui est plus populaire, sans que ça ne change grand-chose à ce tutoriel.

Ce document reprend une partie des éléments déjà programmés dans le cadre du tutoriel « Charger des données depuis une base MySQL en 5 minutes » dont je vous recommande une lecture rapide.

II - Découverte du projet d'exemple

II-A - Télécharger, installer et importer le projet d'exemple

Pour commencer, je vous propose de télécharger le fichier ZIP « notes5_domain-et-dao-csv-db.zip », contenant un projet Java-Maven d'exemple.

Compilez le projet d'exemple et importez-le dans Eclipse (comme expliqué dans le tutoriel « Importer un projet Maven dans Eclipse en 5 minutes ») ou dans l'IDE de votre choix.

Pour suivre ce tutoriel, vous pouvez vous contenter de lire les codes proposés ci-dessous (codes complets en annexes).

II-B - Ce que fait déjà le projet

Le projet permet de charger une liste d'élèves à partir d'une base MySQL. Cette liste est récupérée depuis un DAO à l'aide de JDBC. Pour cela, le programme utilise principalement deux classes.

Bean Eleve.java

public class Eleve {

private Integer id;
private String nom;
private String prenom;
private Integer annee;
private Sexe sexe;
private Date dateNaissance;
private String adresse;

// + constructeurs
// + getters-setters

DAO DbEleveDao.java (sans la gestion des exceptions)

public class DbEleveDao implements EleveDao {

private final static String URL = "jdbc:mysql://localhost:3306/notes_eleves";
private final static String LOGIN = "dvp";
private final static String PASSWORD = "hello";
private final static String QUERY_FIND_ELEVES = "SELECT * FROM eleve ";
private final static String QUERY_FIND_ELEVES_BY_CLASSE = "SELECT * FROM eleve WHERE classe = ? ";

private Connection getConnexion() throws SQLException {
Connection con = DriverManager.getConnection(URL, LOGIN, PASSWORD);
return con;
}

@Override
public List<Eleve> findEleves() {

List<Eleve> eleves = new ArrayList<Eleve>();

Connection con = getConnexion();
Statement stmt = con.createStatement();

ResultSet rset = stmt.executeQuery(QUERY_FIND_ELEVES);

while (rset.next()) {
Eleve eleve = rsetToEleve(rset);
eleves.add(eleve);
}

stmt.close();
con.close();

return eleves;
}

@Override
public List<Eleve> findElevesByClasse(Integer classe) {
...
}

private Eleve rsetToEleve(ResultSet rset) throws SQLException {
Integer id = rset.getInt("id");
String nom = rset.getString("nom");
String prenom = rset.getString("prenom");
Integer annee = rset.getInt("classe");
Date dateNaissance = rset.getDate("date_naissance");
String adresse = rset.getString("adresse");
String sexeStr = rset.getString("sexe");
Sexe sexe = Sexe.valueOf(sexeStr);

Eleve eleve = new Eleve(id, nom, prenom, annee, sexe, dateNaissance, adresse);
return eleve;
}
}

Les données sont demandées à l'aide d'un simple « launcher ».

DaoLauncher.java

public class DaoLauncher {

public static void main(String[] args) {

EleveDao dbEleveDao = new DbEleveDao();
List<Eleve> dbEleves = dbEleveDao.findEleves();
System.out.println();
System.out.println("Liste des eleves en base avec JDBC");
for (Eleve eleve : dbEleves) {
System.out.println(eleve);
}
}

II-C - Avant de commencer

Pour exécuter ce programme, il faut créer une base dans MySQL. Cette procédure est déjà expliquée dans le tutoriel « Charger des données depuis une base MySQL en 5 minutes ». Pour la résumer, elle revient à lancer les commandes SQL suivantes.

create_database.sql

CREATE DATABASE IF NOT EXISTS notes_eleves;
CREATE USER 'dvp'@'%' IDENTIFIED BY  'hello';
GRANT USAGE ON * . * TO  'dvp'@'%' IDENTIFIED BY  '***'
WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0 ;
GRANT SELECT , INSERT , UPDATE ON  `notes\_eleves` . * TO  'dvp'@'%';

create_table.sql

CREATE TABLE IF NOT EXISTS `eleve` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`nom` varchar(100) DEFAULT NULL,
`prenom` varchar(100) DEFAULT NULL,
`classe` int(10) unsigned NOT NULL,
`date_naissance` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`adresse` varchar(250) DEFAULT NULL,
`sexe` enum('HOMME','FEMME') NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;

insert_data.sql

INSERT INTO `eleve` (`id`, `nom`, `prenom`, `classe`, `date_naissance`, `adresse`, `sexe`) VALUES
(1, 'Durand', 'Marie', 3, '1992-01-02 00:00:00', '15 rue du Lac 75001 Paris', 'FEMME'),
(2, 'Alesi', 'Julie', 3, '1992-01-08 00:00:00', '72 av. Jean Dupont 75003 Paris', 'FEMME'),
(3, 'Martini', 'Carine', 3, '1992-01-17 00:00:00', '2 rue du Moulin 92230 Neullavy', 'FEMME'),
...
(51, 'Laferme', 'Martin', 3, '1992-12-07 00:00:00', '15 rue du Lac 75001 Paris', 'HOMME'),
(52, 'Dupuis', 'Vincent', 3, '1992-12-15 00:00:00', '15 rue du Lac 75001 Paris', 'HOMME'),
(53, 'Lagrange', 'Alexandre', 3, '1992-12-28 00:00:00', '15 rue du Lac 75001 Paris', 'HOMME');

III - Action

III-A - Ajout des dépendances

Durée estimée : 30 secondes.

Nous devons ajouter une dépendance vers EclipseLink à notre projet, faute de quoi on ne pourra pas utiliser JPA. Pour cela, il faut ajouter le bloc suivant dans le fichier de configuration « pom.xml » de Maven.

Dépendance vers EclipseLink

<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.3.2</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.0.0</version>
</dependency>

Il faut aussi ajouter l'URL du repository d'EclipseLink.

Repository d'EclipseLink

<repositories>
<repository>
<id>EclipseLink Repo</id>
<url>http://www.eclipse.org/downloads/download.php?r=1&amp;nf=1&amp;file=/rt/eclipselink/maven.repo</url>
</repository>
</repositories>

Il faut ensuite relancer une installation Maven à l'aide de la commande suivante.

Maven en ligne de commande

mvn clean install eclipse:eclipse

En fonction des éléments déjà présents sur l'ordinateur, la réponse de Maven devrait ressembler à la trace suivante.

Maven en ligne de commande

C:\dvp\notes>mvn clean install eclipse:eclipse

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Notes 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.4.1:clean (default-clean) @ notes ---
[INFO] Deleting C:\dvp\notes\target
[INFO]
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ notes ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO]
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ notes ---
[INFO] Compiling 10 source files to C:\dvp\notes\target\classes
[INFO]
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ notes ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 0 resource
...

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 10:13.105s
[INFO] Finished at: Thu Mar 01 20:47:13 CET 2012

III-B - Compléter le modèle

Durée estimée : 1 minute.

Nous allons maintenant compléter notre modèle à l'aide des annotations utilisées par JPA pour « mapper » nos objets avec la structure de la base de données. Dans cette partie, on ne va s'intéresser qu'à l'objet Eleve. Pour les autres objets, il suffira de faire pareil.

D'abord, on va annoter la classe comme une « Entity » pour indiquer à Java que le bean « Eleve » va être persisté.

Annotation Entity dans Eleve.java

@Entity
public class Eleve {
...
}

Les annotations utilisées sont expliquées un peu plus longuement en annexe.

Ensuite, on va préciser comment fonctionnent les différents attributs, en commençant par l'identifiant.

Annotation de l'id de Eleve.java

@Entity
public class Eleve {

@Id
@GeneratedValue
private Integer id;

...

En quelques mots ; l'annotation « @Id » indique que l'attribut est, sans surprise, un identifiant. L'annotation « @GeneratedValue », quant à elle, indique que la valeur est générée par MySQL.

On enchaine directement avec tous les autres attributs.

Eleve.java

@Entity
public class Eleve {

@Id
@GeneratedValue
private Integer id;

@Basic(optional = false)
private String nom;

@Basic(optional = false)
private String prenom;

@Basic(optional = false)
@Column(name = "classe")
private Integer annee;

@Enumerated(STRING)
@Basic(optional = false)
private Sexe sexe;

@Basic(optional = false)
@Column(name = "date_naissance")
@Temporal(TIMESTAMP)
private Date dateNaissance;

@Basic(optional = false)
private String adresse;

...

L'annotation « @Basic » est facultative. Je m'en sers néanmoins, ici, pour utiliser son paramètre « optional » servant à spécifier si le champ peut être nul. L'annotation « @Column » est intéressante lorsque le nom de l'attribut (dans l'objet) diffère du nom du champ en base, comme c'est le cas pour les attributs « annee » et « dateNaissance ». Enfin, les annotations « @Enumerated » et « @Temporal » indiquent respectivement qu'on travaille avec des enum Java et des dates.

Finalement, et on ne peut pas le deviner, il faut ajouter un constructeur sans argument pour notre bean.

Constructeur simple de Eleve.java

@Entity
public class Eleve {
...

public Eleve() {
super();
}

À ce stade, on en a fini avec le bean Eleve.

III-C - persistence.xml

Durée estimée : 30 secondes.

JPA peut être configuré de différentes manières. Ici, je vais utiliser celle qui me semble la plus simple. Pour cela, il faut créer un fichier « persistence.xml » dans le dossier « META-INF » de l'application (et plus précisément dans « src/main/resources/META-INF/ »). Les valeurs de ce fichier sont simplement reprises à partir des valeurs utilisées pour la connexion à MySQL qu'on avait déjà utilisée dans le DAO « DbEleveDao.java ». Il suffit donc de les recopier.

persistence.xml

<?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">

<!-- Unite de persistence [1] -->
<persistence-unit name="notePU" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

<!-- Entites [5] -->
<class>com.thi.notes.domain.Eleve</class>

<properties>
<!-- Login et password [4] -->
<property name="javax.persistence.jdbc.password" value="hello" />
<property name="javax.persistence.jdbc.user" value="dvp" />

<!-- MySQL [2] -->
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />

<!-- Adresse [3] -->
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/notes_eleves" />

<property name="eclipselink.logging.level" value="INFO" />
</properties>
</persistence-unit>
</persistence>

Dans ce fichier, j'indique que [1] mon « unité de persistance » sera identifiée par le nom « notePU », qu'on réutilisera plus tard dans le DAO. Je précise que [2] j'utilise MySQL, dont je donne [3] l'adresse ainsi que [4] le login et le mot de passe à utiliser. Enfin, [5] je liste les « entités » à prendre en compte.

III-D - Créer un DAO utilisant JPA

Durée estimée : 3 minutes.

Sans transition, on enchaine sur la création du DAO « JpaEleveDao.java ». Pour cela, on s'inspire de « DbEleveDao.java » qui utilisait JDBC. Le nouveau DAO va, lui, utiliser JPA.

JpaEleveDao.java

public class JpaEleveDao implements EleveDao {

@Override
public List<Eleve> findEleves() {
// TODO Auto-generated method stub
return null;
}

@Override
public List<Eleve> findElevesByClasse(Integer classe) {
// TODO Auto-generated method stub
return null;
}

}

Il faut maintenant ajouter un « entity manager » à notre DAO. Ce dernier utilisera l'unité de persistance « notePU » déclarée dans le fichier « persistence.xml ».

Entity manager dans JpaEleveDao.java

public class JpaEleveDao implements EleveDao {

private static final String PERSISTENCE_UNIT_NAME = "notePU";
private EntityManagerFactory emf;
private EntityManager em;

public JpaEleveDao() {
emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
em = emf.createEntityManager();
}

...

Il ne reste plus qu'à coder nos fonctions. On commence par la recherche des tous les élèves.

Recherche de tous les élèves dans JpaEleveDao.java

public class JpaEleveDao implements EleveDao {

...

@Override
public List<Eleve> findEleves() {
String sql = "SELECT e FROM Eleve e ";

Query query = em.createQuery(sql);

List<Eleve> eleves = query.getResultList();

return eleves;
}

...

Les requêtes JPA ressemblent beaucoup au SQL classique.

La recherche des élèves, en précisant le numéro de classe, est à peine plus difficile.

Recherche avec critère dans JpaEleveDao.java

public class JpaEleveDao implements EleveDao {

...

@Override
public List<Eleve> findElevesByClasse(Integer classe) {

String sql = "SELECT e FROM Eleve e WHERE e.annee = :annee ";

Query query = em.createQuery(sql);
query.setParameter("annee", classe);

List<Eleve> eleves = query.getResultList();

return eleves;
}

}

Le code est relativement simple. Toutefois, je vais ajouter un singleton dont la responsabilité sera de me fournir un « EntityManager », ce qui devrait être utile lorsque plusieurs DAO auront besoin de ce type de ressource.

Singleton NoteEntityManager.java

public class NoteEntityManager {

private static NoteEntityManager instance;

private static final String PERSISTENCE_UNIT_NAME = "notePU";
private EntityManagerFactory emf;
private EntityManager em;

private NoteEntityManager() {
emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
em = emf.createEntityManager();
}

public static synchronized NoteEntityManager getInstance() {
if (instance == null) {
instance = new NoteEntityManager();
}

return instance;
}

public EntityManager getEntityManager() {
return em;
}

}

Je modifie finalement mon DAO pour utiliser le singleton.

Utilisation du singleton pour obtenir l'EntityManager dans le DAO

public class JpaEleveDao implements EleveDao {

private EntityManager em;

public JpaEleveDao() {
em = NoteEntityManager.getInstance().getEntityManager();
}

Je reprends « DaoLauncher » pour tester que tout fonctionne.

DaoLauncher.java

public class DaoLauncher {

public static void main(String[] args) {

EleveDao dbEleveDao = new DbEleveDao();
List<Eleve> dbEleves = dbEleveDao.findEleves();
System.out.println();
System.out.println("Liste des eleves en base avec JDBC");
for (Eleve eleve : dbEleves) {
System.out.println(eleve);
}

System.out.println("*************");

EleveDao jpaEleveDao = new JpaEleveDao();
List<Eleve> jpaEleves = jpaEleveDao.findEleves();
System.out.println();
System.out.println("Liste des eleves en base avec JPA");
for (Eleve eleve : jpaEleves) {
System.out.println(eleve);
}

}
}

Ce qui doit donner un résultat ressemblant au suivant. Il faut bien entendu que MySQL soit lancée.

Trace

Liste des eleves en base avec JDBC
Eleve [nom=Durand, prenom=Marie]
Eleve [nom=Alesi, prenom=Julie]
Eleve [nom=Martini, prenom=Carine]
...
Eleve [nom=Laferme, prenom=Martin]
Eleve [nom=Dupuis, prenom=Vincent]
Eleve [nom=Lagrange, prenom=Alexandre]

*************

[EL Info]: 2012-03-01 20:25:14.072--ServerSession(862463384)--EclipseLink, version: Eclipse Persistence Services - 2.3.2.v20111125-r10461
[EL Info]: 2012-03-01 20:25:14.215--ServerSession(862463384)--file:/C:/dvp/notes/target/classes/_notePU login successful

Liste des eleves en base avec JPA
Eleve [nom=Durand, prenom=Marie]
Eleve [nom=Alesi, prenom=Julie]
Eleve [nom=Martini, prenom=Carine]
...
Eleve [nom=Fivolini, prenom=Kevin]
Eleve [nom=Laferme, prenom=Martin]
Eleve [nom=Dupuis, prenom=Vincent]
Eleve [nom=Lagrange, prenom=Alexandre]

III-E - NamedQuery

Durée estimée : 1 minute.

Plutôt que de définir nos requêtes comme des constantes, on peut utiliser des « requêtes nommées ». Pour cela il faut revenir à notre bean « Eleve ».

Eleve.java

@Entity
@NamedQueries({
@NamedQuery(name = "Eleve.findAll", query = "SELECT e FROM Eleve e "),
@NamedQuery(name = "Eleve.findByAnnee", query = "SELECT e FROM Eleve e WHERE e.annee = :annee ")
})
public class Eleve {
...

Il faut évidemment modifier légèrement nos méthodes pour prendre cela en compte.

JpaEleveDao.java

public class JpaEleveDao implements EleveDao {

private EntityManager em;

public JpaEleveDao() {
em = NoteEntityManager.getInstance().getEntityManager();
}

@Override
public List<Eleve> findEleves() {
// String sql = "SELECT e FROM Eleve e ";
// Query query = em.createQuery(sql);

Query query = em.createNamedQuery("Eleve.findAll");

List<Eleve> eleves = query.getResultList();

return eleves;
}

@Override
public List<Eleve> findElevesByClasse(Integer classe) {

// String sql = "SELECT e FROM Eleve e WHERE e.annee = :annee ";
// Query query = em.createQuery(sql);

Query query = em.createNamedQuery("Eleve.findByAnnee");
query.setParameter("annee", classe);

List<Eleve> eleves = query.getResultList();

return eleves;
}

}

Et voilà…

IV - Conclusion

Comme on vient de le voir, à travers ce rapide tutoriel, il est relativement simple d'utiliser JPA pour trifouiller dans une base de données telle que MySQL. Le code montre bien que c'est beaucoup plus facile qu'en utilisant JBBC (cf. article précédent) et d'autant plus lisible.

Le code final de ce tutoriel est disponible dans le fichier ZIP notes7_domain-et-dao-csv-db-jpa.zip.

Retrouvez les tutoriels de la série « en 5 minutes » sur developpez.com à l'adresse https://thierry-leriche-dessirier.developpez.com/#page_articles

V - Remerciements

Je tiens à remercier, en tant qu'auteur de tutoriel rapide, toutes les personnes qui m'ont aidé et soutenu. Je pense tout d'abord à mes collègues qui subissent mes questions au quotidien, mais aussi à mes contacts et amis du web, dans le domaine de l'informatique ou non, qui m'ont fait part de leurs remarques et critiques. Bien entendu, je n'oublie pas l'équipe de developpez.com qui m'a guidé dans la rédaction de cet article et m'a aidé à le corriger et le faire évoluer, principalement sur le forum.

Plus particulièrement j'adresse mes remerciements, à Mickael BARON (keulkeul), Marc (Gueritarish), OButterlin et Maxime Gault (_Max_)

VI - Annexes

VI-A - Annotations

Voici quelques détails supplémentaires à propos des annotations utilisées dans ce tutoriel, par ordre d'utilisation.

@Entity : indique que le bean ainsi annoté est un entity. Le bean sera manipulé et persisté par JPA. Cette annotation possède l'attribut optionnel « name », permettant de spécifier le nom de l'entité dans les requêtes, si l'on souhaite un autre nom que celui de la classe. L'utilisation de @Entity impose que le bean possède un identifiant (voir @Id) et un constructeur sans argument.

@Id : indique que le champ ainsi annoté est l'identifiant de l'entité. C'est sa clé primaire.

@GeneratedValue : indique que la valeur du champ ainsi annoté est générée automatiquement par la base de données.

@Basic : cette annotation est facultative. Elle possède les attributs « optional » et « fetch », permettant de spécifier, respectivement, si le champ est facultatif et le mode de chargement (lazy ou eager) de l'objet attaché au champ. En mode « lazy », la valeur est chargée lors de son utilisation. En mode « eager », la valeur est toujours chargée directement.

Le choix du mode « lazy » ou « eager » est très difficile. Il n'est pas possible d'en expliquer la complexité dans un article « en 5 minutes ».

@Column : permet de faire quelques précisions sur le champ ainsi annoté. Cette annotation possède l'attribut « name » permettant de spécifier le nom du champ en base, si celui-ci diffère de celui du champ dans le bean. L'attribut « nullable » spécifie si le champ peut être nul. L'attribut « length » spécifie la taille du champ. L'attribut « table » n'est utilisé qu'en cas de mapping multitable, ce qui n'entre plus dans le cadre d'un tutoriel « en 5 minutes ».

@Enumerated : indique que le champ ainsi annoté est un enum Java, dont la représentation en base pourra être un numérique ou un string.

@Temporal : indique que le champ ainsi annoté est une date. Cette annotation possède des attributs pour préciser le format à utiliser.

@Table : cette annotation s'utilise au niveau du bean, comme @Entity. Elle possède l'attribut « name » permettant de spécifier le nom de la table en base. On utilise @Table lorsque le nom de la table est différent de celui du bean.

VI-B - Liens

Tutoriel « Charger des données depuis une base MySQL en 5 minutes », montrant comment réaliser toute la mécanique expliquée ci-dessus, à l'adresse
https://thierry-leriche-dessirier.developpez.com/tutoriels/java/charger-donnees-mysql-5-min

QR Code vers cet article

Voici deux liens rapides à propos du Singleton, sur Developpez.com.

Le Singleton, par Sébastien MERIC
https://smeric.developpez.com/java/uml/singleton/

Le Singleton en environnement Multithread, par Christophe Jollivet
https://christophej.developpez.com/tutoriel/java/singleton/multithread/

Voici quelques liens vers des tutoriels de Developpez.com, parlant un peu plus précisément de JPA.

Persistance Java 5 par la pratique, par Serge Tahé
https://tahe.developpez.com/java/jpa/

Construire un service web Java EE avec NetBeans 6.5 et le serveur GlassFish, par Serge Tahé
https://tahe.developpez.com/java/webservice-jee/

Hibernate/JPA - Spring 2.5 - Tapestry 5, par Loïc Frering et Baptiste Meurant
https://loic-frering.developpez.com/tutoriels/java/hibernate-jpa-spring-tapestry/

Création d'une application de type CRUD avec JSF et JPA, par Jawher Moussa
https://djo-mos.developpez.com/tutoriels/java/crud-jsf-jpa/

Retrouvez mes autres tutoriels sur developpez.com à l'adresse https://thierry-leriche-dessirier.developpez.com/#page_articles

QR Code vers mes articles

VI-C - Les fichiers importants en entier

Eleve.java

package com.thi.notes.domain;

import static javax.persistence.EnumType.STRING;
import static javax.persistence.TemporalType.TIMESTAMP;

import java.util.Date;

import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Temporal;

@Entity
@NamedQueries({
@NamedQuery(name = "Eleve.findAll", query = "SELECT e FROM Eleve e "),
@NamedQuery(name = "Eleve.findByAnnee", query = "SELECT e FROM Eleve e WHERE e.annee = :annee ")
})
public class Eleve {

@Id
@GeneratedValue
private Integer id;

@Basic(optional = false)
private String nom;

@Basic(optional = false)
private String prenom;

@Basic(optional = false)
@Column(name = "classe")
private Integer annee;

@Enumerated(STRING)
@Basic(optional = false)
private Sexe sexe;

@Basic(optional = false)
@Column(name = "date_naissance")
@Temporal(TIMESTAMP)
private Date dateNaissance;

@Basic(optional = false)
private String adresse;

public Eleve() {
super();
}

public Eleve(String nom, String prenom, Integer annee, Sexe sexe) {
super();
this.nom = nom;
this.prenom = prenom;
this.annee = annee;
this.sexe = sexe;
}

public Eleve(String nom, String prenom, Integer annee, Sexe sexe, Date dateNaissance, String adresse) {
super();
this.nom = nom;
this.prenom = prenom;
this.annee = annee;
this.sexe = sexe;
this.dateNaissance = dateNaissance;
this.adresse = adresse;
}

public Eleve(Integer id, String nom, String prenom, Integer annee, Sexe sexe, Date dateNaissance, String adresse) {
this(nom, prenom, annee, sexe, dateNaissance, adresse);
this.id = id;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getNom() {
return nom;
}

public void setNom(String nom) {
this.nom = nom;
}

public String getPrenom() {
return prenom;
}

public void setPrenom(String prenom) {
this.prenom = prenom;
}

public Integer getAnnee() {
return annee;
}

public void setAnnee(Integer annee) {
this.annee = annee;
}

public Sexe getSexe() {
return sexe;
}

public void setSexe(Sexe sexe) {
this.sexe = sexe;
}

public Date getDateNaissance() {
return dateNaissance;
}

public void setDateNaissance(Date dateNaissance) {
this.dateNaissance = dateNaissance;
}

public String getAdresse() {
return adresse;
}

public void setAdresse(String adresse) {
this.adresse = adresse;
}

@Override
public String toString() {
return "Eleve [nom=" + nom + ", prenom=" + prenom + "]";
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((nom == null) ? 0 : nom.hashCode());
result = prime * result + ((prenom == null) ? 0 : prenom.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Eleve other = (Eleve) obj;
if (nom == null) {
if (other.nom != null)
return false;
} else if (!nom.equals(other.nom))
return false;
if (prenom == null) {
if (other.prenom != null)
return false;
} else if (!prenom.equals(other.prenom))
return false;

return true;
}

}

JpaEleveDao.java

package com.thi.notes.dao;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import com.thi.notes.domain.Eleve;

public class JpaEleveDao implements EleveDao {

private EntityManager em;

public JpaEleveDao() {
em = NoteEntityManager.getInstance().getEntityManager();
}

@Override
public List<Eleve> findEleves() {
// final String sql = "SELECT e FROM Eleve e ";
// final Query query = em.createQuery(sql);

final Query query = em.createNamedQuery("Eleve.findAll");

final List<Eleve> eleves = query.getResultList();

return eleves;
}

@Override
public List<Eleve> findElevesByClasse(Integer classe) {

// final String sql = "SELECT e FROM Eleve e WHERE e.annee = :annee ";
// final Query query = em.createQuery(sql);

final Query query = em.createNamedQuery("Eleve.findByAnnee");
query.setParameter("annee", classe);

final List<Eleve> eleves = query.getResultList();

return eleves;
}

}

JpaEleveDao.java

package com.thi.notes.dao;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class NoteEntityManager {

private static NoteEntityManager instance;

private static final String PERSISTENCE_UNIT_NAME = "notePU";
private EntityManagerFactory emf;
private EntityManager em;

private NoteEntityManager() {
emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
em = emf.createEntityManager();
}

public static synchronized NoteEntityManager getInstance() {
if (instance == null) {
instance = new NoteEntityManager();
}

return instance;
}

public EntityManager getEntityManager() {
return em;
}

}