Dans la série “Vivre sans Spring / Hibernate”, découvrons Apache Torque, un framework ORM qui comble quelques lacunes de son concurrent.

Introduction

Dans le cadre de mon projet “full Apache” (enfin presque, le front-end et la base de données n’utilisent pas de composants Apache), il faut pouvoir accéder aux données et les transformer en objets.

Choix habituel

Comme expliqué dans l’article relatif à la suppression de Spring, la couche ORM (Object-Relational mapping) est la plupart du temps confiée à Hibernate. C’est un framework qui fonctionne globalement bien mais qui impose rapidement l’usage de queries SQL (au travers de JPQL), réduisant ainsi la maintenabilité de l’application.

Apache Torque, un autre ORM

Tout comme Hibernate, Apache Torque remplit la fonction ORM en permettant la création, la modification, la suppression et la recherche d’enregistrements. Il permet également la création des tables et des entités à partir d’un schéma XML (je demande à voir comment modifier une table avec des enregistrements en production). Et là où Torque est meilleur que son concurrent Hibernate, c’est dans la programmation des recherches.

Exemple de recherche avec Torque

1
2
Criteria crit = new Criteria();
crit.where(BookPeer.ISBN, "0-618-12902-2");

Exemple de recherche avec Hibernate

1
2
3
4
5
String jpql = "select e from Event e where e.name = :name";
List<Event> events = 
    em.createQuery(jpql, Event.class)
      .setParameter("name", someNameComingFromAnywhere)
      .getResultList();

On voit immédiatement que Apache Torque propose une maintenabilité bien meilleure: on peut facilement renommer la propriété “ISBN” dans l’entité et les recherches associées. Tandis qu’avec Hibernate, il va falloir repasser sur toutes les requêtes JPQL (autant dire que c’est lourd).

Configuration

La configuration est relativement simple puisqu’elle consiste à compléter le fichier pom.xml.

Étape 1: ajouter le plugin Torque

Le plugin Maven “torque-maven-plugin” est responsable de créer le fichier SQL représentant les tables de la base et également de créer les entités. En gras, il s’agit des propriétés modifiables selon son projet et environnement.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<plugin>
                <groupId>org.apache.torque</groupId>
                <artifactId>torque-maven-plugin</artifactId>
                <version>4.0</version>
                <executions>
                    <execution>
                        <id>generate-sources</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <packaging>classpath</packaging>
                        <configPackage>org.apache.torque.templates.om</configPackage>
                            <sourceDir>src/main/schema</sourceDir>
                            <options>                                <torque.om.package>ch.gobothegeek.entites</torque.om.package>
                                <torque.database>mysql</torque.database>
                            </options>
                        </configuration>
                    </execution>
                    <execution>
                        <id>generate-sql</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <packaging>classpath</packaging>
                            <configPackage>org.apache.torque.templates.sql</configPackage>
                            <sourceDir>src/main/schema</sourceDir>
                            <defaultOutputDir>target/generated-sql</defaultOutputDir>
                            <defaultOutputDirUsage>none</defaultOutputDirUsage>
                            <options>
                                <torque.database>mysql</torque.database>
                            </options>
                        </configuration>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>org.apache.torque</groupId>
                        <artifactId>torque-templates</artifactId>
                        <version>4.0</version>
                    </dependency>
                </dependencies>
            </plugin>

Étape 2: ajouter le plugin SQL

Ce plugin Maven va, pour sa part, assurer l’exécution des requêtes SQL générées par Apache Torque. En gras, comme précédemment, il s’agit des informations à modifier selon son projet et environnement.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>sql-maven-plugin</artifactId>
                <version>1.4</version>
                <configuration>
                    <driver>org.gjt.mm.mysql.Driver</driver>
                    <url>jdbc:mysql://localhost:3306/base</url>
                    <username>torque</username>
                    <password>********</password>
                    <onError>continue</onError>
                    <autocommit>true</autocommit>
                    <fileset>
                        <basedir>${basedir}/target/generated-sql</basedir>
                        <includes>
                            <include>*.sql</include>
                        </includes>
                    </fileset>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.mariadb.jdbc</groupId>
                        <artifactId>mariadb-java-client</artifactId>
                        <version>2.7.3</version>
                    </dependency>
                </dependencies>
            </plugin>

Étape 3: configurer Torque

Il faut maintenant ajouter plusieurs éléments pour que Torque puisse générer les entités. D’abord il faut définir l’arborescence nécessaire: dans le dossier src/main, on ajoute le dossier torque-gen/ puis dans torque-gen/ on ajoute les dossiers conf/, outlets/, resources/ et templates.

Dans le dossier torque-gen/conf/, on ajoute le fichier control.xml suivant:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="UTF-8"?>
<control loglevel="debug"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://db.apache.org/torque/4.0/generator/configuration http://db.apache.org/torque/4.0/generator/configuration.xsd"
         xmlns="http://db.apache.org/torque/4.0/generator/configuration">

    <output name="torque.om.dbObject" existingTargetStrategy="skip" outputDirKey="modifiable">
        <filenameOutlet
                xsi:type="javaOutlet"
                class="org.apache.torque.generator.outlet.java.JavaFilenameOutlet">
            <mergepoint name="package">
                <action
                        xsi:type="sourceElementAttributeAction"
                        element="."
                        attribute="dbObjectPackage"
                        acceptNotSet="false"/>
            </mergepoint>
            <mergepoint name="classname">
                <action
                        xsi:type="sourceElementAttributeAction"
                        element="."
                        attribute="dbObjectClassName"
                        acceptNotSet="false"/>
            </mergepoint>
        </filenameOutlet>
        <source xsi:type="fileSource"
                elements="database/table">
            <transformer class="org.apache.torque.templates.transformer.om.OMTransformer"/>
            <include>*schema.xml</include>
            <exclude>id-table-schema.xml</exclude>
        </source>
        <outlet name="torque.om.dbObject"/>
    </output>
</control>

Utilisation

Étape 1: préparer le fichier XML de schéma

Ici, il faut ajouter le dossier src/main/schema (configurable via la propriété “sourceDir” du plugin Torque) puis y créer un fichier xml dont le nom doit obligatoirement se terminer par schema.xml. Le contenu de ce fichier (nommé book-schema.xml pour cet exemple) doit ressembler à ceci:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?xml version="1.0" encoding="ISO-8859-1" ?>
<database xmlns="http://db.apache.org/torque/5.0/templates/database"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://db.apache.org/torque/5.0/templates/database
        http://db.apache.org/torque/torque-5.0/documentation/orm-reference/database-5-0-strict.xsd"
  name="bookstore"
  defaultIdMethod="native">

  <table name="book" description="Book Table">
    <column
      name="book_id"
      required="true"
      primaryKey="true"
      type="INTEGER"
      description="Book Id"/>
    <column
      name="title"
      required="true"
      type="VARCHAR"
      size="255"
      description="Book Title"/>
    <column
      name="isbn"
      required="true"
      type="VARCHAR"
      size="24"
      javaName="ISBN"
      description="ISBN Number"/>
    <column
      name="publisher_id"
      required="true"
      type="INTEGER"
      description="Foreign Key Publisher"/>
    <column
      name="author_id"
      required="true"
      type="INTEGER"
      description="Foreign Key Author"/>
  </table>
</database>

Étape 2: transformer le fichier de schéma entités

Rien de complexe ici, il faut d’abord invoquer Maven avec “mvn generate-sources” afin qu’il prépare les entités et les requêtes.

A l’issue de l’exécution, si tout s’est déroulé correctement, vous obtenez les fichiers suivants:

  • target/generated-sources/ch/gobothegeek/entites/Book.java
  • target/generated-sources/ch/gobothegeek/entites/BookPeer.java
  • target/generated-sources/ch/gobothegeek/entites/BookPeerImpl.java
  • target/generated-sources/ch/gobothegeek/entites/BookRecordMapper.java
  • target/generated-sql/book-schema.sql

Le fichier SQL obtenu se présente comme ceci:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
-- -----------------------------------------------------------------------
-- mysql SQL script for schema bookstore
-- -----------------------------------------------------------------------


drop table if exists book;



# -----------------------------------------------------------------------
# book
# -----------------------------------------------------------------------
CREATE TABLE book
(
    book_id INTEGER NOT NULL AUTO_INCREMENT,
    title VARCHAR(255) NOT NULL,
    isbn VARCHAR(24) NOT NULL,
    publisher_id INTEGER NOT NULL,
    author_id INTEGER NOT NULL,
    PRIMARY KEY(book_id)
);

Étape 3: exécuter le fichier SQL

Il faut maintenant invoquer Maven avec “mvn sql:execute” afin que les requêtes soient exécutées en base de données, en utilisant le fichier SQL généré lors de l’étape précédente. A la fin du processus, on peut se connecter sur la base (ici j’ai utilisé dBeaver) et constater que la table est bien créée:

Apache Torque: table générée

Modifier une table existante

Si la création des tables est globalement facile, dans la vraie vie on est souvent amené à modifier des tables existantes qui contiennent des données. La documentation Apache étant plutôt légère sur ce point, je vais tester une modification simple: ajouter une colonne.

Pour cela, j’ai ajouté un enregistrement en base puis je modifie le fichier XML book-schema.xml et j’ajoute ceci:

1
2
3
4
5
<column
                name="stock"
                required="true"
                type="INTEGER"
                description="Number of books in stock"/>

Je relance le processus avec “mvn generate-sources”. On obtient alors le fichier de requêtes suivant:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
-- -----------------------------------------------------------------------
-- mysql SQL script for schema bookstore
-- -----------------------------------------------------------------------


drop table if exists book;



# -----------------------------------------------------------------------
# book
# -----------------------------------------------------------------------
CREATE TABLE book
(
    book_id INTEGER NOT NULL AUTO_INCREMENT,
    title VARCHAR(255) NOT NULL,
    isbn VARCHAR(24) NOT NULL,
    publisher_id INTEGER NOT NULL,
    author_id INTEGER NOT NULL,
    stock INTEGER NOT NULL,
    PRIMARY KEY(book_id)
);

Inutile d’exécuter en base puisque la table sera supprimée et les données perdues.

Conclusion

On a vu ici comment configurer et utiliser Apache Torque en remplacement d’Hibernate. Dans le prochain article sur Torque, on verra comment améliorer la configuration et permettre de générer des requêtes SQL de modification des tables.