«1. Обзор

Apache Cayenne — это библиотека с открытым исходным кодом, распространяемая по лицензии Apache и предоставляющая такие функции, как инструмент моделирования, объектно-реляционное сопоставление, также известное как ORM, для операций локального сохранения и удаленных служб.

В следующих разделах мы увидим, как взаимодействовать с базой данных MySQL с помощью Apache Cayenne ORM.

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

Для начала нам просто нужно добавить следующие зависимости, чтобы вызвать Apache Cayenne и MySQL, соединив драйвер JDBC вместе для доступа к нашей базе данных intro_cayenne:

<dependency>
    <groupId>org.apache.cayenne</groupId>
    <artifactId>cayenne-server</artifactId>
    <version>4.0.M5</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.44</version>
    <scope>runtime</scope>
</dependency>

Давайте настроим подключаемый модуль Cayenne modeler. который будет использоваться для разработки или настройки нашего файла сопоставления, который действует как мост между схемой базы данных и объектом Java:

<plugin>
    <groupId>org.apache.cayenne.plugins</groupId>
    <artifactId>maven-cayenne-modeler-plugin</artifactId>
    <version>4.0.M5</version>
</plugin>

Вместо создания XML-файла сопоставления вручную (что делается редко) рекомендуется использовать средство моделирования довольно продвинутый инструмент, входящий в состав дистрибутива Cayenne.

Он доступен для загрузки из этого архива в зависимости от вашей ОС или просто используйте кросс-платформенную версию (JAR), включенную в качестве плагина Maven.

В репозитории Maven Central хранятся последние версии Apache Cayenne, его средства моделирования и MySQL Connector.

Далее давайте создадим наш проект с помощью mvn install и запустим графический интерфейс средства моделирования с помощью команды mvn cayenne-modeler:run, чтобы получить в качестве вывода этот экран:

3. Настройка

Чтобы заставить Apache Cayenne искать правильная локальная база данных, нам просто нужно заполнить его файл конфигурации с правильным драйвером, URL-адресом и пользователем в файле cayenne-project.xml, расположенном в каталоге ресурсов:

<?xml version="1.0" encoding="utf-8"?>
<domain project-version="9">
    <node name="datanode"
          factory
      ="org.apache.cayenne.configuration.server.XMLPoolingDataSourceFactory"
          schema-update-strategy
      ="org.apache.cayenne.access.dbsync.CreateIfNoSchemaStrategy">
        <data-source>
            <driver value="com.mysql.jdbc.Driver"/>
            <url value
              ="jdbc:mysql://localhost:3306/intro_cayenne;create=true"/>
            <connectionPool min="1" max="1"/>
            <login userName="root" password="root"/>
        </data-source>
    </node>
</domain>

Здесь мы видим, что: ~~ ~ Локальная база данных называется intro_cayenne Если она еще не создана, Cayenne сделает это за нас Мы подключимся, используя имя пользователя root и пароль root (измените его в соответствии с пользователями, зарегистрированными в вашей системе управления базами данных)

    Внутренне это XMLPoolingDataSourceFactory отвечает за загрузку информации о соединении JDBC из ресурса XML, связанного с DataNodeDescriptor.

Имейте в виду, что эти параметры относятся к системе управления базами данных и драйверу JDBC, поскольку эта библиотека может поддерживать множество различных баз данных.

У каждого из них есть адаптер, доступный в этом подробном списке. Обратите внимание, что полная документация для версии 4.0 еще недоступна, поэтому здесь мы ссылаемся на предыдущую версию.

4. Отображение и дизайн базы данных

4.1. Моделирование

Давайте теперь нажмем «Открыть проект», перейдите в папку ресурсов проекта и выберите файл cayenne-project.xml, моделлер покажет это:

Здесь, у нас есть выбор: либо создать нашу структуру сопоставления из существующей базы данных, либо продолжить работу вручную. В этой статье рассматривается тот, кто использует модельер и существующую базу данных, чтобы войти в Cayenne и быстро узнать, как это работает.

Давайте взглянем на нашу базу данных intro_cayenne, которая имеет отношение «один ко многим» между двумя таблицами, поскольку автор может публиковать или владеть многими статьями:

author: id (PK) и name article: id (PK) ), title, content и author_id(FK)

    Теперь давайте перейдем в «Инструменты \u003e Реинжиниринг схемы базы данных», и все наши конфигурации сопоставления будут заполнены автоматически. На экране подсказки просто заполните конфигурацию источника данных, доступную там, в файле cayenne-project.xml, и нажмите «Продолжить»:

На следующем экране нам нужно отметить «Использовать примитивные типы Java». ‚¬ следующим образом:

Нам также необходимо поместить com.baeldung.apachecayenne.persistent в качестве пакета Java и сохранить его; мы увидим, что файл конфигурации XML был обновлен для свойства defaultPackage в соответствии с пакетом Java:

В каждом объекте ObjEntity мы должны указать пакет для подклассов, как показано на следующем рисунке, и нажать кнопку  значок «сохранить» снова:

Теперь в меню «Инструменты» \u003e «Создать классы» выберите «Стандартные постоянные объекты» в качестве типа; и на вкладке «Классы» проверьте все классы и нажмите «Создать».

Вернемся к исходному коду, чтобы увидеть, что наши постоянные объекты были успешно сгенерированы, говоря о _Article.java и _Author.java.

«Обратите внимание, что все эти конфигурации сохраняются в файле datamap.map.xml, который также находится в папке ресурсов.

4.2. Структура сопоставления

Сгенерированный XML-файл сопоставления, представленный в папке ресурсов, использует некоторые уникальные теги относительно Apache Cayenne:

DataNode(\u003cnode\u003e) — модель базы данных, ее содержимое, вся информация, необходимая для получения подключен к базе данных (имя базы данных, драйвер и учетные данные пользователя) DataMap(\u003cdata-map\u003e) — это контейнер постоянных сущностей с их отношениями DbAttribute(\u003cdb-attribute\u003e) — представляет столбец в таблице базы данных DbEntity(\u003cобъект-базы-данных\u003e) — модель отдельной таблицы или представления базы данных, может иметь атрибуты DbAttributes и связи. ObjEntity(\u003cобъект-сущность\u003e) — модель одного постоянного Java-класс; состоит из ObjAttributes, которые соответствуют свойствам класса сущностей, и ObjRelationships, которые являются свойствами, имеющими тип другой сущности. Embeddable(\u003cembeddable\u003e) — модель класса Java, которая действует как свойство ObjEntity, но соответствует нескольким столбцам. в базе данных. код

    Вот полная информация.

5. Cayenne API

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

5.1. Создание объекта

Здесь мы просто сохраняем объект Author и позже проверяем, что в базе данных есть только одна запись этого типа:

5.2. Чтение объекта

@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.3. Получение всех записей класса

@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.4. Обновление объекта

@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<Author> authors = ObjectSelect
      .query(Author.class)
      .select(context);
 
    assertEquals(2, authors.size());
}

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

5.5. Прикрепление объекта

@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.6. Удаление объекта

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

Удаление сохраненного объекта полностью удаляет его из базы данных, после чего в результате запроса мы увидим null:

5.7. Удалить все записи класса

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

Также можно удалить все записи таблицы с помощью SQLTemplate, здесь мы делаем это после каждого тестового метода, чтобы всегда иметь пустую базу данных перед запуском каждого теста:

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

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

В этом руководстве мы сосредоточились на использовании Apache Cayenne ORM, чтобы легко продемонстрировать, как выполнять операции CRUD с отношением «один ко многим».

Как всегда, исходный код этой статьи можно найти на GitHub.

«