I. Introduction▲
Dans ce petit tutoriel, nous allons lire des données depuis un fichier CSV et depuis une base MySql. Nous allons fusionner ces données puis les afficher dans un tableau en Swing. Enfin, nous utiliserons les données pour générer des graphiques.
L'application que nous allons développer (en quelques étapes simples) permet d'afficher les notes qu'un groupe d'étudiants a obtenues au dernier examen. Nous allons considérer que la liste des étudiants, contenant des informations personnelles (prénom, nom, adresse, date de naissance, etc.), est stockée dans la base de données MySql de l'école. La liste des notes est, quant à elle, envoyée par le professeur vacataire par email, sous la forme d'un fichier CSV (initialement créé/exporté avec Excel). L'application doit donc fusionner ces deux sources de données avant de les afficher.
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 annexe.
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 ;
- Open CSV 2.3 ;
- jFreeChart 1.0.14.
Pour réaliser ce document, je me suis servi des tutoriels de la série « en 5 minutes »La série en 5 minutes. J'ai tout simplement suivi les tutoriels, les uns après les autres, en ajoutant un peu de glu lorsqu'il y en avait besoin. En particulier, et dans cet ordre, j'ai utilisé les articles suivants :
- tutoriel « Afficher un tableau avec un Table Model en 5 minutes » ;
- tutoriel « Charger des données depuis une base MySql en 5 minutes » ;
- tutoriel « Charger des données depuis un fichier CSV simple en 5 minutes » ;
- tutoriel « Charger des données depuis une base MySql en 5 minutes ».
Ce document est donc, en quelque sorte, la compilation de ces différents tutoriels. En réalité, il faudra donc un peu plus de cinq minutes pour réaliser toutes les étapes, car il est nécessaire de lire les tutoriels indiqués avant de continuer.
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.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 annexe) et de vous repérer à l'aide des captures d'écran.
II-B. Ce que fait déjà le projet▲
Les codes proposés (et déjà en partie assemblés) dans ce projet proviennent des autres tutoriels de la série « en 5 minutes » et réalisent les fonctionnalités suivantes :
- affichage de données dans un tableau à l'aide d'un « Table model » (cf. capture) ;
- génération et affichage de graphes à l'aide de jfreechart (cf. captures) ;
- lecture de données depuis un fichier CSV simple à l'aide d'Open CSV ;
- chargement de données depuis une base MySql à l'aide de JDBC.
La liste des élèves vient d'une base MySql, alimentée à l'aide du script SQL présenté dans le tutoriel « Charger des données depuis une base MySql en 5 minutes », dont le contenu est le suivant :
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'
)
,
(
4
, 'Varola'
, 'Sophie'
, 3
, '1992-01-21 00:00:00'
, '15 rue du Lac 75001 Paris'
, 'FEMME'
)
,
...
(
48
, 'Valegin'
, 'Jean'
, 3
, '1992-11-28 00:00:00'
, '15 rue du Lac 75001 Paris'
, 'HOMME'
)
,
(
49
, 'Eto'
, 'Gabin'
, 3
, '1992-11-18 00:00:00'
, '15 rue du Lac 75001 Paris'
, 'HOMME'
)
,
(
50
, 'Fivolini'
, 'Kevin'
, 3
, '1992-12-06 00:00:00'
, '15 rue du Lac 75001 Paris'
, 'HOMME'
)
,
(
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'
)
;
Il faut donc créer la base de données correspondante comme indiqué dans le tutoriel si ce n'est pas déjà fait.
La liste des notes provient du fichier CSV présenté dans le tutoriel « Charger des données depuis un fichier CSV simple en 5 minutes », dont le contenu est le suivant :
# Nom;Prénom;Note
Durand;Marie;5
Alesi;Julie;8
Martini;Carine;10,5
Varola;Sophie;12
Labiche;Lelou;12
Dujardin;Anne;13
Laventure;Martine;14
...
Fivolini;Kevin;19
Laferme;Martin;20
Dupuis;Vincent;20
Pour l'instant, les données affichées dans le tableau sont indépendantes de celles qui sont chargées en base. Le « Table model » utilise un « bouchon » pour charger les données. Ceci est présenté dans les tutoriels « Afficher un tableau avec un Table Model en 5 minutes » et « Afficher un graphe jfreechart en 5 minutes ». Un des enjeux du tutoriel est justement de ne plus utiliser ce « bouchon ».
public
class
NoteService {
private
List<
NoteEleve>
notes;
...
public
synchronized
List<
NoteEleve>
findLastNotes
(
) {
doBouchon
(
); // ici le bouchon
return
notes;
}
private
void
doBouchon
(
) {
if
(
notes !=
null
) {
return
;
}
notes =
new
ArrayList<
NoteEleve>(
);
// Filles (nom, prenom, annee, sexe, note)
addEleveEtNote
(
"Durand"
, "Marie"
, 3
, FEMME, 5
);
addEleveEtNote
(
"Alesi"
, "Julie"
, 3
, FEMME, 8
);
addEleveEtNote
(
"Martini"
, "Carine"
, 3
, FEMME, 10
);
...
addEleveEtNote
(
"Baladini"
, "Mathilde"
, 3
, FEMME, 17
);
// Garcons (nom, prenom, annee, sexe, note)
addEleveEtNote
(
"Michelet"
, "Jean"
, 3
, HOMME, 0
);
addEleveEtNote
(
"Dupond"
, "Pierre"
, 3
, HOMME, 2
);
addEleveEtNote
(
"Timberot"
, "Martin"
, 3
, HOMME, 4
);
...
addEleveEtNote
(
"Fivolini"
, "Kevin"
, 3
, HOMME, 19
);
addEleveEtNote
(
"Laferme"
, "Martin"
, 3
, HOMME, 20
);
addEleveEtNote
(
"Dupuis"
, "Vincent"
, 3
, HOMME, 20
);
}
private
void
addEleveEtNote
(
String nom, String prenom, Integer annee, Sexe sexe, int
note) {
addEleveEtNote
(
nom, prenom, annee, sexe, new
Double
(
note));
}
private
void
addEleveEtNote
(
String nom, String prenom, Integer annee, Sexe sexe, Double note) {
final
Eleve eleve =
new
Eleve
(
nom, prenom, annee, sexe);
final
NoteEleve noteEleve =
new
NoteEleve
(
eleve, note);
notes.add
(
noteEleve);
}
II-C. Les objets du modèle▲
Le modèle est constitué de trois classes : Eleve.java, Sexe.java et NoteEleve.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;
public
Eleve
(
String nom, String prenom, Integer annee, Sexe sexe, Date dateNaissance, String adresse) {
...
}
public
Eleve
(
Integer id, String nom, String prenom, Integer annee, Sexe sexe, Date dateNaissance, String adresse) {
...
}
// + getter et setter
}
La classe Eleve.java comporte un attribut « id » qui correspond à l'identifiant technique en base de données. Ceci est expliqué dans le tutoriel « Charger des données depuis une base MySql en 5 minutes ».
public
enum
Sexe {
HOMME
(
"Garçon"
),
FEMME
(
"Fille"
);
private
final
String label;
Sexe
(
String label) {
this
.label =
label;
}
public
String getLabel
(
) {
return
label;
}
}
public
class
NoteEleve {
private
Eleve eleve;
private
Double note;
public
NoteEleve
(
Eleve eleve, Double note) {
...
}
// + getter et setter
II-D. Quelques classes importantes▲
On a déjà vu plus haut une partie de « NoteService.java », qui se présente sous la forme d'un singleton.
public
class
NoteService {
private
static
NoteService instance;
private
List<
NoteEleve>
notes;
private
NoteService
(
) {
...
}
public
synchronized
List<
NoteEleve>
findLastNotes
(
) {
doBouchon
(
); // ici le bouchon
return
notes;
}
private
void
doBouchon
(
) {
...
}
}
Ce service est appelé par le « Table model ».
public
class
NotesModele extends
AbstractTableModel {
private
final
String[] entetes =
{
"Nom"
, "Prénom"
, "Année"
, "Sexe"
, "Note"
}
;
private
NoteService noteService;
private
List<
NoteEleve>
notes;
public
NotesModele
(
) {
noteService =
NoteService.getInstance
(
);
notes =
noteService.findLastNotes
(
);
}
...
public
List<
NoteEleve>
getNotes
(
) {
return
notes;
}
}
Le tableau et les boutons visibles dans l'IHM sont déjà connectés au « table model ».
Enfin, les interfaces des DAO sont les suivantes.
public
interface
EleveDao {
List<
Eleve>
findEleves
(
);
List<
Eleve>
findElevesByClasse
(
Integer classe);
}
public
interface
NoteDao {
Map<
String, Double>
findNotesDernierExam
(
);
}
Les implémentations (de ces DAO) qui nous intéressent (dans le cadre de ce tutoriel) sont NoteDao.java et CsvNoteDao.java.
III. Action▲
III-A. Désactivation du bouchon▲
Durée estimée : 1 minute.
Pour commencer, nous allons supprimer le « bouchon » utilisé dans le service.
public
class
NoteService {
private
static
NoteService instance;
private
List<
NoteEleve>
notes;
private
NoteService
(
) {
...
}
public
synchronized
List<
NoteEleve>
findLastNotes
(
) {
notes =
new
ArrayList<
NoteEleve>(
);
return
notes;
}
// Suppression du bouchon.
// private void doBouchon() {
// ...
// }
}
Ici je retourne une liste vide. Je préfère éviter de renvoyer null pour ne pas avoir à gérer le cas de la nullité dans le reste du code.
Sans surprise, le tableau ne contient aucune donnée.
III-B. Charger les données à l'aide des DAO▲
Durée estimée : 2 minutes.
Nous allons maintenant utiliser les DAO pour obtenir la liste des élèves et leurs notes. Tout d'abord, on doit donc déclarer les bonnes implémentations.
public
class
NoteService {
...
private
NoteDao noteDao;
private
EleveDao eleveDao;
/**
* Constructeur prive
*/
private
NoteService
(
) {
super
(
);
noteDao =
new
CsvNoteDao
(
);
eleveDao =
new
DbEleveDao
(
);
}
...
On peut ensuite lancer le chargement des données dans la méthode « find ».
public
synchronized
List<
NoteEleve>
findLastNotes
(
) {
notes =
new
ArrayList<
NoteEleve>(
);
// 1) Chargement de la liste des eleves en base
List<
Eleve>
eleves =
eleveDao.findEleves
(
);
// 2) Chargement des notes depuis le fichier CSV
Map<
String, Double>
map =
noteDao.findNotesDernierExam
(
);
return
notes;
}
...
Petite subtilité : puisqu'on connaît le nombre d'élèves, on peut directement initialiser la liste de retour avec la bonne taille.
public
synchronized
List<
NoteEleve>
findLastNotes
(
) {
// 1) Chargement de la liste des eleves en base
List<
Eleve>
eleves =
eleveDao.findEleves
(
);
// 2) Chargement des notes depuis le fichier CSV
Map<
String, Double>
map =
noteDao.findNotesDernierExam
(
);
// 3) Init avec la bonne taille
notes =
new
ArrayList<
NoteEleve>(
eleves.size
(
) );
return
notes;
...
Pendant qu'on y est, on peut faire de même dans la méthode dataToMap de CsvNoteDao.java puisqu'elle doit renvoyer le même nombre d'éléments qu'elle en reçoit. Elle se contente de les convertir.
private
Map<
String, Double>
dataToMap
(
List<
String[] >
data) {
//final Map<String, Double> map = new HashMap<String, Double>();
final
Map<
String, Double>
map =
new
HashMap<
String, Double>(
data.size
(
) ); // init avec la bonne taille
III-C. Fusionner les données▲
Durée estimée : 1 minute.
Il ne reste plus qu'à réunir les données fournies par chaque DAO. On peut partir du principe que tous les élèves doivent avoir une note. Si la note n'est pas indiquée dans le fichier CSV, alors on en déduit que l'étudiant était absent à l'examen et doit donc recevoir un zéro.
La méthode findNotesDernierExam de CsvNoteDao retourne une map dont les clés sont constituées de la concaténation du nom et du prénom de l'étudiant.
public
synchronized
List<
NoteEleve>
findLastNotes
(
) {
// 1) Chargement de la liste des eleves en base
List<
Eleve>
eleves =
eleveDao.findEleves
(
);
// 2) Chargement des notes depuis le fichier CSV
Map<
String, Double>
map =
noteDao.findNotesDernierExam
(
);
// 3) Init avec la bonne taille
notes =
new
ArrayList<
NoteEleve>(
eleves.size
(
) );
// 4) fusion des donnees
for
(
Eleve eleve : eleves) {
final
String key =
eleve.getNom
(
) +
" "
+
eleve.getPrenom
(
); // ex. "DUPONT Jean"
Double valeur =
map.get
(
key);
if
(
valeur ==
null
) {
valeur =
0.0
; // Si eleve absent
}
NoteEleve noteEleve =
new
NoteEleve
(
eleve, valeur);
notes.add
(
noteEleve);
}
return
notes;
...
Et voilà…
IV. Conclusion▲
Ce petit article était encore plus court que promis puisqu'on reste vraiment sous les cinq minutes, sans forcer. Le plus gros du travail a déjà été réalisé dans les tutoriels précédents.
Pour la suite, on pourrait imaginer de nombreuses nouvelles fonctionnalités. Par exemple, on pourrait enregistrer dans la base MySQL les données lues à partir du fichier CSV. On pourrait également ajouter des menus dans l'IHM pour choisir le fichier CSV à lire. À l'inverse, on pourrait imaginer des fonctions d'export des données consolidées ou encore des fonctions de générations de documents PDF (pour le conseil de classe ou pour envoyer aux parents).
Le code final de ce tutoriel est disponible dans le fichier ZIP notes6.zip.code final
Vos retours nous aident à améliorer nos publications. N'hésitez donc pas à commenter cet article sur le forum : Commentez
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) et Sébastien (FirePrawn).
VI. Annexes▲
VI-A. Liens▲
Le tutoriel « Afficher un tableau avec un Table Model en 5 minutes » est disponible à l'adresse
https://thierry-leriche-dessirier.developpez.com/tutoriels/java/afficher-tableau-avec-tablemodel-5-min
Le tutoriel « Afficher un graphe jfreechart en 5 minutes » est disponible à l'adresse
https://thierry-leriche-dessirier.developpez.com/tutoriels/java/afficher-graphe-jfreechart-5-min
Le tutoriel « Charger des données depuis un fichier CSV simple en 5 minutes » est disponible à l'adresse
https://thierry-leriche-dessirier.developpez.com/tutoriels/java/charger-donnees-fichier-csv-5-min
Enfin, le tutoriel « Charger des données depuis une base MySql en 5 minutes » est disponible à l'adresse
https://thierry-leriche-dessirier.developpez.com/tutoriels/java/charger-donnees-mysql-5-min
VI-B. Les fichiers importants en entier▲
package
com.thi.notes.service;
import
java.util.ArrayList;
import
java.util.List;
import
java.util.Map;
import
com.thi.notes.dao.CsvNoteDao;
import
com.thi.notes.dao.DbEleveDao;
import
com.thi.notes.dao.EleveDao;
import
com.thi.notes.dao.NoteDao;
import
com.thi.notes.domain.Eleve;
import
com.thi.notes.domain.NoteEleve;
public
class
NoteService {
private
static
NoteService instance;
private
List<
NoteEleve>
notes;
private
NoteDao noteDao;
private
EleveDao eleveDao;
private
NoteService
(
) {
super
(
);
noteDao =
new
CsvNoteDao
(
);
eleveDao =
new
DbEleveDao
(
);
}
public
static
synchronized
NoteService getInstance
(
) {
if
(
instance ==
null
) {
instance =
new
NoteService
(
);
}
return
instance;
}
public
synchronized
List<
NoteEleve>
findLastNotes
(
) {
// 1) Chargement de la liste des eleves en base
List<
Eleve>
eleves =
eleveDao.findEleves
(
);
// 2) Chargement des notes depuis le fichier CSV
Map<
String, Double>
map =
noteDao.findNotesDernierExam
(
);
// 3) Init avec la bonne taille
notes =
new
ArrayList<
NoteEleve>(
eleves.size
(
));
// 4) fusion des donnees
for
(
Eleve eleve : eleves) {
final
String key =
eleve.getNom
(
) +
" "
+
eleve.getPrenom
(
);
Double valeur =
map.get
(
key);
if
(
valeur ==
null
) {
valeur =
0.0
;
}
final
NoteEleve noteEleve =
new
NoteEleve
(
eleve, valeur);
notes.add
(
noteEleve);
}
return
notes;
}
}
package
com.thi.notes.ihm;
import
java.util.List;
import
javax.swing.table.AbstractTableModel;
import
com.thi.notes.domain.NoteEleve;
import
com.thi.notes.domain.Sexe;
import
com.thi.notes.service.NoteService;
public
class
NotesModele extends
AbstractTableModel {
private
final
String[] entetes =
{
"Nom"
, "Prénom"
, "Année"
, "Sexe"
, "Note"
}
;
private
NoteService noteService;
private
List<
NoteEleve>
notes;
public
NotesModele
(
) {
super
(
);
noteService =
NoteService.getInstance
(
);
notes =
noteService.findLastNotes
(
);
}
@Override
public
int
getColumnCount
(
) {
return
entetes.length;
}
@Override
public
String getColumnName
(
int
columnIndex) {
return
entetes[columnIndex];
}
@Override
public
int
getRowCount
(
) {
return
notes.size
(
);
}
@Override
public
Object getValueAt
(
int
rowIndex, int
columnIndex) {
switch
(
columnIndex) {
case
0
:
// Nom
return
notes.get
(
rowIndex).getEleve
(
).getNom
(
);
case
1
:
// Prenom
return
notes.get
(
rowIndex).getEleve
(
).getPrenom
(
);
case
2
:
// Annee
return
notes.get
(
rowIndex).getEleve
(
).getAnnee
(
);
case
3
:
// Sexe
return
notes.get
(
rowIndex).getEleve
(
).getSexe
(
);
case
4
:
// Note au controle
return
notes.get
(
rowIndex).getNote
(
);
default
:
throw
new
IllegalArgumentException
(
);
}
}
@Override
public
Class<
?>
getColumnClass
(
int
columnIndex) {
switch
(
columnIndex) {
case
0
:
case
1
:
return
String.class
;
case
3
:
return
Sexe.class
;
case
2
:
return
Integer.class
;
case
4
:
return
Double.class
;
default
:
return
Object.class
;
}
}
public
List<
NoteEleve>
getNotes
(
) {
return
notes;
}
}