TP4.1 - Intégration PostgreSQL avec Jakarta Persistence
Migration vers une Base de Données de Production
Objectifs
Ce travail pratique vous permettra de:
- Intégrer PostgreSQL avec Spring Boot
- Utiliser Jakarta Persistence (JPA) pour la persistance des données
- Configurer Hibernate comme ORM
- Créer et gérer une base de données relationnelle
- Comprendre la différence entre Jakarta et Javax Persistence
- Maîtriser la configuration avec application.properties et application.yml
Table des Matières
- Rappel Rapide
- Ajout de la Base de Données
- Test avec PostgreSQL
- Exercice 1: Javax Persistence
- Exercice 2: Configuration YAML
- Exercice 3: Lombok
- Exercice 4: Recréation Automatique
- Exercice 5: Autre Base de Données
1. Rappel Rapide
Contexte
Dans les TPs précédents, nous avons créé une application CRUD complète avec H2. Maintenant, nous allons migrer vers PostgreSQL, une base de données relationnelle professionnelle adaptée à la production.
Objectifs de ce TP
- Migrer de H2 vers PostgreSQL - Passer d'une base embarquée à une base de production
- Utiliser Jakarta Persistence (JPA) - Mapper les objets Java vers les tables SQL
- Configurer PostgreSQL - Installer et configurer une base de données professionnelle
Prérequis
- TP3 (Architecture CRUD) complété
- PostgreSQL installé (version 12 ou supérieure)
- pgAdmin ou DBeaver pour gérer PostgreSQL
- Visual Studio Code avec extensions Java
2. Ajout de la Base de Données
2.1 Ajout des Dépendances dans pom.xml
Ouvrez votre fichier pom.xml et ajoutez les deux dépendances suivantes dans la section <dependencies>:
<!-- Driver PostgreSQL -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Spring Data JPA (inclut Jakarta Persistence) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Explication:
| Dépendance | Rôle |
|---|---|
postgresql | Driver JDBC pour se connecter à PostgreSQL |
spring-boot-starter-data-jpa | Inclut JPA, Hibernate, et Spring Data |
Note: Après l'ajout, VS Code vous proposera de recharger le projet Maven. Acceptez ou exécutez:
mvn clean install -DskipTests
2.2 Configuration de application.properties
Créez ou modifiez le fichier src/main/resources/application.properties:
# URL de connexion à la base de données
spring.datasource.url=jdbc:postgresql://localhost:5432/greeting_db?useSSL=false
# Credentials de connexion
spring.datasource.username=postgres
spring.datasource.password=postgres
# Configuration Hibernate
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
# Stratégie de génération du schéma
spring.jpa.hibernate.ddl-auto=create
# Afficher les requêtes SQL (utile pour le développement)
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
Explication des propriétés:
| Propriété | Description | Valeurs possibles |
|---|---|---|
spring.datasource.url | URL de connexion JDBC | Format: jdbc:postgresql://host:port/database |
spring.datasource.username | Nom d'utilisateur PostgreSQL | Par défaut: postgres |
spring.datasource.password | Mot de passe PostgreSQL | À adapter selon votre installation |
spring.jpa.hibernate.ddl-auto | Stratégie de création du schéma | none, validate, update, create, create-drop |
spring.jpa.show-sql | Afficher les requêtes SQL dans la console | true ou false |
Valeurs de ddl-auto:
Important: Changez greeting_db par le nom de votre base de données si nécessaire.
2.3 Annotation de l'Entité Greeting.java
Modifiez la classe Greeting.java pour en faire une entité JPA:
package com.example.demo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "greeting")
public class Greeting {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "content", nullable = false)
private String content;
// Constructeur par défaut (requis par JPA)
public Greeting() {
}
// Constructeur avec paramètres
public Greeting(Long id, String content) {
this.id = id;
this.content = content;
}
// Getters et Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
Explication des annotations:
| Annotation | Rôle |
|---|---|
@Entity | Marque la classe comme entité JPA (mappée vers une table) |
@Table(name = "greeting") | Spécifie le nom de la table SQL |
@Id | Marque le champ comme clé primaire |
@GeneratedValue(strategy = GenerationType.IDENTITY) | Auto-incrémentation de l'ID |
@Column(name = "content", nullable = false) | Configuration de la colonne SQL |
Architecture JPA:
2.4 Création du Repository
Créez une interface GreetingRepository.java dans le package com.example.demo:
package com.example.demo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface GreetingRepository extends JpaRepository<Greeting, Long> {
// Spring Data JPA génère automatiquement l'implémentation
// Méthodes disponibles: save(), findAll(), findById(), delete(), etc.
}
Méthodes générées automatiquement:
| Méthode | Description |
|---|---|
save(Greeting g) | Sauvegarder ou mettre à jour |
findAll() | Récupérer toutes les entrées |
findById(Long id) | Récupérer par ID |
deleteById(Long id) | Supprimer par ID |
count() | Compter les entrées |
2.5 Modification du Controller
Modifiez GreetingController.java pour utiliser le repository:
package com.example.demo;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
private final GreetingRepository greetingRepository;
public GreetingController(GreetingRepository greetingRepository) {
this.greetingRepository = greetingRepository;
}
@GetMapping("/greeting")
public Greeting greeting(@RequestParam(value="name") String name) {
Greeting greeting = new Greeting(
counter.incrementAndGet(),
String.format(template, name)
);
// Sauvegarder dans la base de données
return greetingRepository.save(greeting);
}
}
Flux de données:
2.6 Création de la Base de Données PostgreSQL
Option A: Avec pgAdmin
-
Ouvrir pgAdmin
-
Créer un utilisateur (optionnel):
- Clic droit sur "Login/Group Roles"
- "Create" → "Login/Group Role"
- Onglet "General": Name =
postgres - Onglet "Definition": Password =
postgres - Onglet "Privileges": Cocher "Can login?" et "Create databases?"
- Sauvegarder
-
Créer la base de données:
- Clic droit sur "Databases"
- "Create" → "Database"
- Database name:
greeting_db - Owner:
postgres - Sauvegarder
Option B: Avec SQL (ligne de commande)
-- Créer la base de données
CREATE DATABASE greeting_db;
-- Créer l'utilisateur (si nécessaire)
CREATE USER postgres WITH PASSWORD 'postgres';
-- Accorder les privilèges
GRANT ALL PRIVILEGES ON DATABASE greeting_db TO postgres;
Pour exécuter ces commandes:
# Windows
psql -U postgres
# macOS/Linux
sudo -u postgres psql
3. Test avec PostgreSQL
3.1 Vérification de la Configuration
Avant de lancer l'application, vérifiez:
Checklist:
- PostgreSQL est démarré
- La base de données
greeting_dbexiste - L'utilisateur
postgresa les bons privilèges - Le fichier
application.propertiesest correct - La classe
Greetinga l'annotation@Entity - L'interface
GreetingRepositoryest créée - Le
GreetingControllerutilise le repository
3.2 Lancement de l'Application
Dans VS Code:
Méthode 1: Via le Terminal
# Nettoyer et compiler
mvn clean install
# Lancer l'application
mvn spring-boot:run
Méthode 2: Via l'Interface
- Ouvrir
GreetingApplication.java - Cliquer sur "Run" au-dessus de la méthode
main - Ou appuyer sur F5
Logs attendus:
Hibernate: drop table if exists greeting cascade
Hibernate: create table greeting (id bigserial not null, content varchar(255) not null, primary key (id))
Started GreetingApplication in X.XXX seconds
3.3 Test des Endpoints
Test 1: Créer un greeting
http://localhost:8080/greeting?name=John
Réponse attendue:
{
"id": 1,
"content": "Hello, John!"
}
Test 2: Créer plusieurs greetings
http://localhost:8080/greeting?name=Alice
http://localhost:8080/greeting?name=Bob
http://localhost:8080/greeting?name=Charlie
3.4 Vérification dans PostgreSQL
Avec pgAdmin:
- Ouvrir pgAdmin
- Naviguer: Servers → PostgreSQL → Databases → greeting_db → Schemas → public → Tables
- Clic droit sur "greeting" → "View/Edit Data" → "All Rows"
Avec SQL:
-- Se connecter à la base
\c greeting_db
-- Afficher toutes les entrées
SELECT * FROM greeting;
-- Résultat attendu:
-- id | content
-- ---+----------------
-- 1 | Hello, John!
-- 2 | Hello, Alice!
-- 3 | Hello, Bob!
-- 4 | Hello, Charlie!
3.5 Logs SQL dans la Console
Grâce à spring.jpa.show-sql=true, vous verrez les requêtes SQL dans la console:
Hibernate: insert into greeting (content) values (?)
4. Exercice 1: Refaire avec Javax Persistence
Énoncé
Refaites le projet en utilisant Javax Persistence au lieu de Jakarta Persistence (ancienne version de JPA).
Objectif
Comprendre la différence entre Jakarta et Javax Persistence.
Contexte Historique
Instructions
Étape 1: Modifier pom.xml
Ajoutez la dépendance Javax Persistence:
<!-- Ajout de Javax Persistence -->
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
Étape 2: Modifier les imports dans Greeting.java
Remplacez:
import jakarta.persistence.*;
Par:
import javax.persistence.*;
Étape 3: Classe complète avec Javax
package com.example.demo;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "greeting")
public class Greeting {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "content", nullable = false)
private String content;
public Greeting() {}
public Greeting(Long id, String content) {
this.id = id;
this.content = content;
}
// Getters et Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
Étape 4: Tester
- Recompilez:
mvn clean install -DskipTests - Relancez l'application
- Vérifiez que tout fonctionne
Questions de Réflexion
- Quelle est la différence majeure entre Javax et Jakarta Persistence?
- Pourquoi Jakarta est-il recommandé pour les nouveaux projets?
- Peut-on mélanger les deux dans le même projet?
5. Exercice 2: Configuration avec application.yml
Énoncé
Convertissez votre configuration application.properties en format YAML.
Objectif
Maîtriser le format YAML pour la configuration Spring Boot.
Instructions
Étape 1: Supprimer application.properties
Supprimez ou renommez le fichier application.properties.
Étape 2: Créer application.yml
Créez src/main/resources/application.yml:
spring:
datasource:
url: jdbc:postgresql://localhost:5432/greeting_db?useSSL=false
username: postgres
password: postgres
jpa:
hibernate:
ddl-auto: create
show-sql: true
properties:
hibernate:
jdbc:
lob:
non_contextual_creation: true
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
Étape 3: Tester
Relancez l'application et vérifiez que tout fonctionne.
Comparaison
application.properties:
spring.datasource.url=jdbc:postgresql://localhost:5432/greeting_db
spring.datasource.username=postgres
spring.jpa.hibernate.ddl-auto=create
application.yml:
spring:
datasource:
url: jdbc:postgresql://localhost:5432/greeting_db
username: postgres
jpa:
hibernate:
ddl-auto: create
Avantages du YAML:
- Plus lisible pour les structures hiérarchiques
- Moins de répétition
- Format moderne
6. Exercice 3: Simplification avec Lombok
Énoncé
Supprimez les getters, setters et constructeurs manuels en utilisant Lombok.
Objectif
Réduire le code boilerplate avec Lombok.
Prérequis
Étape 1: Ajouter la dépendance Lombok dans pom.xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
Étape 2: Installer l'extension Lombok dans VS Code
- Extensions → Rechercher "Lombok"
- Installer "Lombok Annotations Support for VS Code"
Instructions
Modifier Greeting.java:
package com.example.demo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "greeting")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Greeting {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "content", nullable = false)
private String content;
// Lombok génère automatiquement:
// - Getters
// - Setters
// - toString()
// - equals() et hashCode()
// - Constructeur vide
// - Constructeur avec tous les arguments
}
Annotations Lombok:
| Annotation | Génère |
|---|---|
@Data | Getters, Setters, toString, equals, hashCode |
@NoArgsConstructor | Constructeur vide |
@AllArgsConstructor | Constructeur avec tous les champs |
@Getter | Seulement les getters |
@Setter | Seulement les setters |
Test
- Recompilez
- Vérifiez que l'application fonctionne
- Les méthodes sont générées à la compilation
7. Exercice 4: Recréation Automatique de la Base
Énoncé
Configurez l'application pour qu'elle recrée automatiquement la base de données à chaque démarrage.
Objectif
Comprendre les différentes stratégies de gestion du schéma.
Instructions
Dans application.properties (ou application.yml):
spring.jpa.hibernate.ddl-auto=create-drop
ou
spring:
jpa:
hibernate:
ddl-auto: create-drop
Comparaison des Stratégies
| Stratégie | Comportement | Usage |
|---|---|---|
none | Aucune action | Production |
validate | Valide le schéma sans modifier | Production |
update | Met à jour le schéma | Développement |
create | Crée le schéma au démarrage | Tests |
create-drop | Crée au démarrage, supprime à l'arrêt | Tests unitaires |
Attention
create-drop supprime toutes les données à l'arrêt de l'application!
Usage recommandé:
# Développement
spring:
jpa:
hibernate:
ddl-auto: update
# Production
spring:
jpa:
hibernate:
ddl-auto: validate
8. Exercice 5: Migration vers MySQL, SQL Server ou Oracle
Énoncé
Refaites l'intégration complète avec une autre base de données.
Choix de Base de Données
Option A: MySQL
Dépendance dans pom.xml:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
Configuration application.properties:
spring.datasource.url=jdbc:mysql://localhost:3306/greeting_db?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update
Création de la base dans MySQL:
CREATE DATABASE greeting_db;
CREATE USER 'root'@'localhost' IDENTIFIED BY 'root';
GRANT ALL PRIVILEGES ON greeting_db.* TO 'root'@'localhost';
FLUSH PRIVILEGES;
Option B: SQL Server
Dépendance dans pom.xml:
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency>
Configuration application.properties:
spring.datasource.url=jdbc:sqlserver://localhost:1433;databaseName=greeting_db
spring.datasource.username=sa
spring.datasource.password=YourStrong@Password
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServerDialect
spring.jpa.hibernate.ddl-auto=update
Création de la base dans SQL Server:
CREATE DATABASE greeting_db;
GO
ALTER LOGIN sa WITH PASSWORD = 'YourStrong@Password';
GO
USE greeting_db;
GO
Option C: Oracle
Dépendance dans pom.xml:
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<scope>runtime</scope>
</dependency>
Configuration application.properties:
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:xe
spring.datasource.username=system
spring.datasource.password=oracle
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle12cDialect
spring.jpa.hibernate.ddl-auto=update
Création de la base dans Oracle:
CREATE USER greeting_user IDENTIFIED BY oracle;
GRANT ALL PRIVILEGES TO greeting_user;
Tableau Comparatif
| Base de Données | Port par Défaut | Dialect Hibernate | Difficulté |
|---|---|---|---|
| PostgreSQL | 5432 | PostgreSQLDialect | Facile |
| MySQL | 3306 | MySQLDialect | Facile |
| SQL Server | 1433 | SQLServerDialect | Moyenne |
| Oracle | 1521 | Oracle12cDialect | Difficile |
Instructions Générales
- Installer la base de données choisie
- Modifier pom.xml avec le bon driver
- Modifier application.properties avec la bonne configuration
- Créer la base de données et l'utilisateur
- Recompiler et tester
Résolution de Problèmes
Problème 1: Connection Refused
Symptôme:
org.postgresql.util.PSQLException: Connection to localhost:5432 refused
Solutions:
- Vérifier que PostgreSQL est démarré:
Windows:
# Dans Services, chercher PostgreSQL
services.msc
Linux:
sudo systemctl status postgresql
sudo systemctl start postgresql
- Vérifier le port dans
postgresql.conf
Problème 2: Authentication Failed
Symptôme:
PSQLException: FATAL: password authentication failed for user "postgres"
Solutions:
- Vérifier le mot de passe dans
application.properties - Réinitialiser le mot de passe:
ALTER USER postgres WITH PASSWORD 'nouveau_mot_de_passe';
Problème 3: Database Does Not Exist
Symptôme:
PSQLException: FATAL: database "greeting_db" does not exist
Solution:
Créer la base de données:
CREATE DATABASE greeting_db;
Problème 4: Table Already Exists
Symptôme:
Table "greeting" already exists
Solution:
Changer la stratégie DDL:
spring.jpa.hibernate.ddl-auto=update
Problème 5: Lombok Ne Fonctionne Pas
Symptôme:
error: cannot find symbol
method getId()
Solutions:
- Installer l'extension Lombok dans VS Code
- Redémarrer VS Code
- Vérifier que la dépendance est dans pom.xml
- Recompiler:
mvn clean install -DskipTests spring-boot:run
Questions de Révision
-
Quelle est la différence entre Jakarta et Javax Persistence?
-
Que fait l'annotation @Entity?
-
Quelle est la différence entre create, update et validate pour ddl-auto?
-
Comment Spring Data JPA génère-t-il l'implémentation du repository?
-
Que fait l'annotation @GeneratedValue(strategy = GenerationType.IDENTITY)?
-
Pourquoi doit-on avoir un constructeur vide dans une entité JPA?
-
Que génère l'annotation @Data de Lombok?
-
Quelle configuration utiliser en production pour ddl-auto?
Livrables
À remettre:
- Code source complet avec PostgreSQL fonctionnel
- Captures d'écran montrant:
- L'application en cours d'exécution
- Les données dans pgAdmin
- Les requêtes SQL dans la console
- Au moins 2 exercices complétés
- Document de réponses aux questions de révision
Critères d'Évaluation
| Critère | Points |
|---|---|
| Intégration PostgreSQL fonctionnelle | 25% |
| Entité Greeting correctement annotée | 20% |
| Repository créé et utilisé | 15% |
| Données persistées dans PostgreSQL | 20% |
| Exercices complétés | 10% |
| Questions de révision répondues | 10% |
| Total | 100% |
Prochaines Étapes
Dans le prochain TP, nous verrons:
- TP3: Création d'une API CRUD complète
- Operations: Create, Read, Update, Delete
- Gestion des erreurs
- Validation des données