Introduction à Apache Cayenne ORM

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 comme 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 Maven

Pour commencer, nous devons juste 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éesintro_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 sert de 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 réalisé), il est recommandé d’utiliser le modeleur, un outil assez avancé fourni avec la distribution Cayenne.

Il est disponible en téléchargement à partir de cearchive 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 deApache Cayenne,his modeler etMySQL Connector.

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

image

3. Installer

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 fichiercayenne-project.xml situé dans le répertoireresources:



    
        
            
            
            
            
        
    

Ici on peut voir que:

  • La base de données locale est nomméeintro_cayenne

  • S'il n'est pas encore créé, Cayenne le fera pour nous

  • Nous nous connecterons en utilisant le nom d'utilisateurroot et le mot de passeroot (modifiez-le en fonction des utilisateurs enregistrés dans votre système de gestion de base de données)

En interne, ce sont lesXMLPoolingDataSourceFactory responsables du chargement des informations de connexion JDBC à partir d'une ressource XML associée auxDataNodeDescriptor.

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 a un adaptateur disponible dans celist détaillé. Notez que la documentation complète de la version 4.0 n’est pas encore disponible. Nous renvoyons donc à la version précédente ici.

4. Cartographie et conception de bases de données

4.1. La modélisation

Cliquons maintenant sur les“Open Project”, naviguons vers le dossier des ressources du projet et choisissons le fichiercayenne-project.xml, que le modélisateur affichera:

image

Ici,we’ve got the choice to either create our mapping structure from an existing databaseor to proceed manually. 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éesintro_cayenne qui a une relationone-to-many entre deux tables, car un auteur peut publier ou posséder de nombreux articles:

  • author: id (PK) etname

  • article: id (PK), title, content etauthor_id(FK)

Passons maintenant à «Tools > Reengineer Database Schema», et toutes nos configurations de mappage seront remplies automatiquement. Sur l'écran d'invite, remplissez simplement la configuration de la source de données disponible dans le fichiercayenne-project.xml et appuyez sur Continuer:

image

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

image

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

image

Dans chaqueObjEntity, nous devons spécifier le package pour les sous-classes comme indiqué dans l'image suivante et cliquer à nouveau sur l'icône“save”:

image

Maintenant, dans le menu“Tools > Generate Classes”, sélectionnez «Standard Persistent Objects» comme type; et sur l'onglet“Classes”, vérifiez toutes les classes et appuyez sur“generate”.

Revenons au code source pour voir que nos objets persistants ont été générés avec succès, en parlant deArticle.java_ and Author.java_.

Notez que toutes ces configurations sont enregistrées dans le fichierdatamap.map.xml également situé dans le dossierresources.

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(<node>) - le modèle de la base de données, son contenu toutes les informations nécessaires pour se connecter à une base de données (le nom de la base de données, le pilote et les informations d'identification de l'utilisateur)

  • DataMap(<data-map>) - c'est un conteneur d'entités persistantes avec leurs relations

  • DbAttribute(<db-attribute>) - représente une colonne dans une table de base de données

  • DbEntity(<db-entity>) - le modèle d'une seule table ou vue de base de données, il peut avoir des DbAttributes et des relations

  • ObjEntity(<obj-entity>) - le modèle d'une seule classe java persistante; fait d'ObjAttributes qui correspondent à des propriétés de classe d'entité et ObjRelationships qui sont des propriétés qui ont un type d'une autre entité

  • Embeddable(<embeddable>) - le modèle d'une classe Java qui agit comme une propriété d'un objet, mais correspond à plusieurs colonnes dans la base de données

  • Procedure(<procedure>) - pour enregistrer la procédure stockée dans la base de données

  • Query(<query>) - le modèle d'une requête, utilisé pour la requête mappée dans le fichier de configuration sans oublier que nous pouvons également le faire dans le code

Voici lesdetails complets.

5. API de Cayenne

La seule étape restante consiste à utiliser l'API Cayenne pour effectuer nos opérations de base de données à l'aide de classes générées, sachant que la sous-classification de nos classes persistantes est simplement une méthode recommandée pour personnaliser le modèle ultérieurement.

5.1. Créer un objet

Ici, nous sauvegardons simplement un objetAuthor et vérifions plus tard qu'il n'y a qu'un seul enregistrement de ce type dans la base de données:

@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. Lire un objet

Après avoir sauvegardé unAuthor, nous le sélectionnons simplement parmi d'autres via une simple requête par une propriété particulière:

@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. Récupération de tous les enregistrements d'une classe

Nous allons enregistrer deux auteurs et récupérer une collection d’objets d’auteur pour vérifier qu’il n’y a que ces deux-là enregistrés:

@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. Mise à jour d'un objet

Le processus de mise à jour est également simple, mais nous devons d’abord avoir l’objet souhaité avant de modifier ses propriétés et de l’appliquer à la base de données:

@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. Joindre un objet

Nous pouvons assigner un article à un auteur:

@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. Supprimer un objet

La suppression d'un objet enregistré le supprime complètement de la base de données, par la suite, nous verronsnull comme résultat de la requête:

@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. Supprimer tous les enregistrements d'une classe

Il est également possible de supprimer tous les enregistrements d'une table en utilisantSQLTemplate, ici nous faisons cela après chaque méthode de test pour toujours avoir une base de données vide avant que chaque test ne soit lancé:

@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 relationone-to-many.

Comme toujours, le code source de cet article peut être trouvéover on GitHub.