Gestion du personnel des ligues sportives — Architecture POO, JDBC, Patron Passerelle.
Projet d'atelier professionnel réalisé en 2ème année de BTS SIO SLAM. L'objectif était de concevoir une application desktop en Java permettant à la M2L (Maison des Ligues) de gérer les ligues sportives qu'elle héberge ainsi que leurs employés, via une interface entièrement en ligne de commande. Les données sont persistées dans une base MySQL via JDBC.
DateInvalide)| Package | Rôle |
|---|---|
personnel | Couche métier : Employe, Ligue, GestionPersonnel, exceptions |
commandLine | Interface console : PersonnelConsole, LigueConsole, EmployeConsole |
jdbc | Persistance MySQL : connexion, CRUD complet via PreparedStatement |
serialisation | Persistance alternative : sérialisation Java binaire |
testsUnitaires | Tests JUnit 5 : testLigue, testEmploye |
L'interface Passerelle découple totalement la logique métier de la persistance.
Basculer entre MySQL et sérialisation ne nécessite qu'un seul changement de constante :
GestionPersonnel.java
public final static int SERIALIZATION = 1, JDBC = 2, TYPE_PASSERELLE = JDBC; // ← changer ici pour switcher private static Passerelle passerelle = TYPE_PASSERELLE == JDBC ? new jdbc.JDBC() : new serialisation.Serialization();
Deux tables reliées par une clé étrangère nullable (id_ligue NULL = employé root sans ligue).
0,n ── Appartient ── 0,1 | id_ligue NULL = root (aucune ligue)
Orchestre toutes les opérations sur ligues et employés. Une seule instance possible.
| Méthode | Retour | Description |
|---|---|---|
getGestionPersonnel() | GestionPersonnel | Retourne l'unique instance (Singleton) |
addLigue(nom) | Ligue | Crée et persiste une ligue |
getLigues() | SortedSet<Ligue> | Toutes les ligues en lecture seule |
getRoot() | Employe | Retourne le super-utilisateur root |
sauvegarder() | void | Délègue la sauvegarde à la passerelle |
delete(Employe) | void | Suppression via la passerelle active |
Contient un ensemble trié d'employés. Le root est administrateur par défaut jusqu'à affectation explicite.
| Méthode | Retour | Description |
|---|---|---|
getNom() / setNom() | String / void | Lecture/écriture du nom, persisté en BDD |
getEmployes() | SortedSet<Employe> | Liste triée alphabétiquement, lecture seule |
addEmploye(...) | Employe | Seul moyen de créer un employé |
setAdministrateur(emp) | void | Change l'admin + met à jour les rôles en BDD |
setAdministrateurFromJDBC() | void | Affecte l'admin au chargement sans appel BDD |
remove() | void | Retire la ligue de GestionPersonnel |
Instanciable uniquement via Ligue.addEmploye(). Gère la validation des dates et la protection du root.
| Méthode | Retour | Description |
|---|---|---|
setDateArrivee(date) | void | Lève DateInvalide si date > aujourd'hui |
setDateDepart(date) | void | Lève DateInvalide si date < dateArrivée |
estAdmin(ligue) | boolean | Vrai si this est l'admin de la ligue donnée |
estRoot() | boolean | Vrai si this est le root |
checkPassword(pwd) | boolean | Compare le mot de passe fourni |
remove() | void | Suppression ; lève ImpossibleDeSupprimerRoot si root |
Employe.java — Validation des dates
public void setDateArrivee(LocalDate dateArrivee) throws DateInvalide { if (dateArrivee != null && dateArrivee.isAfter(LocalDate.now())) throw new DateInvalide("La date d'arrivée ne peut pas être future."); this.dateArrivee = dateArrivee; } public void setDateDepart(LocalDate dateDepart) throws DateInvalide { if (dateDepart != null && dateDepart.isBefore(dateArrivee)) throw new DateInvalide("La date de départ ne peut pas précéder l'arrivée."); this.dateDepart = dateDepart; }
Passerelle.java
public interface Passerelle { GestionPersonnel getGestionPersonnel(); void sauvegarderGestionPersonnel(GestionPersonnel gp) throws SauvegardeImpossible; int insert(Ligue ligue) throws SauvegardeImpossible; int insert(Employe employe) throws SauvegardeImpossible; int update(Ligue ligue) throws SauvegardeImpossible; int update(Employe employe) throws SauvegardeImpossible; int delete(Employe employe) throws SauvegardeImpossible; }
La classe JDBC implémente Passerelle et gère toutes les interactions avec MySQL
via des PreparedStatement. La connexion est configurée dans Credentials.java (non versionné).
SELECT * FROM LIGUE
WHERE id_ligue = ?
WHERE id_ligue IS NULL
JDBC.java — insert(Employe)
public int insert(Employe employe) throws SauvegardeImpossible { PreparedStatement ps = connection.prepareStatement( "INSERT INTO EMPLOYE (nom_employe, prenom_employe, mail_employe," + " password_employe, date_arrivee_employe, date_départ_employe," + " id_ligue, rôle_employe) VALUES(?,?,?,?,?,?,?,?)", Statement.RETURN_GENERATED_KEYS); ps.setString(1, employe.getNom()); ps.setString(2, employe.getPrenom()); ps.setObject(5, employe.getDateArrivee()); ps.setString(6, employe.getDateDepart() != null ? employe.getDateDepart().toString() : null); if (employe.getLigue() != null) ps.setInt(7, employe.getLigue().getId()); else ps.setNull(7, java.sql.Types.INTEGER); // root = pas de ligue ps.setString(8, employe.estAdmin(employe.getLigue()) ? "admin" : "employe"); ps.executeUpdate(); ResultSet id = ps.getGeneratedKeys(); id.next(); return id.getInt(1); // id auto-généré renvoyé à l'objet Java }
JDBC.java — update(Employe)
public int update(Employe employe) throws SauvegardeImpossible { PreparedStatement ps = connection.prepareStatement( "UPDATE EMPLOYE SET nom_employe=?, prenom_employe=?, mail_employe=?," + " password_employe=?, date_arrivee_employe=?, date_départ_employe=?," + " id_ligue=?, rôle_employe=? WHERE id_employe=?"); ps.setObject(5, employe.getDateArrivee()); ps.setString(6, employe.getDateDepart() != null ? employe.getDateDepart().toString() : null); // Rôle recalculé à chaque update (gère le changement d'admin) ps.setString(8, (employe.getLigue() != null && employe.estAdmin(employe.getLigue())) ? "admin" : "employe"); ps.setInt(9, employe.getId()); return ps.executeUpdate(); }
Deux classes de test couvrent les entités métier avec des cas nominaux et des cas d'erreur.
DateInvalide si date arrivée futureDateInvalide si départ avant arrivéecheckPassword() après setPassword()getLigue() retourne la bonne liguetestLigue.java
@Test void ExceptionDateInvalideDA() throws SauvegardeImpossible, DateInvalide { Ligue ligue = gestionPersonnel.addLigue("Fléchettes"); LocalDate dateArrivee = LocalDate.now().plusDays(1); // date future assertThrows(DateInvalide.class, () -> { ligue.addEmploye("Laporte", "Jean", "[email protected]", "pass", dateArrivee, null); // doit lever DateInvalide }); }
Ce projet m'a permis de maîtriser la chaîne complète d'un applicatif Java desktop :
PreparedStatement, gestion des clés auto-générées, requêtes paramétrées.Passerelle.assertThrows.ON DELETE CASCADE à ajouter).