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.
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
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 ».
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
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
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
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.
<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.
<repositories>
<repository>
<id>
EclipseLink Repo</id>
<url>
http://www.eclipse.org/downloads/download.php?r=1&
nf=1&
file=/rt/eclipselink/maven.repo</url>
</repository>
</repositories>
Il faut ensuite relancer une installation Maven à l'aide de la commande suivante.
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.
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é.
@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.
@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.
@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.
@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.
<?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.
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 ».
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.
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.
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.
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.
public
class
JpaEleveDao implements
EleveDao {
private
EntityManager em;
public
JpaEleveDao
(
) {
em =
NoteEntityManager.getInstance
(
).getEntityManager
(
);
}
Je reprends « DaoLauncher » pour tester que tout fonctionne.
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.
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 ».
@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.
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.code final
Retrouvez les tutoriels de la série « en 5 minutes » sur developpez.com à l'adresse https://thierry-leriche-dessirier.developpez.com/#page_articlesEn 5 minutes
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-minCharger des données depuis une base MySQL en 5 minutes
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_articlesTutoriels
VI-C. Les fichiers importants en entier▲
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
;
}
}
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;
}
}
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;
}
}