«1. Обзор

В этом уроке мы рассмотрим основы Hibernate Object/Grid Mapper (OGM).

Hibernate OGM обеспечивает поддержку Java Persistence API (JPA) для хранилищ данных NoSQL. NoSQL — это общий термин, охватывающий широкий спектр хранилищ данных. Например, сюда входят хранилища данных типа \»ключ-значение\», \»документ\», \»столбец\» и \»график\».

2. Архитектура Hibernate OGM

Hibernate традиционно предлагает механизм объектно-реляционного отображения (ORM) для реляционных баз данных. Механизм Hibernate OGM расширяет свои функциональные возможности для поддержки хранилищ данных NoSQL. Основным преимуществом его использования является согласованность интерфейса JPA в реляционных хранилищах и хранилищах данных NoSQL.

Hibernate OGM может обеспечить абстракцию над несколькими хранилищами данных NoSQL благодаря двум ключевым интерфейсам, DatastoreProvider и GridDialect. Поэтому каждое новое хранилище данных NoSQL, которое оно поддерживает, поставляется с реализацией этих интерфейсов.

На сегодняшний день он поддерживает не все хранилища данных NoSQL, но способен работать со многими из них, такими как Infinispan и Ehcache (ключ-значение), MongoDB и CouchDB (документ) и Neo4j (граф).

Он также полностью поддерживает транзакции и может работать со стандартными провайдерами JTA. Во-первых, это можно сделать через контейнер Jakarta EE без какой-либо явной настройки. Более того, мы можем использовать автономный менеджер транзакций JTA, такой как Narayana, в среде Java SE.

3. Настройка

В этом руководстве мы будем использовать Maven для получения необходимых зависимостей для работы с Hibernate OGM. Мы также будем использовать MongoDB.

Чтобы внести ясность, давайте посмотрим, как настроить их для урока.

3.1. Зависимости Maven

Давайте посмотрим на зависимости, необходимые для работы с Hibernate OGM и MongoDB:

<dependency>
    <groupId>org.hibernate.ogm</groupId>
    <artifactId>hibernate-ogm-mongodb</artifactId>
    <version>5.4.0.Final</version>
</dependency>
<dependency>
    <groupId>org.jboss.narayana.jta</groupId>
    <artifactId>narayana-jta</artifactId>
    <version>5.9.2.Final</version>
</dependency>

Здесь мы извлекаем необходимые зависимости через Maven:

    Диалект Hibernate OGM для MongoDB Narayana Transaction Manager (фактический поставщик JTA)

3.2. Persistence Unit

Нам также нужно определить детали хранилища данных в файле Hibernate persistance.xml:

<persistence-unit name="ogm-mongodb" transaction-type="JTA">
    <provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>
    <properties>
        <property name="hibernate.ogm.datastore.provider" value="MONGODB" />
        <property name="hibernate.ogm.datastore.database" value="TestDB" />
        <property name="hibernate.ogm.datastore.create_database" value="true" />
    </properties>
</persistence-unit>

Обратите внимание на приведенные здесь определения:

    значение атрибута transaction-type как «JTA». € (это означает, что нам нужен менеджер сущностей JTA из EntityManagerFactory) провайдер, который является HibernateOgmPersistence для Hibernate OGM несколько дополнительных деталей, связанных с БД (они обычно различаются между различными источниками данных)

Конфигурация предполагает, что MongoDB работает и доступны по умолчанию. Если это не так, мы всегда можем предоставить подробную информацию по мере необходимости. В одной из наших предыдущих статей также подробно рассматривается настройка MongoDB.

4. Определение сущностей

Теперь, когда мы рассмотрели основы, давайте определим некоторые сущности. Если раньше мы работали с Hibernate ORM или JPA, то здесь больше добавить нечего. Это основная предпосылка Hibernate OGM. Он обещает позволить нам работать с различными хранилищами данных NoSQL, зная только JPA.

Для этого урока мы определим простую объектную модель:

Она определяет классы Article, Author и Editor вместе с их отношениями.

Давайте также определим их в 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<Article> 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<Author> assignedAuthors = new HashSet<>();

    // constructors, getters and setters...
}

    Теперь мы определили классы сущностей и аннотировали их стандартными аннотациями JPA:

@Entity, чтобы установить их как сущности JPA @Id для создания первичных ключей для сущностей с UUID @OneToMany и @ManyToOne для установления двунаправленных отношений между сущностями

5. Операции

Теперь, когда мы создали наши сущности, давайте посмотрим, сможем ли мы выполнить над ними некоторые операции. В качестве первого шага нам нужно сгенерировать некоторые тестовые данные. Здесь мы создадим редактора, нескольких авторов и статью. Мы также установим их отношения.

После этого, прежде чем мы сможем выполнить какую-либо операцию, нам понадобится экземпляр EntityManagerFactory. Мы можем использовать это для создания EntityManager. Наряду с этим нам нужно создать TransactionManager для обработки границ транзакций.

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();
}

Давайте посмотрим, как мы можем использовать их для сохранения и извлечения ранее созданных сущностей:

«

@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
}

«Здесь мы используем EntityManager для сохранения корневого объекта, который распространяется на все его отношения. Мы также выполняем эту операцию в рамках определенной границы транзакции.

Теперь мы готовы загрузить объект, который мы только что сохранили, и проверить его содержимое. Мы можем запустить тест, чтобы убедиться в этом:

Здесь мы снова используем EntityManager для поиска данных и выполнения над ними стандартных утверждений. Когда мы запускаем этот тест, он создает хранилище данных, сохраняет объекты, извлекает их обратно и проверяет.

Опять же, мы только что использовали JPA для сохранения объектов вместе с их отношениями. Точно так же мы используем JPA для обратной загрузки сущностей, и все работает нормально, даже если мы выбираем базу данных MongoDB вместо традиционной реляционной базы данных.

6. Переключение бэкэнда

Мы также можем переключить наш бэкэнд. Давайте теперь узнаем, насколько сложно это будет сделать.

<dependency>
    <groupId>org.hibernate.ogm</groupId>
    <artifactId>hibernate-ogm-neo4j</artifactId>
    <version>5.4.0.Final</version>
</dependency>

Мы изменим наш бэкэнд на Neo4j, который оказался популярным графоориентированным хранилищем данных.

<persistence-unit name="ogm-neo4j" transaction-type="JTA">
    <provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>
    <properties>
        <property name="hibernate.ogm.datastore.provider" value="NEO4J_EMBEDDED" />
        <property name="hibernate.ogm.datastore.database" value="TestDB" />
        <property name="hibernate.ogm.neo4j.database_path" value="target/test_data_dir" />
    </properties>
</persistence-unit>

Во-первых, давайте добавим зависимость Maven для Neo4j:

Затем нам нужно будет добавить соответствующий модуль сохранения в наш файл persistence.xml:

Короче говоря, это те самые базовые конфигурации, необходимые для Neo4j. При необходимости это можно детализировать.

Ну, это почти то, что нужно сделать. Когда мы запускаем тот же тест с Neo4j в качестве внутреннего хранилища данных, он работает без проблем.

Обратите внимание, что мы переключили наш сервер с MongoDB, который является хранилищем данных, ориентированным на документы, на Neo4j, который является хранилищем данных, ориентированным на графы. И все это мы сделали с минимальными изменениями и без каких-либо изменений ни в одной из наших операций.

7. Заключение