↑
CHAPITRE 6.3

TableView

Maîtriser l'affichage de tableaux de données dans JavaFX
Dans cette section, vous allez apprendre à utiliser le composant TableView de JavaFX pour afficher des données sous forme de tableau. Vous découvrirez comment créer des colonnes, lier des données, utiliser PropertyValueFactory pour lier automatiquement les propriétés, et même permettre l'édition des cellules. Le TableView est le composant idéal pour afficher des données structurées en lignes et colonnes.

6.3TableView

6.3.1 – Structure : colonnes, lignes, modèle

Le TableView est un composant JavaFX qui affiche des données sous forme de tableau avec des colonnes et des lignes. C'est l'équivalent d'un tableau HTML ou d'une grille de données. Chaque ligne représente un objet de votre modèle, et chaque colonne affiche une propriété de cet objet.

🎯 Structure d'un TableView

Un TableView est composé de plusieurs éléments :

  • TableView : Le conteneur principal qui affiche le tableau
  • TableColumn : Chaque colonne du tableau (une par propriĂ©tĂ©)
  • Lignes : Chaque ligne reprĂ©sente un objet de votre modèle
  • Modèle : La classe qui reprĂ©sente vos donnĂ©es (ex: Personne, Produit, etc.)
  • ObservableList : La liste d'objets Ă  afficher dans le tableau
Important - Configuration du module :
Si vous utilisez des propriétés observables (StringProperty, IntegerProperty, etc.) dans votre modèle, vous devez ajouter javafx.base dans le fichier module-info.java :

opens application to javafx.graphics, javafx.fxml, javafx.base;

Sinon, vous obtiendrez l'erreur : "module javafx.base cannot access class application.Personne"

📝 Créer un TableView simple

Pour créer un TableView, vous devez :

  1. Créer une classe modèle (ex: Personne)
  2. Créer des TableColumn pour chaque propriété
  3. Lier les colonnes aux propriétés
  4. Créer une ObservableList avec vos données
  5. Lier la liste au TableView
Exemple TableView - Structure : colonnes, lignes, modèle
Exemple d'application TableView avec structure de base : colonnes, lignes et modèle

Exemple 1 : Classe Personne

package application;

public class Personne {
    private String nom;
    private String prenom;
    private int age;
    private String email;
    
    public Personne(String nom, String prenom, int age, String email) {
        this.nom = nom;
        this.prenom = prenom;
        this.age = age;
        this.email = email;
    }
    
    // Getters
    public String getNom() { return nom; }
    public String getPrenom() { return prenom; }
    public int getAge() { return age; }
    public String getEmail() { return email; }
    
    // Setters
    public void setNom(String nom) { this.nom = nom; }
    public void setPrenom(String prenom) { this.prenom = prenom; }
    public void setAge(int age) { this.age = age; }
    public void setEmail(String email) { this.email = email; }
}

Exemple 2 : TableView avec colonnes

package application;

import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class MainController {
    
    @FXML
    private TableView<Personne> tableViewPersonnes;
    
    @FXML
    private TableColumn<Personne, String> colNom;
    
    @FXML
    private TableColumn<Personne, String> colPrenom;
    
    @FXML
    private TableColumn<Personne, Integer> colAge;
    
    @FXML
    private TableColumn<Personne, String> colEmail;
    
    private ObservableList<Personne> personnes;
    
    @FXML
    private void initialize() {
        // Créer les données
        personnes = FXCollections.observableArrayList(
            new Personne("Dupont", "Jean", 25, "jean@exemple.com"),
            new Personne("Martin", "Marie", 30, "marie@exemple.com"),
            new Personne("Bernard", "Pierre", 28, "pierre@exemple.com")
        );
        
        // Lier les colonnes aux propriétés (voir section 6.3.2 pour PropertyValueFactory)
        // Pour l'instant, on définit juste les colonnes
        colNom.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getNom()));
        colPrenom.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getPrenom()));
        colAge.setCellValueFactory(cellData -> new SimpleIntegerProperty(cellData.getValue().getAge()).asObject());
        colEmail.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getEmail()));
        
        // Définir les titres des colonnes
        colNom.setText("Nom");
        colPrenom.setText("Prénom");
        colAge.setText("Âge");
        colEmail.setText("Email");
        
        // Lier la liste au TableView
        tableViewPersonnes.setItems(personnes);
    }
}
Explication :
• TableView<Personne> : TableView qui affiche des objets Personne
• TableColumn<Personne, String> : Colonne pour une propriété String de Personne
• setCellValueFactory() : Définit comment obtenir la valeur à afficher
• setItems() : Lie l'ObservableList au TableView

📝 Structure FXML

Dans le fichier FXML, vous devez définir le TableView et ses colonnes :

<TableView fx:id="tableViewPersonnes">
    <columns>
        <TableColumn fx:id="colNom" text="Nom" />
        <TableColumn fx:id="colPrenom" text="Prénom" />
        <TableColumn fx:id="colAge" text="Âge" />
        <TableColumn fx:id="colEmail" text="Email" />
    </columns>
</TableView>

📝 Types de colonnes

Les colonnes peuvent afficher différents types de données :

  • String : TableColumn<Personne, String>
  • Integer : TableColumn<Personne, Integer>
  • Double : TableColumn<Personne, Double>
  • Boolean : TableColumn<Personne, Boolean>
  • Date : TableColumn<Personne, LocalDate>
Important :
• Pour les types primitifs (int, double, etc.), utilisez les wrappers (Integer, Double)
• Pour Integer, utilisez .asObject() pour convertir en IntegerProperty
• Chaque colonne doit être typée correctement selon la propriété qu'elle affiche

💡 Points clés à retenir

  • TableView : Composant pour afficher des donnĂ©es en tableau
  • TableColumn : Chaque colonne reprĂ©sente une propriĂ©tĂ©
  • setCellValueFactory() : DĂ©finit comment obtenir la valeur Ă  afficher
  • setItems() : Lie l'ObservableList au TableView
  • Modèle : CrĂ©ez une classe pour reprĂ©senter vos donnĂ©es
  • ObservableList : Utilisez une liste observable pour la mise Ă  jour automatique
Conseil : Commencez par créer votre modèle (classe Personne), puis créez les colonnes une par une. Testez chaque colonne avant d'en ajouter d'autres !

6.3.2 – PropertyValueFactory

Le PropertyValueFactory est une classe utilitaire qui simplifie la liaison des colonnes aux propriétés de votre modèle. Au lieu d'écrire manuellement le code pour extraire la valeur, vous pouvez utiliser PropertyValueFactory qui le fait automatiquement en utilisant le nom de la propriété.

🎯 Avantages de PropertyValueFactory

  • SimplicitĂ© : Moins de code Ă  Ă©crire
  • Automatique : La liaison se fait automatiquement par nom de propriĂ©tĂ©
  • MaintenabilitĂ© : Plus facile Ă  maintenir
  • Standard : MĂ©thode standard recommandĂ©e par JavaFX

📝 Utilisation de PropertyValueFactory

Au lieu d'écrire manuellement le code pour chaque colonne :

// Méthode manuelle (longue)
colNom.setCellValueFactory(cellData -> 
    new SimpleStringProperty(cellData.getValue().getNom())
);

// Avec PropertyValueFactory (simple)
colNom.setCellValueFactory(new PropertyValueFactory<>("nom"));
Exemple TableView - PropertyValueFactory
Exemple d'application TableView utilisant PropertyValueFactory pour simplifier la liaison des colonnes

Exemple complet : TableView avec PropertyValueFactory

package application;

import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class MainController {
    
    @FXML
    private TableView<Personne> tableViewPersonnes;
    
    @FXML
    private TableColumn<Personne, String> colNom;
    
    @FXML
    private TableColumn<Personne, String> colPrenom;
    
    @FXML
    private TableColumn<Personne, Integer> colAge;
    
    @FXML
    private TableColumn<Personne, String> colEmail;
    
    private ObservableList<Personne> personnes;
    
    @FXML
    private void initialize() {
        // Créer les données
        personnes = FXCollections.observableArrayList(
            new Personne("Dupont", "Jean", 25, "jean@exemple.com"),
            new Personne("Martin", "Marie", 30, "marie@exemple.com"),
            new Personne("Bernard", "Pierre", 28, "pierre@exemple.com"),
            new Personne("Dubois", "Sophie", 32, "sophie@exemple.com")
        );
        
        // Utiliser PropertyValueFactory pour lier automatiquement
        colNom.setCellValueFactory(new PropertyValueFactory<>("nom"));
        colPrenom.setCellValueFactory(new PropertyValueFactory<>("prenom"));
        colAge.setCellValueFactory(new PropertyValueFactory<>("age"));
        colEmail.setCellValueFactory(new PropertyValueFactory<>("email"));
        
        // Définir les titres des colonnes
        colNom.setText("Nom");
        colPrenom.setText("Prénom");
        colAge.setText("Âge");
        colEmail.setText("Email");
        
        // Lier la liste au TableView
        tableViewPersonnes.setItems(personnes);
    }
}
Comment ça marche :
• PropertyValueFactory cherche une méthode getter correspondant au nom de la propriété
• Pour "nom", il cherche getNom()
• Pour "age", il cherche getAge()
• La méthode getter doit exister dans votre classe modèle

📝 Convention de nommage

PropertyValueFactory suit la convention JavaBean :

  • PropriĂ©tĂ© "nom" → cherche getNom() ou nomProperty()
  • PropriĂ©tĂ© "age" → cherche getAge() ou ageProperty()
  • PropriĂ©tĂ© "email" → cherche getEmail() ou emailProperty()
Important :
  • âś… Le nom de la propriĂ©tĂ© doit correspondre au nom du getter (sans "get" et en minuscule)
  • âś… Les getters doivent suivre la convention JavaBean (get + Nom en CamelCase)
  • âś… Si vous utilisez des propriĂ©tĂ©s observables (StringProperty, etc.), PropertyValueFactory les utilisera automatiquement
  • ❌ Ne pas utiliser de noms avec des majuscules au dĂ©but (ex: "Nom" au lieu de "nom")

📝 Comparaison : Manuelle vs PropertyValueFactory

Aspect Méthode manuelle PropertyValueFactory
Code Plus de code Moins de code
Simplicité Plus complexe Plus simple
Performance Légèrement plus rapide Légèrement plus lent (négligeable)
Maintenabilité Moins maintenable Plus maintenable
Recommandation Cas spéciaux uniquement ✅ Méthode standard

💡 Points clés à retenir

  • PropertyValueFactory : Simplifie la liaison des colonnes aux propriĂ©tĂ©s
  • Convention JavaBean : Utilise les getters standard (getNom, getAge, etc.)
  • Nom de propriĂ©tĂ© : Utilisez le nom en minuscule (ex: "nom", pas "Nom")
  • Getters requis : Les mĂ©thodes getter doivent exister dans votre classe
  • MĂ©thode standard : C'est la mĂ©thode recommandĂ©e pour la plupart des cas
Conseil : Utilisez PropertyValueFactory dans la plupart des cas. Utilisez la méthode manuelle seulement si vous avez besoin de logique spéciale (transformation, calcul, etc.).

6.3.3 – TableView + modèle observable

Pour que le TableView se mette à jour automatiquement quand vous modifiez les propriétés des objets, vous devez utiliser des propriétés observables JavaFX (StringProperty, IntegerProperty, etc.) dans votre modèle. Cela permet une mise à jour en temps réel du tableau.

🎯 Pourquoi utiliser des propriétés observables ?

  • Mise Ă  jour automatique : Le TableView se met Ă  jour quand vous modifiez une propriĂ©tĂ©
  • Temps rĂ©el : Les changements sont visibles immĂ©diatement
  • Pas de refresh() : Plus besoin d'appeler refresh() manuellement
  • Binding : Permet de crĂ©er des bindings avec d'autres composants

📝 Modèle avec propriétés observables

Personne.java avec propriétés observables

package application;

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Personne {
    private StringProperty nom = new SimpleStringProperty();
    private StringProperty prenom = new SimpleStringProperty();
    private IntegerProperty age = new SimpleIntegerProperty();
    private StringProperty email = new SimpleStringProperty();
    
    public Personne(String nom, String prenom, int age, String email) {
        this.nom.set(nom);
        this.prenom.set(prenom);
        this.age.set(age);
        this.email.set(email);
    }
    
    // Getters pour les propriétés (nécessaires pour PropertyValueFactory)
    public StringProperty nomProperty() { return nom; }
    public StringProperty prenomProperty() { return prenom; }
    public IntegerProperty ageProperty() { return age; }
    public StringProperty emailProperty() { return email; }
    
    // Getters classiques
    public String getNom() { return nom.get(); }
    public String getPrenom() { return prenom.get(); }
    public int getAge() { return age.get(); }
    public String getEmail() { return email.get(); }
    
    // Setters
    public void setNom(String nom) { this.nom.set(nom); }
    public void setPrenom(String prenom) { this.prenom.set(prenom); }
    public void setAge(int age) { this.age.set(age); }
    public void setEmail(String email) { this.email.set(email); }
}
Points importants :
• Utilisez StringProperty au lieu de String
• Utilisez IntegerProperty au lieu de int
• Créez les méthodes nomProperty(), ageProperty(), etc.
• PropertyValueFactory utilisera automatiquement ces propriétés

📝 Utilisation avec TableView

package application;

import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class MainController {
    
    @FXML
    private TableView<Personne> tableViewPersonnes;
    
    @FXML
    private TableColumn<Personne, String> colNom;
    
    @FXML
    private TableColumn<Personne, String> colPrenom;
    
    @FXML
    private TableColumn<Personne, Integer> colAge;
    
    @FXML
    private TableColumn<Personne, String> colEmail;
    
    private ObservableList<Personne> personnes;
    
    @FXML
    private void initialize() {
        // Créer les données
        personnes = FXCollections.observableArrayList(
            new Personne("Dupont", "Jean", 25, "jean@exemple.com"),
            new Personne("Martin", "Marie", 30, "marie@exemple.com"),
            new Personne("Bernard", "Pierre", 28, "pierre@exemple.com")
        );
        
        // PropertyValueFactory utilise automatiquement les propriétés observables
        colNom.setCellValueFactory(new PropertyValueFactory<>("nom"));
        colPrenom.setCellValueFactory(new PropertyValueFactory<>("prenom"));
        colAge.setCellValueFactory(new PropertyValueFactory<>("age"));
        colEmail.setCellValueFactory(new PropertyValueFactory<>("email"));
        
        // Définir les titres
        colNom.setText("Nom");
        colPrenom.setText("Prénom");
        colAge.setText("Âge");
        colEmail.setText("Email");
        
        // Lier au TableView
        tableViewPersonnes.setItems(personnes);
    }
    
    @FXML
    private void modifierPersonne() {
        Personne selectionnee = tableViewPersonnes.getSelectionModel().getSelectedItem();
        if (selectionnee != null) {
            // Modifier les propriétés
            selectionnee.setNom("Nouveau nom");
            selectionnee.setAge(30);
            
            // Le TableView se met Ă  jour AUTOMATIQUEMENT !
            // Pas besoin d'appeler refresh()
        }
    }
}
Avantage :
• Quand vous modifiez une propriété avec setNom(), le TableView se met à jour automatiquement
• Plus besoin d'appeler tableView.refresh()
• Les changements sont visibles en temps réel
Exemple TableView - Modèle observable
Exemple d'application TableView avec modèle observable : mise à jour automatique lors de la modification des propriétés

📝 Comparaison : Modèle simple vs Modèle observable

Aspect Modèle simple Modèle observable
Types String, int, etc. StringProperty, IntegerProperty, etc.
Mise à jour Nécessite refresh() Automatique
Complexité Plus simple Légèrement plus complexe
Performance Légèrement plus rapide Légèrement plus lent (négligeable)
Recommandation Données statiques ✅ Données modifiables

💡 Points clés à retenir

  • PropriĂ©tĂ©s observables : Utilisez StringProperty, IntegerProperty, etc.
  • MĂ©thodes Property : CrĂ©ez nomProperty(), ageProperty(), etc.
  • Mise Ă  jour automatique : Le TableView se met Ă  jour automatiquement
  • Pas de refresh() : Plus besoin d'appeler refresh() manuellement
  • PropertyValueFactory : Utilise automatiquement les propriĂ©tĂ©s observables
Conseil : Utilisez des propriétés observables si vous prévoyez de modifier les données après leur création. Pour des données statiques (lecture seule), un modèle simple suffit.

6.3.4 – Édition de cellule (optionnel)

Par défaut, les cellules d'un TableView sont en lecture seule. Vous pouvez activer l'édition pour permettre à l'utilisateur de modifier directement les valeurs dans le tableau. Cette fonctionnalité est optionnelle et doit être activée explicitement.

Note : L'édition de cellule est optionnelle. Activez-la seulement si vous voulez permettre à l'utilisateur de modifier les données directement dans le tableau.

🎯 Quand activer l'édition ?

  • Modification directe : L'utilisateur doit pouvoir modifier les valeurs rapidement
  • Interface simple : Pas besoin de formulaire sĂ©parĂ©
  • DonnĂ©es simples : Les donnĂ©es sont simples Ă  modifier (texte, nombres)
  • ExpĂ©rience utilisateur : AmĂ©liore l'expĂ©rience utilisateur pour certaines applications

📝 Activer l'édition

Pour activer l'édition, vous devez :

  1. Activer l'édition sur le TableView
  2. Définir un CellFactory éditable pour chaque colonne
  3. Définir un OnEditCommit pour sauvegarder les modifications

Exemple : TableView avec édition

package application;

import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class MainController {
    
    @FXML
    private TableView<Personne> tableViewPersonnes;
    
    @FXML
    private TableColumn<Personne, String> colNom;
    
    @FXML
    private TableColumn<Personne, String> colPrenom;
    
    @FXML
    private TableColumn<Personne, Integer> colAge;
    
    @FXML
    private TableColumn<Personne, String> colEmail;
    
    private ObservableList<Personne> personnes;
    
    @FXML
    private void initialize() {
        // Créer les données
        personnes = FXCollections.observableArrayList(
            new Personne("Dupont", "Jean", 25, "jean@exemple.com"),
            new Personne("Martin", "Marie", 30, "marie@exemple.com")
        );
        
        // Configurer les colonnes avec PropertyValueFactory
        colNom.setCellValueFactory(new PropertyValueFactory<>("nom"));
        colPrenom.setCellValueFactory(new PropertyValueFactory<>("prenom"));
        colAge.setCellValueFactory(new PropertyValueFactory<>("age"));
        colEmail.setCellValueFactory(new PropertyValueFactory<>("email"));
        
        // Activer l'édition sur le TableView
        tableViewPersonnes.setEditable(true);
        
        // Activer l'édition pour chaque colonne
        colNom.setCellFactory(TextFieldTableCell.forTableColumn());
        colNom.setOnEditCommit(event -> {
            Personne personne = event.getRowValue();
            personne.setNom(event.getNewValue());
        });
        
        colPrenom.setCellFactory(TextFieldTableCell.forTableColumn());
        colPrenom.setOnEditCommit(event -> {
            Personne personne = event.getRowValue();
            personne.setPrenom(event.getNewValue());
        });
        
        colEmail.setCellFactory(TextFieldTableCell.forTableColumn());
        colEmail.setOnEditCommit(event -> {
            Personne personne = event.getRowValue();
            personne.setEmail(event.getNewValue());
        });
        
        // Pour Integer, utiliser IntegerTextFieldTableCell
        colAge.setCellFactory(TextFieldTableCell.forTableColumn(new IntegerStringConverter()));
        colAge.setOnEditCommit(event -> {
            Personne personne = event.getRowValue();
            personne.setAge(event.getNewValue());
        });
        
        // Lier au TableView
        tableViewPersonnes.setItems(personnes);
    }
}
Explication :
• setEditable(true) : Active l'édition sur le TableView
• TextFieldTableCell.forTableColumn() : Crée une cellule éditable pour String
• setOnEditCommit() : Définit l'action à exécuter quand l'édition est terminée
• event.getRowValue() : Récupère l'objet de la ligne modifiée
• event.getNewValue() : Récupère la nouvelle valeur
Exemple TableView - Édition de cellule
Exemple d'application TableView avec édition de cellule : double-clic sur une cellule pour la modifier directement

📝 Types de cellules éditables

JavaFX fournit plusieurs types de cellules éditables :

  • TextFieldTableCell : Pour les String (texte)
  • CheckBoxTableCell : Pour les Boolean (cases Ă  cocher)
  • ChoiceBoxTableCell : Pour les choix dans une liste
  • ComboBoxTableCell : Pour les choix dans une liste dĂ©roulante

Exemple : CheckBoxTableCell pour Boolean

// Si vous avez une propriété Boolean "actif"
@FXML
private TableColumn<Personne, Boolean> colActif;

// Dans initialize()
colActif.setCellValueFactory(new PropertyValueFactory<>("actif"));
colActif.setCellFactory(CheckBoxTableCell.forTableColumn(colActif));
// Pas besoin de setOnEditCommit pour CheckBox (automatique)

📝 Comment éditer une cellule

Pour éditer une cellule, l'utilisateur doit :

  1. Double-cliquer sur la cellule (ou appuyer sur F2)
  2. Modifier la valeur dans le champ de texte
  3. Appuyer sur Entrée pour valider (ou Échap pour annuler)
Important :
  • âś… L'Ă©dition doit ĂŞtre activĂ©e explicitement avec setEditable(true)
  • âś… Chaque colonne doit avoir sa propre CellFactory Ă©ditable
  • âś… Utilisez setOnEditCommit() pour sauvegarder les modifications
  • âś… Pour les types non-String, utilisez un StringConverter appropriĂ©
  • ❌ L'Ă©dition ne fonctionne pas si le modèle n'a pas de setters

💡 Points clés à retenir

  • setEditable(true) : Active l'Ă©dition sur le TableView
  • CellFactory Ă©ditable : DĂ©finit le type de cellule Ă©ditable
  • setOnEditCommit() : Sauvegarde les modifications
  • TextFieldTableCell : Pour les String
  • CheckBoxTableCell : Pour les Boolean
  • StringConverter : Pour convertir les types (Integer, Double, etc.)
  • Optionnel : Utilisez seulement si nĂ©cessaire
Conseil : L'édition de cellule est pratique pour des modifications rapides, mais pour des modifications complexes, utilisez plutôt un formulaire séparé. Testez bien l'expérience utilisateur avant de l'activer partout !

6.3.5 – TableView avec MySQL (CRUD complet)

Dans cette section, vous allez apprendre à intégrer un TableView avec une base de données MySQL pour créer une application CRUD complète (Create, Read, Update, Delete). Vous découvrirez comment charger les données depuis MySQL, ajouter, modifier et supprimer des enregistrements, tout en gardant la synchronisation entre le TableView et la base de données.

🎯 Objectifs de cette section

  • Connexion MySQL : Configurer la connexion Ă  une base de donnĂ©es MySQL
  • Chargement des donnĂ©es : Charger les donnĂ©es depuis MySQL dans le TableView
  • CRUD complet : ImplĂ©menter toutes les opĂ©rations (Create, Read, Update, Delete)
  • Synchronisation : Maintenir la synchronisation entre TableView et base de donnĂ©es
  • Édition en cellule : Permettre l'Ă©dition directe dans le tableau avec sauvegarde automatique
Application JavaFX - TableView avec MySQL CRUD
Application JavaFX avec TableView connectée à MySQL : affichage des données, édition en cellule et formulaire modal pour modifier une personne

📝 Configuration de la base de données

Avant de commencer, vous devez configurer votre base de données MySQL :

1. Créer la base de données et la table

Base de données MySQL - Table personne dans phpMyAdmin
Vue de la table personne dans phpMyAdmin avec les données d'exemple
-- Création de la base de données
CREATE DATABASE IF NOT EXISTS db_javafx;

-- Utilisation de la base de données
USE db_javafx;

-- Création de la table personne
CREATE TABLE IF NOT EXISTS personne (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nom VARCHAR(100) NOT NULL,
    prenom VARCHAR(100) NOT NULL,
    age INT NOT NULL CHECK (age >= 0 AND age <= 150)
);

-- Insertion de données d'exemple
INSERT INTO personne (nom, prenom, age) VALUES
('Dupont', 'Jean', 25),
('Martin', 'Marie', 30),
('Bernard', 'Pierre', 22);

2. Classe de connexion à la base de données

package application;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DbConnexion {
    
    private static final String DB_URL = "jdbc:mysql://localhost:3306/db_javafx";
    private static final String DB_USER = "root";
    private static final String DB_PASSWORD = "";
    
    private static Connection connection = null;
    
    // Obtient une connexion à la base de données (singleton)
    public static Connection getConnection() throws SQLException {
        if (connection == null || connection.isClosed()) {
            try {
                Class.forName("com.mysql.cj.jdbc.Driver");
                connection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
            } catch (ClassNotFoundException e) {
                throw new SQLException("Driver MySQL non trouvé", e);
            }
        }
        return connection;
    }
    
    // Ferme la connexion
    public static void closeConnection() throws SQLException {
        if (connection != null && !connection.isClosed()) {
            connection.close();
        }
    }
}
Configuration importante :
  • âś… TĂ©lĂ©chargez le driver MySQL Connector/J (mysql-connector-java-8.x.x.jar)
  • âś… Ajoutez le driver au classpath du projet (dans le dossier libs/)
  • âś… Modifiez les paramètres de connexion (URL, USER, PASSWORD) selon votre configuration
  • âś… Le port par dĂ©faut de MySQL est 3306 (modifiez si nĂ©cessaire)

📝 Modèle avec ID

Le modèle doit inclure un ID pour identifier chaque enregistrement dans la base de données :

package application;

public class Personne {
    
    private int id;
    private String nom;
    private String prenom;
    private int age;
    
    public Personne() {
        this.id = 0;
        this.nom = "";
        this.prenom = "";
        this.age = 0;
    }
    
    public Personne(int id, String nom, String prenom, int age) {
        this.id = id;
        this.nom = nom;
        this.prenom = prenom;
        this.age = age;
    }
    
    // Getters et Setters
    public int getId() { return id; }
    public void setId(int 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 int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
}

📝 CRUD complet dans le contrôleur

1. Charger les données (Read)

// Charge toutes les personnes depuis la base de données
private void loadPersonnesFromDB() {
    String sql = "SELECT id, nom, prenom, age FROM personne ORDER BY id";
    
    try (Connection conn = DbConnexion.getConnection();
         Statement stmt = conn.createStatement();
         ResultSet rs = stmt.executeQuery(sql)) {
        
        personnes.clear();
        
        while (rs.next()) {
            int id = rs.getInt("id");
            String nom = rs.getString("nom");
            String prenom = rs.getString("prenom");
            int age = rs.getInt("age");
            
            personnes.add(new Personne(id, nom, prenom, age));
        }
        
    } catch (SQLException e) {
        showError("Erreur de chargement", 
            "Impossible de charger les données depuis la base de données.\n" + e.getMessage());
        e.printStackTrace();
    }
}

2. Ajouter une personne (Create)

// Insère une nouvelle personne dans la base de données
// L'ID est généré automatiquement par MySQL (AUTO_INCREMENT)
private void insertPersonneInDB(Personne personne) {
    String sql = "INSERT INTO personne (nom, prenom, age) VALUES (?, ?, ?)";
    
    try (Connection conn = DbConnexion.getConnection();
         PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
        
        pstmt.setString(1, personne.getNom());
        pstmt.setString(2, personne.getPrenom());
        pstmt.setInt(3, personne.getAge());
        
        int affectedRows = pstmt.executeUpdate();
        
        if (affectedRows > 0) {
            ResultSet generatedKeys = pstmt.getGeneratedKeys();
            if (generatedKeys.next()) {
                // Récupère l'ID auto-généré par MySQL
                personne.setId(generatedKeys.getInt(1));
            }
        }
        
    } catch (SQLException e) {
        showError("Erreur d'insertion", 
            "Impossible d'ajouter la personne dans la base de données.\n" + e.getMessage());
        e.printStackTrace();
    }
}

3. Modifier une personne (Update)

// Met à jour une personne dans la base de données
private void updatePersonneInDB(Personne personne) {
    String sql = "UPDATE personne SET nom = ?, prenom = ?, age = ? WHERE id = ?";
    
    try (Connection conn = DbConnexion.getConnection();
         PreparedStatement pstmt = conn.prepareStatement(sql)) {
        
        pstmt.setString(1, personne.getNom());
        pstmt.setString(2, personne.getPrenom());
        pstmt.setInt(3, personne.getAge());
        pstmt.setInt(4, personne.getId());
        
        pstmt.executeUpdate();
        
    } catch (SQLException e) {
        showError("Erreur de mise Ă  jour", 
            "Impossible de mettre à jour la personne dans la base de données.\n" + e.getMessage());
        e.printStackTrace();
        loadPersonnesFromDB(); // Recharger en cas d'erreur
    }
}

4. Supprimer une personne (Delete)

// Supprime une personne de la base de données
private void deletePersonneFromDB(int id) {
    String sql = "DELETE FROM personne WHERE id = ?";
    
    try (Connection conn = DbConnexion.getConnection();
         PreparedStatement pstmt = conn.prepareStatement(sql)) {
        
        pstmt.setInt(1, id);
        pstmt.executeUpdate();
        
    } catch (SQLException e) {
        showError("Erreur de suppression", 
            "Impossible de supprimer la personne de la base de données.\n" + e.getMessage());
        e.printStackTrace();
    }
}

📝 Configuration du TableView avec édition

Le TableView est configuré avec édition de cellule et synchronisation automatique avec la base de données :

@FXML
private void initialize() {
    personnes = FXCollections.observableArrayList();
    
    // Configuration des colonnes avec PropertyValueFactory
    colonneId.setCellValueFactory(new PropertyValueFactory<>("id"));
    colonneNom.setCellValueFactory(new PropertyValueFactory<>("nom"));
    colonnePrenom.setCellValueFactory(new PropertyValueFactory<>("prenom"));
    colonneAge.setCellValueFactory(new PropertyValueFactory<>("age"));
    
    // Édition en cellule : double-clic sur une cellule pour éditer
    tablePersonnes.setEditable(true);
    
    colonneNom.setCellFactory(TextFieldTableCell.forTableColumn());
    colonneNom.setOnEditCommit(event -> {
        Personne personne = event.getRowValue();
        personne.setNom(event.getNewValue());
        updatePersonneInDB(personne); // Synchronisation avec la BDD
    });
    
    colonnePrenom.setCellFactory(TextFieldTableCell.forTableColumn());
    colonnePrenom.setOnEditCommit(event -> {
        Personne personne = event.getRowValue();
        personne.setPrenom(event.getNewValue());
        updatePersonneInDB(personne); // Synchronisation avec la BDD
    });
    
    // Édition de l'âge avec validation
    colonneAge.setCellFactory(TextFieldTableCell.forTableColumn(new IntegerStringConverter()));
    colonneAge.setOnEditCommit(event -> {
        Personne personne = event.getRowValue();
        try {
            int newAge = event.getNewValue();
            if (newAge >= 0 && newAge <= 150) {
                personne.setAge(newAge);
                updatePersonneInDB(personne); // Synchronisation avec la BDD
            } else {
                showError("Erreur", "L'âge doit être entre 0 et 150.");
                tablePersonnes.refresh();
            }
        } catch (Exception e) {
            showError("Erreur", "L'âge doit être un nombre valide.");
            tablePersonnes.refresh();
        }
    });
    
    tablePersonnes.setItems(personnes);
    
    // Charger les données depuis la base de données
    loadPersonnesFromDB();
}

📝 Gestion des boutons CRUD

Bouton Ajouter

@FXML
private void handleAjouter() {
    // Crée une nouvelle personne sans ID (ID sera généré par MySQL)
    Personne nouvellePersonne = new Personne();
    
    boolean okClicked = showPersonneEditDialog(nouvellePersonne, true);
    
    if (okClicked) {
        insertPersonneInDB(nouvellePersonne); // Insère dans la BDD (ID auto-généré)
        personnes.add(nouvellePersonne); // Ajoute à la liste (ID déjà mis à jour)
    }
}

Bouton Modifier

@FXML
private void handleModifier() {
    Personne personneSelectionnee = tablePersonnes.getSelectionModel().getSelectedItem();
    
    if (personneSelectionnee != null) {
        boolean okClicked = showPersonneEditDialog(personneSelectionnee, false);
        if (okClicked) {
            updatePersonneInDB(personneSelectionnee); // Met Ă  jour dans la BDD
            tablePersonnes.refresh();
        }
    } else {
        showWarning("Aucune sélection", "Veuillez sélectionner une personne dans la table.");
    }
}

Bouton Supprimer

@FXML
private void handleSupprimer() {
    int selectedIndex = tablePersonnes.getSelectionModel().getSelectedIndex();
    if (selectedIndex >= 0) {
        Personne personne = tablePersonnes.getSelectionModel().getSelectedItem();
        
        Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
        alert.setTitle("Confirmation");
        alert.setHeaderText("Supprimer la personne");
        alert.setContentText("Êtes-vous sûr de vouloir supprimer " + 
            personne.getNom() + " " + personne.getPrenom() + " ?");
        
        alert.showAndWait().ifPresent(response -> {
            if (response == ButtonType.OK) {
                deletePersonneFromDB(personne.getId()); // Supprime de la BDD
                personnes.remove(selectedIndex);
            }
        });
    } else {
        showWarning("Aucune sélection", "Veuillez sélectionner une personne dans la table.");
    }
}

💡 Points clés à retenir

  • Connexion MySQL : Utilisez JDBC avec le driver MySQL Connector/J
  • PreparedStatement : Utilisez PreparedStatement pour Ă©viter les injections SQL
  • ID auto-gĂ©nĂ©rĂ© : Utilisez AUTO_INCREMENT dans MySQL et rĂ©cupĂ©rez l'ID avec getGeneratedKeys()
  • Synchronisation : Chaque modification dans le TableView doit ĂŞtre synchronisĂ©e avec la BDD
  • Édition en cellule : Utilisez setOnEditCommit() pour sauvegarder automatiquement
  • Gestion d'erreurs : Affichez des messages d'erreur clairs en cas de problème
  • Try-with-resources : Utilisez try-with-resources pour fermer automatiquement les connexions
Conseil : Pour une application professionnelle, considérez l'utilisation d'un pattern DAO (Data Access Object) pour séparer la logique d'accès aux données du contrôleur. Cela améliore la maintenabilité et la testabilité du code.
Important - Configuration du module :
Assurez-vous que votre fichier module-info.java inclut les modules nécessaires :

module Chapitre6_3_5_TableViewMySql {
requires javafx.controls;
requires javafx.fxml;
requires java.sql;

opens application to javafx.graphics, javafx.fxml, javafx.base;
}