You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Système de Gestion Académique — JavaFX · JDBC · PostgreSQL
Application de bureau pour gérer les filières, les étudiants, les cours, les inscriptions et les dossiers administratifs d'un établissement d'enseignement, avec des règles métier strictes et des transactions explicites.
Création automatique du dossier administratif, dans la même transaction que l'élève.
Inscription à plusieurs cours, limitée aux cours proposés par sa filière ; impossible pour un élève suspendu.
Bouton Dossier : accès direct au dossier administratif de l'élève.
📚 Cours
CRUD complet.
Affectation aux filières depuis la fiche du cours.
🗂️ Dossiers administratifs
Création pour les élèves qui n'en ont pas encore, et modification (N° d'inscription, date).
Un seul dossier par élève, garanti en base et dans l'application.
💡 Expérience utilisateur
Validation des formulaires
Champs obligatoires, longueurs maximales, format de l'email
Alertes claires
Erreurs SQL traduites (doublon, base inaccessible, mot de passe refusé…)
Confirmations
Avant chaque suppression, avec ses conséquences (dossier, inscriptions)
Interface fluide
Requêtes exécutées en arrière-plan : la fenêtre ne se fige jamais
Navigation clavier
La sélection d'une ligne remplit le formulaire, à la souris comme au clavier
📏 Règles métier
#
Règle
Garantie par
1
Matricule élève unique
Contrainte UNIQUE + alerte « valeur déjà existante »
2
Code filière / cours unique
Contraintes UNIQUE
3
Un élève ne suit que des cours proposés par sa filière
Vérification dans la transaction d'inscription ; nettoyage automatique des inscriptions invalides (changement de filière, cours retiré d'une filière)
4
Un dossier administratif unique par élève
UNIQUE (eleve_id) + contrôle dans DossierDAO
5
Suppression interdite d'une filière contenant des élèves
Contrôle dans FiliereDAO + ON DELETE RESTRICT
6
Toute inscription aux cours est transactionnelle
EleveDAO.setEnrollments() dans DBConnection.inTransaction()
★
Statut ACTIF / SUSPENDU ; un élève suspendu ne peut pas être inscrit
Type ENUM student_status + contrôle à l'inscription
★
Affichage du nombre d'élèves par filière
LEFT JOIN + COUNT (filières et tableau de bord)
🏛️ Architecture
L'application suit une architecture MVC en couches : chaque couche ne parle qu'à la couche voisine.
flowchart TB
subgraph V["🎨 Vue — FXML + CSS"]
direction LR
LV[login-view] --- DV[dashboard] --- MV["student · filiere · cours · dossier · stats"]
end
subgraph C["🎮 Contrôleurs"]
direction LR
LC[LoginController] --- DC[DashboardController] --- MC["Student · Filiere · Cours<br/>Dossier · Stats Controllers"]
end
subgraph U["🧰 Utilitaires"]
direction LR
AS[Async] --- AL[Alerts] --- VA[Validator] --- CF[AppConfig]
end
subgraph D["🗄️ DAO — JDBC"]
direction LR
ED[EleveDAO] --- FD[FiliereDAO] --- CD[CoursDAO] --- DD[DossierDAO]
end
subgraph M["🧩 Modèle"]
direction LR
E[Eleve] --- F[Filiere] --- CO[Cours] --- DA[DossierAdministratif]
end
DB[("🐘 PostgreSQL<br/>gestion_academique")]
V -->|événements| C
C -->|validation · alertes · arrière-plan| U
C -->|appels| D
D -->|objets| M
D -->|"PreparedStatement · JOIN · transactions"| CX[DBConnection]
CX --> DB
Loading
Principes clés
Principe
Mise en œuvre
JDBC pur
PreparedStatement partout, aucune requête construite par concaténation de valeurs
Requêtes JOIN
Nom de filière des élèves, élèves des dossiers, nombre d'élèves par filière, cours disponibles pour un élève
Transactions explicites
DBConnection.inTransaction() : setAutoCommit(false) → commit(), ou rollback() en cas d'erreur
Connexion partagée
Une seule connexion JDBC, réouverte automatiquement si elle tombe
UI non bloquante
Async exécute les requêtes sur un unique thread d'arrière-plan (pas d'accès concurrent à la connexion) puis met à jour l'UI sur le thread JavaFX
Règles métier explicites
Une violation lève une BusinessRuleException, affichée telle quelle à l'utilisateur
Exemple : ajout d'un étudiant (transaction)
sequenceDiagram
actor A as Administrateur
participant SC as StudentController
participant AS as Async (thread DB)
participant ED as EleveDAO
participant DD as DossierDAO
participant DB as PostgreSQL
A->>SC: Clic « AJOUTER »
SC->>SC: Validator (champs, email, filière)
SC->>AS: addWithDossier(eleve)
AS->>ED: inTransaction
ED->>DB: BEGIN
ED->>DB: INSERT INTO eleve
ED->>DD: add(dossier « INS-id »)
DD->>DB: INSERT INTO dossier_administratif
alt succès
ED->>DB: COMMIT
AS-->>SC: dossier créé
SC-->>A: ✅ « Étudiant ajouté, dossier N° INS-… »
else erreur (doublon, contrainte…)
ED->>DB: ROLLBACK (ni élève, ni dossier)
AS-->>SC: exception
SC-->>A: ❌ Alerte explicite
end
erDiagram
FILIERE ||--o{ ELEVE : "regroupe"
ELEVE ||--o| DOSSIER_ADMINISTRATIF : "possède"
FILIERE ||--o{ FILIERE_COURS : "propose"
COURS ||--o{ FILIERE_COURS : "est proposé"
ELEVE ||--o{ ELEVE_COURS : "s'inscrit"
COURS ||--o{ ELEVE_COURS : "accueille"
FILIERE {
int id PK
varchar code UK
varchar nom
text description
}
ELEVE {
int id PK
varchar matricule UK
varchar nom
varchar prenom
varchar email UK
int filiere_id FK
student_status status "ACTIVE | SUSPENDED"
}
DOSSIER_ADMINISTRATIF {
int id PK
varchar numero_inscription UK
date date_creation
int eleve_id FK,UK
}
COURS {
int id PK
varchar code UK
varchar intitule
}
FILIERE_COURS {
int filiere_id PK,FK
int cours_id PK,FK
}
ELEVE_COURS {
int eleve_id PK,FK
int cours_id PK,FK
}
Loading
Clé étrangère
Comportement à la suppression
eleve.filiere_id → filiere
RESTRICT : pas de suppression d'une filière qui a des élèves
dossier_administratif.eleve_id → eleve
CASCADE : le dossier disparaît avec l'élève
filiere_cours, eleve_cours
CASCADE : les associations disparaissent avec la filière, le cours ou l'élève
config.properties est ignoré par Git : les mots de passe ne sont jamais commités.
Chaque clé peut aussi être fournie par une variable d'environnement, qui est alors prioritaire (db.password → DB_PASSWORD, app.admin.password → APP_ADMIN_PASSWORD).
3. Lancer
./mvnw clean javafx:run # Windows : mvnw.cmd clean javafx:run
4. Tests
./mvnw test
🛠️ Choix techniques
Domaine
Choix
Pourquoi
Langage
Java 17 (LTS)
Robustesse, record, support long terme
Interface
JavaFX 17 + FXML + CSS
Vues déclaratives séparées de la logique, thème personnalisé
Base de données
PostgreSQL
Types ENUM, clés étrangères, ON DELETE RESTRICT / CASCADE, SELECT … FOR UPDATE
Persistance
JDBC natif
Maîtrise totale des requêtes, des jointures et des transactions
Modularité
JPMS (module-info.java)
Dépendances et accès réflexifs explicites
Build & tests
Maven + JUnit 5
Cycle de build standard, tests automatisés
🧗 Difficultés rencontrées
Type ENUM PostgreSQL : le type student_status impose CAST(? AS student_status) dans les requêtes JDBC.
Atomicité élève + dossier : la création d'un élève et de son dossier doit réussir ou échouer d'un bloc. D'où une méthode inTransaction() que partagent tous les DAO grâce à la connexion commune.
Cohérence des inscriptions : changer la filière d'un élève, ou retirer un cours d'une filière, peut rendre des inscriptions invalides. Elles sont supprimées dans la même transaction.
Fluidité de l'interface : exécuter les requêtes sur le thread JavaFX figeait la fenêtre. Elles passent désormais par un thread d'arrière-plan unique, qui évite aussi les accès concurrents à la connexion partagée.
Expérience « premium » : un thème CSS personnalisé et des alertes qui expliquent l'erreur au lieu d'afficher un message SQL brut.
👤 Auteur
EL HAIL JAOUAD
Mini-projet JavaFX + JDBC — ENSA Tanger
About
Application desktop de gestion académique (étudiants, filières, cours, dossiers). Inscriptions transactionnelles, règles métier avancées, UI asynchrone non bloquante et design Modern Dark. Développée avec Java 17, JavaFX, JDBC natif & PostgreSQL.