Charger et afficher des données de la base et d'un fichier CSV simple en 5 minutes

Thierry Leriche-Dessirier

Ce petit article montre (par l'exemple) comment charger des données depuis un fichier CSV simple (avec Open-CSV) et depuis la base MySql (avec JDBC), en fusionnant les valeurs pour les afficher dans une Interface (Swing) sous forme de tableau (JTable et Table model) et sous forme de graphes, le tout en quelques minutes seulement. Commentez ♪

- Titre : Charger et afficher des données de la base et d'un fichier CSV simple en 5 minutes

- Auteur : Thierry Leriche-Dessirier

- Parution : 4 juin 2012

- Public visé :

- 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. Les objets du modèle

- II-D. Quelques classes importantes

- III. Action

- III-A. Désactivation du bouchon

- III-B. Charger les données à l'aide des DAO

- III-C. Fusionner les données

- IV. Conclusion

- V. Remerciements

- VI. Annexes

- VI-A. Liens

- VI-B. Les fichiers importants en entier

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 ». 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.

Tableau avec les données

Graphe camembert

Graphe en barre

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_data.sql (script complet dans le fichier Zip)

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 :

notes-dernier-exam.csv

# 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 ».

Bouchon dans NoteService.java

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

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;

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 ».

Sexe.java

public enum Sexe {

HOMME("Garçon"),
FEMME("Fille");

private final String label;

Sexe(String label) {
this.label = label;
}

public String getLabel() {
return label;
}
}

NoteEleve.java

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.

NoteService.java

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 ».

NotesModele.java

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.

EleveDao.java

public interface EleveDao {

List<Eleve> findEleves();

List<Eleve> findElevesByClasse(Integer classe);
}

NoteDao.java

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.

NoteService.java

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.

Tableau vide

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.

NoteService.java

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 ».

NoteService.java

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.

NoteService.java

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.

NoteService.java

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.

NoteService.java

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;

...

Tableau avec les données fusionnées

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.

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_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) 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

NoteService.java

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;
}
}

NotesModele.java

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;
}
}