Introduction à Apache Cayenne ORM

1. Vue d'ensemble

Apache Cayenne est une bibliothèque open-source, distribuée sous la licence Apache, fournissant des fonctionnalités telles qu'un outil de modélisation, un mappage objet-relationnel aka ORM pour les opérations de persistance locales et les services à distance.

Dans les sections suivantes, nous verrons comment interagir avec une base de données MySQL à l'aide d'Apache Cayenne ORM.

2. Dépendances de Maven

Pour commencer, il suffit d'ajouter les dépendances suivantes pour mettre en place Apache Cayenne et MySQL Connector le pilote JDBC ensemble pour accéder à notre base de données intro_cayenne :

 org.apache.cayenne cayenne-server 4.0.M5   mysql mysql-connector-java 5.1.44 runtime 

Configurons le plugin Cayenne modeler qui sera utilisé pour concevoir ou paramétrer notre fichier de mappage qui agit comme un pont entre le schéma de base de données et l'objet Java:

 org.apache.cayenne.plugins maven-cayenne-modeler-plugin 4.0.M5 

Au lieu de créer un fichier de mappage XML à la main (rarement créé), il est recommandé d'utiliser le modeleur qui est un outil assez avancé fourni avec la distribution Cayenne.

Il est disponible en téléchargement à partir de cette archive en fonction de votre système d'exploitation ou utilisez simplement la version multiplateforme (JAR) incluse en tant que plugin Maven là-haut.

Le référentiel Maven Central héberge les dernières versions d'Apache Cayenne, de son modeleur et de MySQL Connector.

Ensuite, construisons notre projet avec l' installation mvn et lançons l'interface graphique du modeleur avec la commande mvn cayenne-modeler: run pour obtenir en sortie cet écran:

3. Configuration

Pour qu'Apache Cayenne recherche la bonne base de données locale, il suffit de remplir son fichier de configuration avec le bon pilote, l'URL et un utilisateur dans le fichier cayenne-project.xml situé dans le répertoire des ressources :

Ici, nous pouvons voir que:

  • La base de données locale est nommée intro_cayenne
  • S'il n'est pas encore créé, Cayenne le fera pour nous
  • Nous nous connecterons en utilisant le nom d'utilisateur root et le mot de passe root (changez-le en fonction des utilisateurs enregistrés dans votre système de gestion de base de données)

En interne, c'est XMLPoolingDataSourceFactory responsable du chargement des informations de connexion JDBC à partir d'une ressource XML associée au DataNodeDescriptor .

Sachez que ces paramètres sont relatifs au système de gestion de base de données et à un pilote JDBC car cette bibliothèque peut prendre en charge de nombreuses bases de données différentes.

Chacun d'eux dispose d'un adaptateur disponible dans cette liste détaillée. Notez que la documentation complète de la version 4.0 n'est pas encore disponible, nous nous référons donc à la version précédente ici.

4. Cartographie et conception de bases de données

4.1. La modélisation

Cliquons maintenant sur «Ouvrir le projet» , naviguons vers le dossier des ressources du projet et choisissons le fichier cayenne-project.xml, le modélisateur montrera ceci:

Ici, nous avons le choix de créer notre structure de mappage à partir d'une base de données existante ou de procéder manuellement. Cet article traitera de celui utilisant le modeleur et la base de données existante pour entrer dans Cayenne et savoir rapidement comment cela fonctionne.

Jetons un coup d'œil à notre base de données intro_cayenne qui a une relation un-à-plusieurs entre deux tables, car un auteur peut publier ou posséder de nombreux articles:

  • auteur: id (PK) et nom
  • article: id (PK), titre, contenu et author_id (FK)

Passons maintenant à « Outils> Schéma de la base de données Reengineer », et nous aurons toutes nos configurations de mappage remplies automatiquement. Sur l'écran d'invite, remplissez simplement la configuration de la source de données disponible dans le fichier cayenne-project.xml et appuyez sur Continuer:

Sur l'écran suivant, nous devons cocher «Utiliser les types primitifs Java» comme suit:

Nous devons également nous assurer? mettre com.baeldung.apachecayenne.persistent comme package Java et le sauvegarder; nous verrons que le fichier de configuration XML a été mis à jour pour sa propriété defaultPackage pour correspondre au package Java:

Dans chaque ObjEntity, nous devons spécifier le package pour les sous-classes comme indiqué dans l'image suivante et cliquer à nouveau sur l'icône «enregistrer» :

Maintenant, dans le menu «Outils> Générer des classes» , sélectionnez « Objets persistants standard » comme type; et sur l' onglet «Classes», cochez toutes les classes et appuyez sur «générer» .

Revenons au code source pour voir que nos objets persistants ont été générés avec succès, en parlant de _Article.java et _Author.java .

Notez que toutes ces configurations sont enregistrées dans le fichier datamap.map.xml également situé dans le dossier des ressources .

4.2. Structure de cartographie

Le fichier de mappage XML généré présent dans le dossier de ressources utilise des balises uniques relatives à Apache Cayenne:

  • DataNode() – the model of the database, its contents all information necessary to get connected to a database (the name of the database, the driver and the user credentials)
  • DataMap() – it's a container of persistent entities with their relations
  • DbAttribute() – represents a column in a database table
  • DbEntity() – the model of a single database table or view, it can have DbAttributes and relationships
  • ObjEntity() – the model of a single persistent java class; made of ObjAttributes that correspond to entity class properties and ObjRelationships that are properties that have a type of another entity
  • Embeddable() – the model of a Java class that acts as a property of an ObjEntity, but corresponds to multiple columns in the database
  • Procedure() – to register stored procedure in the database
  • Query() – the model of a query, used to mapped query in configuration file without forget that we can also do it in the code

Here are the full details.

5. Cayenne API

The only remaining step is to use the Cayenne API to do our database operations using generated classes, knowing that subclassing our persistent classes is just a best practice used for customizing the model later.

5.1. Creating an Object

Here, we just save an Author object and check later that there is only one record of this type in the database:

@Test public void whenInsert_thenWeGetOneRecordInTheDatabase() { Author author = context.newObject(Author.class); author.setName("Paul"); context.commitChanges(); long records = ObjectSelect.dataRowQuery(Author.class) .selectCount(context); assertEquals(1, records); }

5.2. Reading an Object

After saving an Author, we just pick it among others via a simple query by a particular property:

@Test public void whenInsert_andQueryByFirstName_thenWeGetTheAuthor() { Author author = context.newObject(Author.class); author.setName("Paul"); context.commitChanges(); Author expectedAuthor = ObjectSelect.query(Author.class) .where(Author.NAME.eq("Paul")) .selectOne(context); assertEquals("Paul", expectedAuthor.getName()); }

5.3. Retrieving All Records of a Class

We're going to save two authors and retrieve a collection of author objects to check that there are just these two saved:

@Test public void whenInsert_andQueryAll_thenWeGetTwoAuthors() { Author firstAuthor = context.newObject(Author.class); firstAuthor.setName("Paul"); Author secondAuthor = context.newObject(Author.class); secondAuthor.setName("Ludovic"); context.commitChanges(); List authors = ObjectSelect .query(Author.class) .select(context); assertEquals(2, authors.size()); }

5.4. Updating an Object

The updating process is easy too, but we need first to have the desired object before modifying its properties and applying it to the database:

@Test public void whenUpdating_thenWeGetAnUpatedeAuthor() { Author author = context.newObject(Author.class); author.setName("Paul"); context.commitChanges(); Author expectedAuthor = ObjectSelect.query(Author.class) .where(Author.NAME.eq("Paul")) .selectOne(context); expectedAuthor.setName("Garcia"); context.commitChanges(); assertEquals(author.getName(), expectedAuthor.getName()); }

5.5. Attaching an Object

We can assign an article to an author:

@Test public void whenAttachingToArticle_thenTheRelationIsMade() { Author author = context.newObject(Author.class); author.setName("Paul"); Article article = context.newObject(Article.class); article.setTitle("My post title"); article.setContent("The content"); article.setAuthor(author); context.commitChanges(); Author expectedAuthor = ObjectSelect.query(Author.class) .where(Author.NAME.eq("Smith")) .selectOne(context); Article expectedArticle = (expectedAuthor.getArticles()).get(0); assertEquals(article.getTitle(), expectedArticle.getTitle()); }

5.6. Deleting an Object

The deletion of a saved object completely removes it from the database, thereafter we'll see null as the result of the query:

@Test public void whenDeleting_thenWeLostHisDetails() { Author author = context.newObject(Author.class); author.setName("Paul"); context.commitChanges(); Author savedAuthor = ObjectSelect.query(Author.class) .where(Author.NAME.eq("Paul")) .selectOne(context); if(savedAuthor != null) { context.deleteObjects(author); context.commitChanges(); } Author expectedAuthor = ObjectSelect.query(Author.class) .where(Author.NAME.eq("Paul")) .selectOne(context); assertNull(expectedAuthor); }

5.7. Delete All Records of a Class

Il est également possible de supprimer tous les enregistrements d'une table à l'aide de SQLTemplate , ici nous faisons cela après chaque méthode de test pour toujours avoir une base de données vide avant le lancement de chaque test :

@After public void deleteAllRecords() { SQLTemplate deleteArticles = new SQLTemplate( Article.class, "delete from article"); SQLTemplate deleteAuthors = new SQLTemplate( Author.class, "delete from author"); context.performGenericQuery(deleteArticles); context.performGenericQuery(deleteAuthors); }

6. Conclusion

Dans ce didacticiel, nous nous sommes concentrés sur l'utilisation d'Apache Cayenne ORM pour démontrer facilement comment effectuer des opérations CRUD avec une relation un-à-plusieurs .

Comme toujours, le code source de cet article se trouve à l'adresse over sur GitHub.