Introduzione ad Apache Cayenne ORM

1. Panoramica

Apache Cayenne è una libreria open source, distribuita con la licenza Apache, che fornisce funzionalità come uno strumento di modellazione, mappatura relazionale a oggetti o ORM per operazioni di persistenza locale e servizi remoti.

Nelle sezioni seguenti vedremo come interagire con un database MySQL utilizzando Apache Cayenne ORM.

2. Dipendenze di Maven

Per iniziare, dobbiamo solo aggiungere le seguenti dipendenze per far apparire Apache Cayenne e il connettore MySQL il driver JDBC insieme per accedere al nostro database intro_cayenne :

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

Configuriamo il plugin di modellazione Cayenne che verrà utilizzato per progettare o impostare il nostro file di mappatura che funge da ponte tra lo schema del database e l'oggetto Java:

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

Invece di costruire manualmente il file di mappatura XML (fatto raramente), si consiglia di utilizzare il modellatore che è uno strumento piuttosto avanzato fornito con la distribuzione di Cayenne.

È disponibile per il download da questo archivio a seconda del sistema operativo o utilizza semplicemente la versione multipiattaforma (JAR) inclusa come plug-in Maven.

Il repository Maven Central ospita le ultime versioni di Apache Cayenne, il suo modellatore e MySQL Connector.

Successivamente, costruiamo il nostro progetto con mvn install e lanciamo la GUI del modellatore con il comando mvn cayenne-modeler: run per ottenere come output questa schermata:

3. Configurazione

Per fare in modo che Apache Cayenne cerchi il database locale corretto, dobbiamo solo riempire il suo file di configurazione con il driver giusto, l'URL e un utente nel file cayenne-project.xml che si trova nella directory delle risorse :

Qui possiamo vedere che:

  • Il database locale si chiama intro_cayenne
  • Se non è ancora stato creato, Cayenne lo farà per noi
  • Ci collegheremo utilizzando il nome utente root e la password root (cambiarla in base agli utenti registrati nel sistema di gestione del database)

Internamente, è XMLPoolingDataSourceFactory responsabile del caricamento delle informazioni di connessione JDBC da una risorsa XML associata a DataNodeDescriptor .

Tenere presente che questi parametri sono relativi al sistema di gestione del database e a un driver JDBC poiché questa libreria può supportare molti database diversi.

Ognuno di loro ha un adattatore disponibile in questo elenco dettagliato. Si noti che la documentazione completa per la versione 4.0 non è ancora disponibile, quindi fare riferimento alla versione precedente qui.

4. Mappatura e progettazione di database

4.1. Modellazione

Facciamo ora clic su "Apri progetto" , andiamo nella cartella delle risorse del progetto e scegliamo il file cayenne-project.xml, il modellatore mostrerà questo:

Qui, abbiamo la possibilità di creare la nostra struttura di mappatura da un database esistente o di procedere manualmente. Questo articolo tratterà quello che utilizza il modellatore e il database esistente per entrare in Cayenne e sapere rapidamente come funziona.

Diamo un'occhiata al nostro database intro_cayenne che ha una relazione uno-a-molti su due tabelle, poiché un autore può pubblicare o possedere molti articoli:

  • autore: id (PK) e nome
  • articolo: id (PK), titolo, contenuto e author_id (FK)

Ora andiamo su " Strumenti> Reengineer Database Schema " e tutte le nostre configurazioni di mappatura saranno compilate automaticamente. Nella schermata del prompt basta riempire la configurazione dell'origine dati disponibile lassù nel file cayenne-project.xml e premere continua:

Nella schermata successiva, dobbiamo selezionare "Usa tipi primitivi Java" come segue:

Dobbiamo anche garantire? mettere com.baeldung.apachecayenne.persistent come pacchetto Java e salvarlo; vedremo che il file di configurazione XML è stato aggiornato per la sua proprietà defaultPackage in modo che corrisponda al pacchetto Java:

In ogni ObjEntity dobbiamo specificare il pacchetto per le sottoclassi come mostrato nell'immagine seguente e fare nuovamente clic sull'icona "salva" :

Ora nel menu "Strumenti> Genera classi" , seleziona " Oggetti persistenti standard " come tipo; e nella scheda "Classi" seleziona tutte le classi e premi "genera" .

Torna Let al codice sorgente per vedere che i nostri oggetti persistenti sono stati generati con successo, parlando di _Article.java e _Author.java .

Notare che tutte queste configurazioni vengono salvate nel file datamap.map.xml che si trova anch'esso nella cartella delle risorse .

4.2. Struttura della mappatura

Il file di mapping XML generato presente nella cartella delle risorse utilizza alcuni tag univoci relativi ad Apache Cayenne:

  • DataNode () - il modello del database, il suo contenuto tutte le informazioni necessarie per connettersi a un database (il nome del database, il driver e le credenziali dell'utente)
  • DataMap () : è un contenitore di entità persistenti con le loro relazioni
  • DbAttribute () : rappresenta una colonna in una tabella di database
  • DbEntity () - il modello di una singola tabella o vista di database, può avere DbAttributes e relazioni
  • ObjEntity () - il modello di una singola classe java persistente; fatto di ObjAttributes che corrispondono alle proprietà della classe di entità e ObjRelationships che sono proprietà che hanno un tipo di un'altra entità
  • Embeddable () : il modello di una classe Java che funge da proprietà di un ObjEntity, ma corrisponde a più colonne nel database
  • Procedura () - per registrare la procedura memorizzata nel database
  • Query () - il modello di una query, utilizzato per mappare la query nel file di configurazione senza dimenticare che possiamo farlo anche nel codice

Ecco i dettagli completi.

5. API Cayenne

L'unico passaggio rimanente è utilizzare l'API Cayenne per eseguire le operazioni del nostro database utilizzando le classi generate, sapendo che la sottoclasse delle nostre classi persistenti è solo una best practice utilizzata per personalizzare il modello in seguito.

5.1. Creazione di un oggetto

Qui, salviamo solo un oggetto Autore e controlliamo in seguito che ci sia un solo record di questo tipo nel 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. Leggere un oggetto

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

È anche possibile eliminare tutti i record di una tabella utilizzando SQLTemplate , qui lo facciamo dopo ogni metodo di test per avere sempre un database vuoto prima che ogni test venga avviato :

@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. Conclusione

In questo tutorial, ci siamo concentrati sull'utilizzo di Apache Cayenne ORM per dimostrare facilmente come eseguire operazioni CRUD con una relazione uno-a-molti .

Come sempre, il codice sorgente di questo articolo può essere trovato su GitHub.