Une base de données relationnelle embarquée dans Apache TomEE et sans prise de tête? Allons faire un petit tour du côté de HSQLDB

HSQLDB ou la base de données facile

Introduction

Dans le cadre d’un side-project, j’avais besoin d’une base de données relationnelle. Au départ, je voulais utiliser PostgreSql, mais l’application devant fonctionner sur un Raspberry 3+, j’ai revu mon choix et j’ai opté pour une base embarquée (“Embedded database”).

Embarquée vs Autonome

D’un côté, on trouve les serveurs de bases de données à installer (comme MariaDB, PostgreSql, Oracle, etc) et de l’autre on trouve des serveurs qui fonctionnent dans un serveur applicatif (par exemple Tomcat).

Les deux approches sont intéressantes et bien sûr chaque solution possède des avantages et des inconvénients.

Le principal avantage des bases embarquées est qu’elles démarrent avec en même temps que Tomcat et les autres applications.

Bases candidates

  • H2: base plutôt fiable mais la console d’administration est très pauvre.
  • Apache Derby: grosse déception! Ne fonctionne tout simplement pas avec Apache Deltaspike.
  • HSQLDB (HyperSql DataBase): intégration sans histoire avec Apache Deltaspike et gérable avec un client Sql tel que dBeaver.

Deux modes de fonctionnement

HSQLDB propose deux modes de fonctionnement: “in process” et “server”. Le mode “in process” consiste a inclure le jar HSQLDB dans l’application. Le mode “server” est plus intéressant puisqu’il permet d’héberger plusieurs bases dans un même serveur applicatif. Je vais présenter un exemple d’application appliquant le mode “server”.

Application HSqlServer

L’application s’articule autour d’une seule classe, nommée ici HSqlServer. Cette classe permet de configurer le port de connexion ainsi que la ou les bases exposées.

    
 1package ch.gobothegeek.hsqlsrv;
 2
 3import org.apache.deltaspike.core.api.config.ConfigResolver;
 4import org.apache.logging.log4j.LogManager;
 5import org.apache.logging.log4j.Logger;
 6import org.hsqldb.Server;
 7import javax.servlet.*;
 8import java.io.IOException;
 9
10public class HSqlServerApp implements Servlet {
11    private static final Logger logger = LogManager.getLogger(HSqlServerApp.class);
12
13    private Server hServer;
14
15    @Override
16    public void init(ServletConfig servletConfig) throws ServletException {
17        String[] arDbsCfg;
18        String[] arDbInfo;
19
20        logger.info("Starting HSqlServer");
21        this.hServer = new Server();
22        this.hServer.setPort(Integer.parseInt(ConfigResolver.getPropertyValue("application.port")));
23        arDbsCfg = ConfigResolver.getPropertyValue("application.databases").split(";");
24        for (int posDb = 0; posDb < arDbsCfg.length; posDb++) {
25            if (0 < arDbsCfg[posDb].indexOf(":")) {
26                arDbInfo = arDbsCfg[posDb].split(":");
27                this.hServer.setDatabaseName(posDb, arDbInfo[0]);
28                this.hServer.setDatabasePath(posDb, arDbInfo[1]);
29            }
30        }
31        this.hServer.start();
32    }
33
34    @Override
35    public ServletConfig getServletConfig() {
36        return null;
37    }
38
39    @Override
40    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
41    }
42
43    @Override
44    public String getServletInfo() {
45        return null;
46    }
47
48    @Override
49    public void destroy() {
50        logger.info("Destroying HSqlServer");
51        this.hServer.stop();
52    }
53}

Le reste du projet se compose de fichiers de configuration. Le projet complet est téléchargeable ici et consultable sur mon GitHub.

Connexion et utilisation dans une application

Configurer pom.xml

Il faut en premier lieu inclure le jar HSqlDB via Maven dans le pom.xml:

    
1<dependency>    
2    <groupId>org.hsqldb</groupId>
3    <artifactId>hsqldb</artifactId>
4    <version>2.6.1</version>
5</dependency>

Configurer Persistence.xml

Ensuite, il faut ajouter le fichier de persistance src/main/resources/META-INF/persistence.xml. En jaune, il s’agit des deux informations à adapter à son projet.

    
 1<persistence
 2    version="2.0"
 3    xmlns="http://java.sun.com/xml/ns/persistence"
 4    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 5    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
 6    <persistence-unit name="myDbUnit" transaction-type="RESOURCE_LOCAL">
 7        <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
 8        <non-jta-data-source>myDS</non-jta-data-source>
 9        <properties>
10            <property name="openjpa.Log" value="SQL=Trace" />
11            <property name="openjpa.ConnectionFactoryProperties" value="PrintParameters=true" />
12            <property name="openjpa.jdbc.DBDictionary" value="hsql" />
13        </properties>
14    </persistence-unit>
15</persistence>

A noter que j’ai activé l’affichage des requêtes (“SQL=Trace”) et des paramètres (“PrintParameters=true”) afin de faciliter la mise au point de mon application.

Configurer Resources.xml

Il faut maintenant configurer la datasource en ajoutant le fichier src/WebContent/WEB-INF/resources.xml:

    
 1<?xml version="1.0" encoding="iso-8859-1" ?>
 2<resources>
 3    <Resource id="myDB" type="javax.sql.DataSource">
 4        UserName SA
 5        Password
 6        jtaManaged false
 7        JdbcDriver org.hsqldb.jdbcDriver
 8        JdbcUrl jdbc:hsqldb:hsql://localhost:9001/projet
 9    </Resource>
10</resources>

En jaune, on trouve les informations à adapter:

  • myDB: nom de la datasource.
  • SA: nom de l’utilisateur permettant d’accéder aux données.
  • localhost: le nom DNS du serveur sur lequel est accessible la base de données.
  • 9001: la port de connexion à la base de données.
  • projet: le nom de la base elle-même.

Configurer Web.xml

Le point suivant consiste à exposer la datasource nécessaire au bon fonctionnement de Deltaspike. Pour cela, on complète le fichier src/WebContent/WEB-INF/web.xml:

    
1<resource-ref>
2        <res-ref-name>myDB</res-ref-name>
3        <res-type>javax.sql.DataSource</res-type>
4        <res-auth>Container</res-auth>
5        <res-sharing-scope>Shareable</res-sharing-scope>
6    </resource-ref>

En jaune, “myDB” est le nom de la datasource et doit être identique à celui configuré dans le fichier resources.xml.

Définir un “Entity Manager”

Et pour terminer, il faut définir un “Entity Manager” qui va effectuer les requêtes produites par le ou les repositories.

    
 1@ApplicationScoped
 2public class MyEntityManagerProducer implements Serializable {
 3    @PersistenceUnit(name = "MyDbUnit") private EntityManagerFactory entityManagerFactory;
 4
 5    // return an EntityManager use to access data
 6    @Produces
 7    @TransactionScoped
 8    public EntityManager create() {
 9        return this.entityManagerFactory.createEntityManager();
10    }
11
12    // close current entity manager
13    public void close(@Disposes EntityManager em)
14    {
15        if (em.isOpen()) { em.close(); }
16    }
17
18    // getter/setter for EntityManagerFactory
19    public EntityManagerFactory getEntityManagerFactory() { return entityManagerFactory; }
20    public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) { this.entityManagerFactory = entityManagerFactory; }
21}

En jaune, on retrouve le nom de l’unité de persistance définie dans le fichier src/main/resources/META-INF/persistence.xml.

Et voilà, HSqlDB est prêt à l’emploi!

Conclusion

Il est assez facile et rapide d’ajouter un serveur HSqlDB et l’utiliser dans ses projets.