Developper avec JPA et eXo Platform
Depuis la version 4.3, eXo Platform fournit une intégration native avec JPA (Java Persistence API).
JPA est une spécification d’API Java pour la gestion de données relationnelles dans les applications Java. Cette API permet de lier une classe Java (appelée Entité) avec une table de base de données relationnelles à l’aide d’annotations Java ou de configuration XML.
La spécification définit aussi une API EntityManager pour gérer le traitement des requêtes et des transactions sur les objets Java sur la base de données.
Pour avoir plus d’informations sur JPA, n’hésitez pas à parcourir la documentation officielle.
L’intégration faite dans eXo Platform nous a permis de démarrer notre transition vers un nouveau modèle de données et de donner aux développeurs la capacité de créer leurs propres applications basées sur JPA.
Cet article décrit cette intégration et explique comment développer des applications en utilisant JPA dans eXo Platform.
Integration JPA
Hibernate est l’implémentation JPA utilisée, c’est l’implémentation de référence de la spécification. Depuis eXo Platform 4.3 il est possible de “mapper” facilement des entités (classes) Java avec JPA/Hibernate.
Nous avons tout préparé pour vous : la datasource, la persistence unit, des annotations pour les entités et un DAO générique.
Datasource
Une nouvelle datasource est disponible par défaut dans eXo Platform, appelée “exo-jpa_portal” (le nom peut être changé à l’aide de la propriété “exo.jpa.datasource.name” dans le fichier exo.properties).
Cette datasource est utilisée pour toutes les opérations qui utilisent la persistence unit eXo.
Persistence unit et entity manager
Toutes les opérations JPA doivent utiliser une persistence unit et un entity manager pour interagir avec la base de données. La persistence unit définie la datasource à utiliser, les entités à gérer et un ensemble d’autres paramètres de configuration.
Nous avons ajouté une persistence unit par défaut dans eXo Platform, appelé exo-pu, qui utilise la datasource par défaut exo-jpa_portal, et qui ne déclare aucune entité :
<persistence xmlns=”http://java.sun.com/xml/ns/persistence”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd”
version=”2.0″>
<persistence-unit name=”exo-pu” transaction-type=”RESOURCE_LOCAL”>
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<non-jta-data-source>java:/comp/env/exo-jpa_portal</non-jta-data-source>
<properties>
<property name=”persistenceUnitName” value=”exo-pu”></property>
</properties>
</persistence-unit>
</persistence>
La configuration de cette persistence unit peut être modifié facilement en définissant le paramètre dans le fichier exo.properties, préfixé par “exo.jpa.”.
Par exemple, pour demander à Hibernate de tracer toutes les requêtes SQL, il suffit d’ajouter la ligne suivante :
exo.jpa.hibernate.show_sql=true
Entitiés
eXo Platform fournit une annotation pour ajouter une entité JPA dans la persistence unit (exo-pu): @ExoEntity. Voici un example :
@Entity(name = “TaskTask”)
@ExoEntity
public class Task {
@Id
@SequenceGenerator(name=”SEQ_TASK_TASKS_TASK_ID”, sequenceName=”SEQ_TASK_TASKS_TASK_ID”)
@GeneratedValue(strategy=GenerationType.AUTO, generator=”SEQ_TASK_TASKS_TASK_ID”)
@Column(name = “TASK_ID”)
private long id;
private String title;
private String description;
…
}
Ceci est un extrait de l’entité task. Comme vous pouvez le voir, il s’agit d’une entité JPA standard (annoté avec @Entity). La seule différence est l’ajout de l’annotation @ExoEntity.
Ajouter cette annotation et mettre cette classe dans le classpath ajoute automatiquement cette entité dans la persistence unit eXo.
Transactions
@ExoEntity n’est pas la seule annotation fournie par eXo Platform; nous avons également ajouté une annotation pour définir une méthode transactionnelle : @ExoTransactional.
Apposer cette annotation sur une méthode permet d’utiliser automatiquement l’entity manager pour démarrer une transaction et la fermer, ou d’utiliser une transaction existante si elle a déjà été ouverte.
@ExoTransactional
public Task createTask(Task task) {
…
}
Le plugin Maven JCabi doit être activé pour le bon fonctionnement de cette annotation. Pour cela, 2 façons de procéder existent :
- Utiliser le POM parent eXo addons en tant que POM (Project Object Model) parent de votre projet, comme dans le projet Task :
<parent>
<artifactId>addons-parent-pom</artifactId>
<groupId>org.exoplatform.addons</groupId>
<version>6</version>
</parent>
et déclarer le plugin JCabi dans le module qui utilise @ExoTransactional :
<build>
<plugins>
<plugin>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- Ajouter le plugin JCabi avec la configuration complète :
<build>
<plugins>
<plugin>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-maven-plugin</artifactId>
<version>${version.jcabi.plugin}</version>
<executions>
<execution>
<id>weave-classes</id>
<phase>process-classes</phase>
<goals>
<goal>ajc</goal>
</goals>
<configuration>
<classesDirectory>${project.build.outputDirectory}</classesDirectory>
</configuration>
</execution>
<execution>
<id>weave-test-classes</id>
<phase>process-test-classes</phase>
<goals>
<goal>ajc</goal>
</goals>
<configuration>
<classesDirectory>${project.build.testOutputDirectory}</classesDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
DAO générique
Afin de rendre la vie des développeurs plus simple nous avons également ajouté un DAO générique qui définit toutes les opérations basiques d’accès aux données, en utilisant l’entity manager eXo : find, findAll, create, update, delete, …
xIl suffit de faire hériter votre DAO de la classe org.exoplatform.commons.persistence.impl.GenericDAOJPAImpl pour bénéficier de toutes ces méthodes :
public class TaskDAOImpl extends GenericDAOJPAImpl<Task, Long> {
…
}
Créez votre modèle de données
Maintenant que nous avons conçu notre modèle de données en Java, nous avons besoin de créer la structure des données dans la base de données.
Hibernate permet de générer automatiquement la structure des données à partir du modèle Java, en se basant sur les annotations JPA et le paramètre de configuration hbm2ddl.auto.
Cependant, nous pensons que la modélisation des données dans la base de données est une phase primordiale dans la conception d’une solution et ne doit pas reposer sur la génération automatique par Hibernate.
Elle doit être réalisée dans un processus dédié, d’un point de vue conception et exécution. Il est important que les développeurs maîtrisent complètement le modèle de données.
eXo Platform intègre Liquibase, un outil qui permet d’initialiser et de mettre à jour de manière incrémentale le modèle de données et les données.
Son utilisation n’est pas obligatoire pour développer des applications dans eXo Platform mais elle est grandement recommandée puisqu’elle fournit un moyen simple et efficace de gérer une base de données.
C’est ce que nous utilisons pour les données de la plateforme.
Liquibase utilise des fichiers de changelog pour initialiser et mettre à jour la base de données. Ces fichiers contiennent un ensemble de change sets.
Chaque change set définit une opération sur la base de données (création d’une table, ajout d’une contrainte, création d’un index, exécution d’une requête SQL, …). Voici un exemple de changelog (celui de l’application eXo Task) :
<?xml version=”1.0″ encoding=”UTF-8″?>
<databaseChangeLog
xmlns=”http://www.liquibase.org/xml/ns/dbchangelog”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.3.xsd”>
<!– Managing both DB that use sequences and db that use auto increment –>
<property name=”autoIncrement” value=”true” dbms=”mysql,mssql,h2,sybase,db2,hsqldb”/>
<property name=”autoIncrement” value=”false” dbms=”oracle,postgresql”/>
<!– Definition of TASK_PROJECTS table –>
<changeSet author=”task” id=”1.0.0-1″>
<createTable tableName=”TASK_PROJECTS”>
<column name=”PROJECT_ID” type=”BIGINT” autoIncrement=”${autoIncrement}” startWith=”1″>
<constraints nullable=”false” primaryKey=”true” primaryKeyName=”PK_TASK_PROJECTS” />
</column>
<column name=”NAME” type=”NVARCHAR(50)”>
<constraints nullable=”false”/>
</column>
<column name=”DESCRIPTION” type=”NVARCHAR(2000)”>
<constraints nullable=”true”/>
</column>
<column name=”COLOR” type=”NVARCHAR(100)”>
<constraints nullable=”true”/>
</column>
<column name=”CALENDAR_INTEGRATED” type=”BIT” defaultValue=”0″>
<constraints nullable=”false”/>
</column>
<column name=”DUE_DATE” type=”TIMESTAMP”>
<constraints nullable=”true”/>
</column>
<column name=”PARENT_PROJECT_ID” type=”BIGINT”>
<constraints foreignKeyName=”FK_TASK_PRJ_TASK_PRJ_01″ references=”TASK_PROJECTS(PROJECT_ID)” nullable=”true”/>
</column>
</createTable>
<modifySql dbms=”mysql”>
<append value=” ENGINE=INNODB CHARSET=UTF8 COLLATE utf8_general_ci”/>
</modifySql>
</changeSet>
<changeSet author=”task” id=”1.0.0-2″ dbms=”oracle,postgresql”>
<createSequence sequenceName=”SEQ_TASK_PROJECTS_PROJECT_ID” startValue=”1″/>
</changeSet>
…
<changeSet author=”task” id=”1.0.0-23″>
<createIndex indexName=”IDX_TASK_LABELS_01″
tableName=”TASK_LABELS”>
<column name=”USERNAME” type=”NVARCHAR(50)”/>
</createIndex>
</changeSet>
…
<changeSet author=”task” id=”1.0.0-25″>
<addUniqueConstraint columnNames=”LABEL_ID, TASK_ID”
constraintName=”UQ_TASK_LABEL_TASK”
tableName=”TASK_LABEL_TASK”/>
</changeSet>
Le contenu complet du fichier de changelog de la version 1.0.0 de l’application Task est disponible ici : https://raw.githubusercontent.com/exoplatform/task/1.0.0/services/src/main/resources/db/changelog/task.db.changelog-1.0.0.xml.
Une fois le change set appliqué, Liquibase l’enregistre afin de ne pas l’appliquer de nouveau à la prochaine mise à jour.
Il s’appuie sur l’ID de change set, il est donc très important de ne pas modifier les ID une fois que les change sets correspondants ont été appliqués, et de n’avoir que des ID uniquement.
eXo Platform exécute Liquibase à chaque démarrage. Pour l’application Task par exemple, si ses données n’ont jamais été initialisées, tous les change sets de l’application vont être exécutés au premier démarrage.
Ensuite, lors des démarrages suivants, aucun changement ne sera appliqué, jusqu’à ce que d’autres change sets soient ajoutés.
Un composant eXo Platform est disponible pour ajouter des nouveaux fichiers de changelog (pour les données d’applications métier par exemple). Il est utilisable dans une extension, comme n’importe quel autre composant :
<external-component-plugins>
<target-component>org.exoplatform.commons.api.persistence.DataInitializer</target-component>
<component-plugin>
<name>TaskManagementChangeLogsPlugin</name>
<set-method>addChangeLogsPlugin</set-method>
<type>org.exoplatform.commons.persistence.impl.ChangeLogsPlugin</type>
<init-params>
<values-param>
<name>changelogs</name>
<description>Change logs of task management</description>
<value>db/changelog/task.db.changelog-1.0.0.xml</value>
<value>db/changelog/task.db.changelog-1.1.0.xml</value>
</values-param>
</init-params>
</component-plugin>
</external-component-plugins>
Le paramètre changelogs du composant permet de définir une liste de chemins (dans le classpath) pour les fichiers de changelog. Dans l’exemple précédent, les changelogs des versions 1.0.0 et 1.1.0 de l’application Task sont déclarés. La convention utilisée dans eXo Platform est d’avoir un fichier de change log par version de l’application, mais ce n’est qu’une convention, libre à vous de les organiser comme bon vous semble.
Une fois ces fichiers ajoutés dans votre extension, vous êtes prêt à initialiser votre premier modèle de données avec liquibase dans eXo Platform !
Pour avoir plus d’informations sur Liquibase, n’hésitez pas à parcourir la documentation officielle.
Conclusion
Maintenant que vous savez comment déclarer des entités JPA, configurer la persistence unit, créer les classes DAO, gérer les transactions et l’initialisation et la mise à jour du modèle de données, vous êtes prêt à développer vos propres applications avec JPA !
eXo Platform s’occupe de toute la plomberie afin que vous vous concentriez sur l’essentiel.
A vous de jouer ! 😉
Découvrez comment eXo Platform peut vous aider à transformer votre entreprise!