• 15 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 15/12/2020

Implémenter des DAO

Lorsque vous interagissez avec une base de données, vous utilisez souvent le design pattern DAO (Data Access Objet) et l'API JDBC (Java Database Connectivity).

Dans ce chapitre, je vais vous montrer comment créer vos DAO en respectant les principes SOLID et comment les configurer avec Spring IoC.

Création et configuration des DAO

Les DAO sont à créer dans le module ticket-consumer qui, je vous le rappelle, est responsable des relations avec les services extérieurs à l'application, notamment la base de données.

Pour les classes DAO, je vais suivre le même principe que pour les Managers : je crée une classe DAO par sous-packages de beans métier.

Bien entendu, il ne faut pas oublier l'inversion de dépendances et les abstractions donc, chaque classe DAO (suffixée par DaoImpl) implémente une interface DAO (suffixée par Dao).

package org.example.demo.ticket.consumer.contract.dao;

/**
 * Interface DAO du package
 * {@link org.example.demo.ticket.model.bean.ticket}
 */
public interface TicketDao {
    // ...
}
package org.example.demo.ticket.consumer.impl.dao;

import org.example.demo.ticket.consumer.contract.dao.TicketDao;

/**
 * Classe d'implémentation de {@link TicketDao}.
 */
@Named
public class TicketDaoImpl extends AbstractDaoImpl implements TicketDao {
    // ...
}

Afin de généraliser certains éléments, toutes les classes DAO vont hériter de la classe AbstractDaoImpl qui aura, entre autres, un attribut dataSource permettant la connexion à la base de données de l'application.

package org.example.demo.ticket.consumer.impl.dao;

import javax.sql.DataSource;

public abstract class AbstractDaoImpl {

    private DataSource dataSource;

    protected DataSource getDataSource() {
        return dataSource;
    }

    // ...
}

Configuration des DAO

Je veux que la DataSource soit injectée dans l'attribut dataSource des classes d'implémentation des DAO.

Pour faire cela facilement, je dis à Spring d'injecter le bean dataSourceTicket via les annotations @Inject et @Named("dataSourceTicket") directement sur l'attribut dataSource dans la classe AbstractDaoImpl :

package org.example.demo.ticket.consumer.impl.dao;

import javax.inject.Inject;
import javax.inject.Named;
import javax.sql.DataSource;


public abstract class AbstractDaoImpl {

    @Inject
    @Named("dataSourceTicket")
    private DataSource dataSource;

    // ...
}

Configuration de la DataSource

Il ne reste plus qu'à créer la DataSource d'accès à la base de données DB_TICKET que Spring doit injecter (bean dataSourceTicket).

Drivers JDBC

Afin de se connecter à une base de données avec JDBC, les drivers JDBC correspondant au SGBD (Système de Gestion de Base de Données) doivent être accessibles par le classloader lors de la création de la DataSource.

Dans le fichier pom.xml racine, j'ajoute :

<project>
    ...
    <dependencyManagement>
        <dependencies>
            <!-- Drivers JDBC PostgreSQL -->
            <dependency>
                <groupId>org.postgresql</groupId>
                <artifactId>postgresql</artifactId>
                <version>9.4.1212</version>
                <scope>runtime</scope>
            </dependency>
            ...
        </dependencies>
    </dependencyManagement>
</project>

Module ticket-batch

Pour les batches, je vais gérer moi-même la création de la DataSource. Je dois donc ajouter les drivers JDBC de PostgreSQL dans les dépendances Maven du module ticket-batch.

Dans le fichier ticket-batch/pom.xml, j'ajoute :

<project>
    ...
    <dependencies>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>
        ...
    </dependencies>
</project>

Module ticket-webapp

Pour l'application web, en revanche, il est possible :

  • soit d'ajouter le JAR des drivers JDBC au classpath de Tomcat (typiquement dans $CATALINA_HOME/lib) ;

  • soit de l'embarquer directement dans le WAR (dans WEB-INF/lib).

C'est ce que je vais faire ici : embarquer directement les drivers dans le WAR. J'ajoute donc la dépendance Maven dans le fichier ticket-webapp/pom.xml :

<project>
    ...
    <dependencies>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>
        ...
    </dependencies>
</project>

Configuration de l'application web

Le serveur Apache Tomcat, comme la majorité des serveurs d'application, permet de créer des ressources JNDI de DataSource s'appuyant sur un pool de connexion.

Ces ressources peuvent être configurées :

  • soit au niveau du serveur Tomcat,

  • soit directement dans le WAR de déploiement de la webapp (cf documentation).

Je vais utiliser la dernière solution ici, qui est plus facile à mettre en oeuvre dans cette démonstraction (je n'ai pas besoin de rentrer le détail de la configuration de Tomcat).

Pour configurer les ressources directement dans le WAR, il faut ajouter un fichier /META-INF/context.xml dans celui-ci.

Afin de l'embarquer facilement lors du build Maven, je crée le fichier src/main/webapp/META-INF/context.xml dans le module ticket-webapp :

<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <Resource name="jdbc/DB_TICKET"
              auth="Container"
              type="javax.sql.DataSource"

              url="jdbc:postgresql://localhost:5432/db_ticket"
              driverClassName="org.postgresql.Driver"
              username="ticket"
              password="ticket"
              defaultAutoCommit="false"
              defaultTransactionIsolation="READ_COMMITTED"

              initialSize="1"
              maxTotal="30"
              maxIdle="10"
              maxWaitMillis="60000"
              minIdle="1"
              removeAbandonedTimeout="60"
              removeAbandonedOnBorrow="true"
              logAbandoned="true"
              minEvictableIdleTimeMillis="10000"
              timeBetweenEvictionRunsMillis="30000"
              validationQuery="SELECT 1"
              testWhileIdle="true"
              testOnBorrow="true"
    />
</Context>

Chaque ressource JNDI à créer fait l'objet d'un élément <Resource> dans l'élément racine <Context>. J'ai formaté ici l'élément <Resource> en 3 blocs d'attributs :

  • le premier définit la nature et le nom de la ressource JNDI ;

  • le deuxième définit les paramètres de connexion à la base de données ;

  • le troisième définit les paramètres de configuration du pool de connexion (Apache Commons DBCP).

La ressource JNDI est maintenant configurée dans Tomcat, il ne reste plus qu'à utiliser cette ressource JNDI dans le module ticket-webapp :

  1. Je commence par indiquer dans le fichier src/main/webapp/WEF-INF/web.xml que j'ai besoin de la ressource JNDI jdbc/DB_TICKET :

    <web-app>
        <resource-ref>
            <res-ref-name>jdbc/DB_TICKET</res-ref-name>
            <res-type>javax.sql.DataSource</res-type>
            <res-auth>Container</res-auth>
        </resource-ref>
        ...
    </web-app>
  2. Puis, j'ajoute cette ressource en tant que bean dataSourceTicket dans l'IoC container de Spring. J'ajoute dans le fichier src/main/resources/.../webappContext.xml :

    <bean id="dataSourceTicket" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="java:comp/env/jdbc/DB_TICKET"/>
    </bean>

Configuration des batches

En ce qui concerne les batches, il n'y a pas de serveur d'application pour gérer les ressources JNDI. Je vais embarquer et gérer un pool de connexion directement dans l'application.

Je vais utiliser le pool de connexion Apache Commons DBCP. J'ajoute donc la dépendance Maven :

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-dbcp2</artifactId>
    <version>2.1.1</version>
</dependency>

Afin de configurer le pool de connexion, je vais utiliser un fichier properties.

J'aborde cette problématique dans le chapitre Packagez vos livrables du cours Organisez et packagez une application Java avec Apache Maven. Pour rappel, le principe est le suivant :

  1. Je crée un répertoire src/data/conf dans le module ticket-batch contenant un exemple des fichiers de configuration.

  2. Lors du build Maven, le plugin maven-assembly-plugin crée une archive avec tous les JAR nécessaires et ces fichiers de configuration.

  3. L'administrateur décompresse l'archive sur le serveur de production et modifie les valeurs dans les fichiers de configuration.

Je crée donc le fichier src/data/conf/db-ticket.properties contenant les propriétés de configuration du pool de connexion à la base DB_TICKET :

url=jdbc:postgresql://localhost:5432/db_ticket
driverClassName=org.postgresql.Driver
username=ticket
password=ticket
defaultAutoCommit=false
defaultTransactionIsolation=READ_COMMITTED

initialSize=1
maxTotal=30
maxIdle=10
maxWaitMillis=60000
minIdle=1
removeAbandonedTimeout=60
removeAbandonedOnBorrow=true
logAbandoned=true
minEvictableIdleTimeMillis=10000
timeBetweenEvictionRunsMillis=30000
validationQuery=SELECT 1
testWhileIdle=true
testOnBorrow=true

Afin de faciliter l'adressage des fichiers de l'application, mes scripts de lancement des batches assignent la propriété système application.home indiquant la racine de l'application (le répertoire où a été décompressée l'archive de déploiement).

Dans le fichier src/main/resources/.../batchContext.xml du module ticket-batch, j'importe le fichier properties de configuration de la base de données et je crée la datasource dataSourceTicket :

<beans>
    <!-- Chargement du fichier properties contenant
        la configuration de la datasource vers DB_TICKET  -->
    <bean id="dataSourceTicketConfiguration" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
        <property name="location" value="file:${application.home}/conf/db-ticket.properties"/>
    </bean>

    <!-- Création de la datasource "dataSourceTicket" -->
    <bean id="dataSourceTicket"
          class="org.apache.commons.dbcp2.BasicDataSourceFactory"
          factory-method="createDataSource"
          destroy-method="close">
        <constructor-arg ref="dataSourceTicketConfiguration"/>
    </bean>
</beans>

Dans cette configuration Spring, je crée :

  • une instance de java.util.Properties (bean dataSourceTicketConfiguration) à partir du fichier properties file:${application.home}/conf/db-ticket.properties ;

  • une instance de org.apache.commons.dbcp2.BasicDataSource (bean dataSourceTicket).

Le bean dataSourceTicket est créé :

  • grâce à la méthode static createDataSource (attribut factory-method),

  • de la classe org.apache.commons.dbcp2.BasicDataSourceFactory (attribut class),

  • en lui passant en paramètre le bean dataSourceTicketConfiguration (élément constructor-arg).

L'attribut destroy-method indique que la méthode close devra être appelée sur ce bean lorsqu'il sera détruit (typiquement à l'arrêt de l'application).

Ça y est, tout est prêt ! Cela vous a peut-être semblé un peu long, mais je tenais à bien vous expliquer chaque étape. Ne vous inquiétez pas, en réalité (et d'autant plus avec l'habitude), ce travail préparatoire ne prend que quelques minutes. ;)

Dans le chapitre suivant, je vais passer aux choses sérieuses : exécuter des requêtes SQL... mais en utilisant Spring JDBC !

Exemple de certificat de réussite
Exemple de certificat de réussite