Una guida per Hibernate OGM

1. Panoramica

In questo tutorial, esamineremo le basi di Hibernate Object / Grid Mapper (OGM).

Hibernate OGM fornisce il supporto JPA (Java Persistence API) per i datastore NoSQL. NoSQL è un termine generico che copre un'ampia varietà di archiviazione dei dati. Ad esempio, questo include archivi dati chiave-valore, documenti, colonne e grafici.

2. L'architettura di Hibernate OGM

Hibernate offre tradizionalmente un motore ORM (Object Relational Mapping) per i database relazionali. Il motore OGM di Hibernate estende le sue funzionalità per supportare i datastore NoSQL. Il vantaggio principale dell'utilizzo è la coerenza dell'interfaccia JPA tra datastore relazionali e NoSQL.

Hibernate OGM è in grado di fornire astrazione su un numero di datastore NoSQL grazie a due interfacce chiave, DatastoreProvider e GridDialect . Pertanto, ogni nuovo datastore NoSQL che supporta viene fornito con un'implementazione di queste interfacce.

Ad oggi, non supporta tutti i datastore NoSQL, ma è in grado di funzionare con molti di essi come Infinispan ed Ehcache (valore-chiave), MongoDB e CouchDB (documento) e Neo4j (grafico).

Supporta inoltre completamente le transazioni e può funzionare con fornitori JTA standard. In primo luogo, questo può essere fornito tramite il contenitore Jakarta EE senza alcuna configurazione esplicita. Inoltre, possiamo utilizzare un gestore di transazioni JTA autonomo come Narayana nell'ambiente Java SE.

3. Configurazione

Per questo tutorial, useremo Maven per estrarre le dipendenze richieste per lavorare con Hibernate OGM. Useremo anche MongoDB.

Per chiarire, vediamo come impostarli per il tutorial.

3.1. Dipendenze di Maven

Vediamo le dipendenze richieste per lavorare con Hibernate OGM e MongoDB:

 org.hibernate.ogm hibernate-ogm-mongodb 5.4.0.Final   org.jboss.narayana.jta narayana-jta 5.9.2.Final 

Qui stiamo estraendo le dipendenze richieste tramite Maven:

  • Dialetto OGM ibernato per MongoDB
  • Narayana Transaction Manager (attuale fornitore della JTA)

3.2. Unità di persistenza

Dovremo anche definire i dettagli del datastore in Hibernate persistance.xml :

 org.hibernate.ogm.jpa.HibernateOgmPersistence      

Nota le definizioni che abbiamo fornito qui:

  • il valore dell'attributo tipo di transazione come "JTA" (questo implica che vogliamo un gestore di entità JTA da EntityManagerFactory)
  • il provider, che è HibernateOgmPersistence per Hibernate OGM
  • alcuni dettagli aggiuntivi relativi al DB (questi in genere variano tra diverse fonti di dati)

La configurazione presuppone che MongoDB sia in esecuzione e accessibile nelle impostazioni predefinite. Se questo non è il caso, possiamo sempre fornire i dettagli necessari. Uno dei nostri articoli precedenti copre anche la configurazione di MongoDB in dettaglio.

4. Definizione di entità

Ora che abbiamo esaminato le basi, definiamo alcune entità. Se abbiamo già lavorato con Hibernate ORM o JPA, questo non ha altro da aggiungere . Questa è la premessa fondamentale di Hibernate OGM. E promette di farci lavorare con diversi archivi di dati NoSQL con solo la conoscenza di APP .

Per questo tutorial, definiremo un semplice modello a oggetti:

Definisce le classi Article , Author e Editor insieme alle loro relazioni.

Definiamoli anche in Java:

@Entity public class Article { @Id @GeneratedValue(generator = "uuid") @GenericGenerator(name = "uuid", strategy = "uuid2") private String articleId; private String articleTitle; @ManyToOne private Author author; // constructors, getters and setters... }
@Entity public class Author { @Id @GeneratedValue(generator = "uuid") @GenericGenerator(name = "uuid", strategy = "uuid2") private String authorId; private String authorName; @ManyToOne private Editor editor; @OneToMany(mappedBy = "author", cascade = CascadeType.PERSIST) private Set authoredArticles = new HashSet(); // constructors, getters and setters... }
@Entity public class Editor { @Id @GeneratedValue(generator = "uuid") @GenericGenerator(name = "uuid", strategy = "uuid2") private String editorId; private String editorName; @OneToMany(mappedBy = "editor", cascade = CascadeType.PERSIST) private Set assignedAuthors = new HashSet(); // constructors, getters and setters... }

Ora abbiamo definito le classi di entità e le abbiamo annotate con annotazioni standard JPA:

  • @Entity per stabilirli come entità JPA
  • @Id per generare chiavi primarie per le entità con UUID
  • @OneToMany e @ManyToOne per stabilire relazioni bidirezionali tra le entità

5. Operazioni

Ora che abbiamo creato le nostre entità, vediamo se possiamo eseguire alcune operazioni su di esse. Come primo passo, dovremo generare alcuni dati di prova. Qui creeremo un editor , alcuni autori e alcuni articoli. Stabiliremo anche le loro relazioni.

Successivamente, prima di poter eseguire qualsiasi operazione, avremo bisogno di un'istanza di EntityManagerFactory. Possiamo usarlo per creare EntityManager . Insieme a questo, dobbiamo creare TransactionManager per gestire i confini delle transazioni.

Vediamo come possiamo usarli per persistere e recuperare le entità che abbiamo creato in precedenza:

private void persistTestData(EntityManagerFactory entityManagerFactory, Editor editor) throws Exception { TransactionManager transactionManager = com.arjuna.ats.jta.TransactionManager.transactionManager(); transactionManager.begin(); EntityManager entityManager = entityManagerFactory.createEntityManager(); entityManager.persist(editor); entityManager.close(); transactionManager.commit(); }

Qui, stiamo usando EntityManager per rendere persistente l'entità radice, che si estende a tutte le sue relazioni. Stiamo anche eseguendo questa operazione entro un limite di transazione definito.

Ora siamo pronti per caricare l'entità che abbiamo appena mantenuto e verificarne il contenuto. Possiamo eseguire un test per verificarlo:

@Test public void givenMongoDB_WhenEntitiesCreated_thenCanBeRetrieved() throws Exception { EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("ogm-mongodb"); Editor editor = generateTestData(); persistTestData(entityManagerFactory, editor); TransactionManager transactionManager = com.arjuna.ats.jta.TransactionManager.transactionManager(); transactionManager.begin(); EntityManager entityManager = entityManagerFactory.createEntityManager(); Editor loadedEditor = entityManager.find(Editor.class, editor.getEditorId()); assertThat(loadedEditor).isNotNull(); // Other assertions to verify the entities and relations }

Qui, stiamo usando di nuovo EntityManager per trovare i dati ed eseguire asserzioni standard su di essi. Quando eseguiamo questo test, crea un'istanza del datastore, mantiene le entità, le recupera e verifica.

Again, we've just used JPA to persist the entities along with their relationship. Similarly, we use JPA to load the entities back and it all works fine, even when our database choice is MongoDB instead of a traditional relational database.

6. Switching Backend

We can also switch our backend. Let's find out now how difficult it'll be to do this.

We'll change our backend to Neo4j, which happens to be a popular graph-oriented datastore.

Firstly, let's add the Maven dependency for Neo4j:

 org.hibernate.ogm hibernate-ogm-neo4j 5.4.0.Final 

Next, we'll have to add the relevant persistence unit in our persistence.xml:

 org.hibernate.ogm.jpa.HibernateOgmPersistence      

In short, these are the very basic configurations required for Neo4j. This can be detailed further as required.

Bene, questo è più o meno quello che deve essere fatto. Quando eseguiamo lo stesso test con Neo4j del datastore backend, funziona abbastanza senza problemi.

Nota che abbiamo cambiato il nostro backend da MongoDB, che sembra essere un datastore orientato ai documenti, a Neo4j, che è un datastore orientato ai grafici. E abbiamo fatto tutto questo con modifiche minime e senza bisogno di modifiche in nessuna delle nostre operazioni .

7. Conclusione

In questo articolo, abbiamo esaminato le basi di Hibernate OGM, inclusa la sua architettura. Successivamente, abbiamo implementato un modello di dominio di base ed eseguito varie operazioni utilizzando vari DB.

Come sempre, il codice per gli esempi è disponibile su GitHub.