Aller au contenu

Accès à PostgreSQL en Java : JDBC DAO/Singleton et Hibernate sans Spring Boot

Support de cours adapté à PostgreSQL avec :

  1. une version Java pur + JDBC + Maven + DAO + Singleton
  2. une version Hibernate sans Spring Boot + Maven avec les patterns utiles

Exemple métier utilisé : Pilote


Objectifs

À la fin de ce support, vous saurez :


1) Version Java pur + JDBC + Maven + DAO + Singleton

1.1 Architecture conseillée

pilote-jdbc-postgresql/
├── pom.xml
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── fr/bouget/pilote/
│   │   │       ├── App.java
│   │   │       ├── dao/
│   │   │       │   ├── PiloteDao.java
│   │   │       │   └── PiloteDaoJdbc.java
│   │   │       ├── model/
│   │   │       │   └── Pilote.java
│   │   │       └── util/
│   │   │           └── ConnectionSingleton.java
│   │   └── resources/
│   │       └── schema.sql
│   └── test/
│       └── java/
└── README.md

1.2 Dépendances Maven

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>fr.bouget</groupId>
    <artifactId>pilote-jdbc-postgresql</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- Driver JDBC PostgreSQL -->
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.7.8</version>
        </dependency>
    </dependencies>

</project>

Pourquoi cette dépendance ?

Le driver PostgreSQL permet à Java de dialoguer avec PostgreSQL via JDBC.


1.3 Script SQL PostgreSQL

PostgreSQL n’utilise pas AUTO_INCREMENT comme MySQL.
En PostgreSQL moderne, on utilise généralement GENERATED ALWAYS AS IDENTITY.

CREATE TABLE IF NOT EXISTS pilote (
    id   INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    nom  VARCHAR(50) NOT NULL,
    site VARCHAR(50) NOT NULL
);

INSERT INTO pilote (nom, site) VALUES
('SERGE', 'NICE'),
('JEAN', 'PARIS'),
('CLAUDINE', 'GRENOBLE'),
('ROBERT', 'NANTES'),
('MICHEL', 'PARIS'),
('LUCIENNE', 'TOULOUSE'),
('BERTRAND', 'LYON'),
('HERVE', 'BASTIA'),
('LUC', 'PARIS'),
('GASPARD', 'PARIS'),
('ELODIE', 'BREST');

1.4 Classe métier Pilote

package fr.bouget.pilote.model;

public class Pilote {

    private Integer id;
    private String nom;
    private String site;

    public Pilote() {
    }

    public Pilote(Integer id, String nom, String site) {
        this.id = id;
        this.nom = nom != null ? nom.toUpperCase() : null;
        this.site = site != null ? site.toUpperCase() : null;
    }

    public Pilote(String nom, String site) {
        this(null, nom, site);
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getNom() {
        return nom;
    }

    public void setNom(String nom) {
        this.nom = nom != null ? nom.toUpperCase() : null;
    }

    public String getSite() {
        return site;
    }

    public void setSite(String site) {
        this.site = site != null ? site.toUpperCase() : null;
    }

    @Override
    public String toString() {
        return "Pilote{id=" + id + ", nom='" + nom + "', site='" + site + "'}";
    }
}

1.5 Pattern Singleton pour la connexion JDBC

Pourquoi un Singleton ici ?

L’idée est de centraliser la configuration de connexion dans une seule classe.

Attention : dans une vraie application multi-utilisateur ou complexe, on évite souvent de conserver une seule connexion partagée trop longtemps.
Le plus propre consiste généralement à centraliser la configuration, puis ouvrir/fermer proprement les connexions selon le besoin.

Ici, pour un support pédagogique simple, le Singleton est pratique.

package fr.bouget.pilote.util;

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

public class ConnectionSingleton {

    private static final String URL = "jdbc:postgresql://localhost:5432/bd_avion";
    private static final String USER = "test";
    private static final String PASSWORD = "test";

    private static ConnectionSingleton instance;
    private Connection connection;

    private ConnectionSingleton() {
        try {
            this.connection = DriverManager.getConnection(URL, USER, PASSWORD);
        } catch (SQLException e) {
            throw new RuntimeException("Impossible de créer la connexion PostgreSQL", e);
        }
    }

    public static synchronized ConnectionSingleton getInstance() {
        if (instance == null) {
            instance = new ConnectionSingleton();
        }
        return instance;
    }

    public Connection getConnection() {
        try {
            if (connection == null || connection.isClosed()) {
                connection = DriverManager.getConnection(URL, USER, PASSWORD);
            }
        } catch (SQLException e) {
            throw new RuntimeException("Impossible de récupérer la connexion PostgreSQL", e);
        }
        return connection;
    }
}

1.6 Interface DAO

Le pattern DAO (Data Access Object) permet d’isoler les accès à la base de données du reste du programme.

package fr.bouget.pilote.dao;

import fr.bouget.pilote.model.Pilote;

import java.util.List;
import java.util.Optional;

public interface PiloteDao {

    Optional<Pilote> findById(int id);

    Optional<Pilote> findByNom(String nom);

    List<Pilote> findAll();

    int addPilote(Pilote pilote);

    int updatePilote(Pilote pilote);

    int removePilote(int id);

    long count();
}

Pourquoi Optional<Pilote> ?

Parce qu’un pilote recherché peut ne pas exister.
Cela évite les null qui se promènent partout comme des valises perdues à Roissy.


1.7 Implémentation JDBC du DAO

package fr.bouget.pilote.dao;

import fr.bouget.pilote.model.Pilote;
import fr.bouget.pilote.util.ConnectionSingleton;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class PiloteDaoJdbc implements PiloteDao {

    private static final String SELECT_BY_ID =
            "SELECT id, nom, site FROM pilote WHERE id = ?";

    private static final String SELECT_BY_NOM =
            "SELECT id, nom, site FROM pilote WHERE nom = ?";

    private static final String SELECT_ALL =
            "SELECT id, nom, site FROM pilote ORDER BY id";

    private static final String INSERT =
            "INSERT INTO pilote(nom, site) VALUES (?, ?)";

    private static final String UPDATE =
            "UPDATE pilote SET nom = ?, site = ? WHERE id = ?";

    private static final String DELETE =
            "DELETE FROM pilote WHERE id = ?";

    private static final String COUNT =
            "SELECT COUNT(*) FROM pilote";

    private final Connection connection;

    public PiloteDaoJdbc() {
        this.connection = ConnectionSingleton.getInstance().getConnection();
    }

    @Override
    public Optional<Pilote> findById(int id) {
        try (PreparedStatement ps = connection.prepareStatement(SELECT_BY_ID)) {
            ps.setInt(1, id);

            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    Pilote pilote = mapRow(rs);
                    return Optional.of(pilote);
                }
            }
        } catch (SQLException e) {
            throw new RuntimeException("Erreur lors de findById()", e);
        }
        return Optional.empty();
    }

    @Override
    public Optional<Pilote> findByNom(String nom) {
        try (PreparedStatement ps = connection.prepareStatement(SELECT_BY_NOM)) {
            ps.setString(1, nom.toUpperCase());

            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    Pilote pilote = mapRow(rs);
                    return Optional.of(pilote);
                }
            }
        } catch (SQLException e) {
            throw new RuntimeException("Erreur lors de findByNom()", e);
        }
        return Optional.empty();
    }

    @Override
    public List<Pilote> findAll() {
        List<Pilote> pilotes = new ArrayList<>();

        try (PreparedStatement ps = connection.prepareStatement(SELECT_ALL);
             ResultSet rs = ps.executeQuery()) {

            while (rs.next()) {
                pilotes.add(mapRow(rs));
            }

        } catch (SQLException e) {
            throw new RuntimeException("Erreur lors de findAll()", e);
        }

        return pilotes;
    }

    @Override
    public int addPilote(Pilote pilote) {
        try (PreparedStatement ps = connection.prepareStatement(INSERT, Statement.RETURN_GENERATED_KEYS)) {
            ps.setString(1, pilote.getNom());
            ps.setString(2, pilote.getSite());

            int lignesModifiees = ps.executeUpdate();

            try (ResultSet keys = ps.getGeneratedKeys()) {
                if (keys.next()) {
                    pilote.setId(keys.getInt(1));
                }
            }

            return lignesModifiees;
        } catch (SQLException e) {
            throw new RuntimeException("Erreur lors de addPilote()", e);
        }
    }

    @Override
    public int updatePilote(Pilote pilote) {
        try (PreparedStatement ps = connection.prepareStatement(UPDATE)) {
            ps.setString(1, pilote.getNom());
            ps.setString(2, pilote.getSite());
            ps.setInt(3, pilote.getId());

            return ps.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException("Erreur lors de updatePilote()", e);
        }
    }

    @Override
    public int removePilote(int id) {
        try (PreparedStatement ps = connection.prepareStatement(DELETE)) {
            ps.setInt(1, id);
            return ps.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException("Erreur lors de removePilote()", e);
        }
    }

    @Override
    public long count() {
        try (PreparedStatement ps = connection.prepareStatement(COUNT);
             ResultSet rs = ps.executeQuery()) {

            if (rs.next()) {
                return rs.getLong(1);
            }

        } catch (SQLException e) {
            throw new RuntimeException("Erreur lors de count()", e);
        }

        return 0;
    }

    private Pilote mapRow(ResultSet rs) throws SQLException {
        return new Pilote(
                rs.getInt("id"),
                rs.getString("nom"),
                rs.getString("site")
        );
    }
}

1.8 Classe de test App.java

package fr.bouget.pilote;

import fr.bouget.pilote.dao.PiloteDao;
import fr.bouget.pilote.dao.PiloteDaoJdbc;
import fr.bouget.pilote.model.Pilote;

public class App {

    public static void main(String[] args) {
        PiloteDao piloteDao = new PiloteDaoJdbc();

        System.out.println("=== Liste des pilotes ===");
        piloteDao.findAll().forEach(System.out::println);

        System.out.println("\nNombre de pilotes : " + piloteDao.count());

        Pilote nouveau = new Pilote("Philippe", "Paris");
        piloteDao.addPilote(nouveau);
        System.out.println("\nPilote ajouté : " + nouveau);

        piloteDao.findByNom("PHILIPPE")
                .ifPresent(p -> System.out.println("\nTrouvé par nom : " + p));

        nouveau.setSite("Lyon");
        piloteDao.updatePilote(nouveau);
        System.out.println("\nAprès mise à jour : " + piloteDao.findById(nouveau.getId()).orElse(null));

        piloteDao.removePilote(nouveau.getId());
        System.out.println("\nAprès suppression, nombre de pilotes : " + piloteDao.count());
    }
}

1.9 Pourquoi cette version est intéressante ?

Elle permet de voir clairement :

Limite principale

Le code JDBC devient vite répétitif :

C’est justement ce qui motive l’usage d’un ORM comme Hibernate.


2) Version Hibernate sans Spring Boot + Maven + patterns utiles

2.1 Les patterns importants avec Hibernate

Avec Hibernate, les patterns les plus utiles dans un projet simple sont :

Autrement dit, Hibernate n’est pas juste un outil : c’est aussi un festival de patterns bien habillés.


2.2 Structure du projet

pilote-hibernate-postgresql/
├── pom.xml
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── fr/bouget/pilote/
│   │   │       ├── App.java
│   │   │       ├── dao/
│   │   │       │   ├── PiloteDao.java
│   │   │       │   └── PiloteDaoHibernate.java
│   │   │       ├── model/
│   │   │       │   └── Pilote.java
│   │   │       └── util/
│   │   │           └── HibernateUtil.java
│   │   └── resources/
│   │       └── hibernate.cfg.xml
│   └── test/
│       └── java/
└── README.md

2.3 Dépendances Maven

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>fr.bouget</groupId>
    <artifactId>pilote-hibernate-postgresql</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- Hibernate ORM -->
        <dependency>
            <groupId>org.hibernate.orm</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>6.6.14.Final</version>
        </dependency>

        <!-- Driver PostgreSQL -->
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.7.8</version>
        </dependency>

        <!-- API Jakarta Persistence -->
        <dependency>
            <groupId>jakarta.persistence</groupId>
            <artifactId>jakarta.persistence-api</artifactId>
            <version>3.2.0</version>
        </dependency>

        <!-- Logs simples pour voir ce qui se passe -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>2.0.17</version>
        </dependency>
    </dependencies>

</project>

2.4 Fichier hibernate.cfg.xml

Placez ce fichier dans src/main/resources
et non dans src/main/java.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "https://hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>

        <!-- Connexion PostgreSQL -->
        <property name="hibernate.connection.driver_class">org.postgresql.Driver</property>
        <property name="hibernate.connection.url">jdbc:postgresql://localhost:5432/bd_avion</property>
        <property name="hibernate.connection.username">test</property>
        <property name="hibernate.connection.password">test</property>

        <!-- Dialecte PostgreSQL -->
        <property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>

        <!-- Affichage SQL -->
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>
        <property name="hibernate.highlight_sql">true</property>

        <!-- Contexte de session -->
        <property name="hibernate.current_session_context_class">thread</property>

        <!-- Stratégie de génération de schéma -->
        <property name="hibernate.hbm2ddl.auto">update</property>

        <!-- Mapping -->
        <mapping class="fr.bouget.pilote.model.Pilote"/>

    </session-factory>
</hibernate-configuration>

Remarques importantes


2.5 Entité Pilote

package fr.bouget.pilote.model;

import jakarta.persistence.*;

@Entity
@Table(name = "pilote")
public class Pilote {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "nom", nullable = false, length = 50)
    private String nom;

    @Column(name = "site", nullable = false, length = 50)
    private String site;

    public Pilote() {
    }

    public Pilote(Integer id, String nom, String site) {
        this.id = id;
        this.nom = nom != null ? nom.toUpperCase() : null;
        this.site = site != null ? site.toUpperCase() : null;
    }

    public Pilote(String nom, String site) {
        this(null, nom, site);
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getNom() {
        return nom;
    }

    public void setNom(String nom) {
        this.nom = nom != null ? nom.toUpperCase() : null;
    }

    public String getSite() {
        return site;
    }

    public void setSite(String site) {
        this.site = site != null ? site.toUpperCase() : null;
    }

    @Override
    public String toString() {
        return "Pilote{id=" + id + ", nom='" + nom + "', site='" + site + "'}";
    }
}

2.6 Utilitaire HibernateUtil avec Singleton + Factory

package fr.bouget.pilote.util;

import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;

public class HibernateUtil {

    private static final SessionFactory sessionFactory = buildSessionFactory();

    private HibernateUtil() {
    }

    private static SessionFactory buildSessionFactory() {
        try {
            StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
                    .configure("hibernate.cfg.xml")
                    .build();

            Metadata metadata = new MetadataSources(registry)
                    .getMetadataBuilder()
                    .build();

            return metadata.getSessionFactoryBuilder().build();

        } catch (Exception e) {
            throw new ExceptionInInitializerError("Erreur d'initialisation de la SessionFactory : " + e.getMessage());
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public static void shutdown() {
        getSessionFactory().close();
    }
}

Pourquoi cette classe est importante ?

Une SessionFactory coûte cher à construire. On l’instancie donc une fois, pas toutes les trente secondes !


2.7 Interface DAO

On peut garder exactement la même interface que dans la version JDBC.

package fr.bouget.pilote.dao;

import fr.bouget.pilote.model.Pilote;

import java.util.List;
import java.util.Optional;

public interface PiloteDao {

    Optional<Pilote> findById(int id);

    Optional<Pilote> findByNom(String nom);

    List<Pilote> findAll();

    int addPilote(Pilote pilote);

    int updatePilote(Pilote pilote);

    int removePilote(int id);

    long count();
}

2.8 Implémentation DAO avec Hibernate

package fr.bouget.pilote.dao;

import fr.bouget.pilote.model.Pilote;
import fr.bouget.pilote.util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;

import java.util.List;
import java.util.Optional;

public class PiloteDaoHibernate implements PiloteDao {

    @Override
    public Optional<Pilote> findById(int id) {
        try (Session session = HibernateUtil.getSessionFactory().openSession()) {
            Pilote pilote = session.get(Pilote.class, id);
            return Optional.ofNullable(pilote);
        }
    }

    @Override
    public Optional<Pilote> findByNom(String nom) {
        try (Session session = HibernateUtil.getSessionFactory().openSession()) {
            String hql = "from Pilote p where p.nom = :nom";
            Pilote pilote = session.createQuery(hql, Pilote.class)
                    .setParameter("nom", nom.toUpperCase())
                    .uniqueResult();

            return Optional.ofNullable(pilote);
        }
    }

    @Override
    public List<Pilote> findAll() {
        try (Session session = HibernateUtil.getSessionFactory().openSession()) {
            String hql = "from Pilote p order by p.id";
            return session.createQuery(hql, Pilote.class).list();
        }
    }

    @Override
    public int addPilote(Pilote pilote) {
        Transaction tx = null;

        try (Session session = HibernateUtil.getSessionFactory().openSession()) {
            tx = session.beginTransaction();
            session.persist(pilote);
            tx.commit();
            return 1;
        } catch (Exception e) {
            if (tx != null) tx.rollback();
            throw new RuntimeException("Erreur lors de addPilote()", e);
        }
    }

    @Override
    public int updatePilote(Pilote pilote) {
        Transaction tx = null;

        try (Session session = HibernateUtil.getSessionFactory().openSession()) {
            tx = session.beginTransaction();
            session.merge(pilote);
            tx.commit();
            return 1;
        } catch (Exception e) {
            if (tx != null) tx.rollback();
            throw new RuntimeException("Erreur lors de updatePilote()", e);
        }
    }

    @Override
    public int removePilote(int id) {
        Transaction tx = null;

        try (Session session = HibernateUtil.getSessionFactory().openSession()) {
            tx = session.beginTransaction();

            Pilote pilote = session.get(Pilote.class, id);
            if (pilote != null) {
                session.remove(pilote);
                tx.commit();
                return 1;
            }

            tx.commit();
            return 0;

        } catch (Exception e) {
            if (tx != null) tx.rollback();
            throw new RuntimeException("Erreur lors de removePilote()", e);
        }
    }

    @Override
    public long count() {
        try (Session session = HibernateUtil.getSessionFactory().openSession()) {
            String hql = "select count(p) from Pilote p";
            return session.createQuery(hql, Long.class).uniqueResult();
        }
    }
}

2.9 Classe App.java

package fr.bouget.pilote;

import fr.bouget.pilote.dao.PiloteDao;
import fr.bouget.pilote.dao.PiloteDaoHibernate;
import fr.bouget.pilote.model.Pilote;
import fr.bouget.pilote.util.HibernateUtil;

public class App {

    public static void main(String[] args) {
        PiloteDao piloteDao = new PiloteDaoHibernate();

        System.out.println("=== Liste des pilotes ===");
        piloteDao.findAll().forEach(System.out::println);

        System.out.println("\nNombre de pilotes : " + piloteDao.count());

        Pilote nouveau = new Pilote("Philippe", "Paris");
        piloteDao.addPilote(nouveau);
        System.out.println("\nAjout : " + nouveau);

        piloteDao.findByNom("PHILIPPE")
                .ifPresent(p -> System.out.println("\nTrouvé : " + p));

        nouveau.setSite("Lyon");
        piloteDao.updatePilote(nouveau);

        piloteDao.findById(nouveau.getId())
                .ifPresent(p -> System.out.println("\nAprès mise à jour : " + p));

        piloteDao.removePilote(nouveau.getId());
        System.out.println("\nAprès suppression : " + piloteDao.count());

        HibernateUtil.shutdown();
    }
}

2.10 Différences entre JDBC et Hibernate

Point JDBC pur Hibernate
SQL écrit à la main Oui Souvent moins
Mapping ResultSet -> objet Manuel Automatique
Verbosité Forte Plus faible
Contrôle bas niveau Excellent Moins direct
Courbe d’apprentissage Simple au début Plus abstraite
Productivité CRUD Moyenne Bonne

Résumé simple


2.11 Bonnes pratiques

En JDBC

En Hibernate


2.12 Ce qu’il fallait corriger par rapport à votre exemple initial

Points d’attention

Exemple HQL correct

String hql = "from Pilote p where p.nom = :nom";

et non :

String hql = "SELECT p FROM Pilote p WHERE PI_NOM = :nom";

2.13 Variante possible : ajouter une couche Service

Dans un petit projet, DAO + main peuvent suffire.

Dans un projet un peu plus propre, on ajoute une couche Service :

IHM / Main
   ↓
Service
   ↓
DAO
   ↓
Base de données

Intérêt


2.14 Exemple de couche Service (optionnelle)

package fr.bouget.pilote.service;

import fr.bouget.pilote.dao.PiloteDao;
import fr.bouget.pilote.model.Pilote;

import java.util.List;
import java.util.Optional;

public class PiloteService {

    private final PiloteDao piloteDao;

    public PiloteService(PiloteDao piloteDao) {
        this.piloteDao = piloteDao;
    }

    public List<Pilote> listerTous() {
        return piloteDao.findAll();
    }

    public Optional<Pilote> rechercherParNom(String nom) {
        return piloteDao.findByNom(nom);
    }

    public void ajouter(String nom, String site) {
        piloteDao.addPilote(new Pilote(nom, site));
    }
}

2.16 Versions techniques utilisées dans ce support


2.17 Exercices

Exercice 1

Ajouter la méthode :

List<Pilote> findBySite(String site);

à la version JDBC puis à la version Hibernate.

Exercice 2

Créer un menu console :

  1. lister les pilotes
  2. rechercher un pilote par nom
  3. ajouter un pilote
  4. modifier son site
  5. supprimer un pilote

Exercice 3

Ajouter une couche PiloteService et déplacer les contrôles métier dedans :