Перейти к основному содержимому

Введение в Apache Cayenne ORM

· 7 мин. чтения

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 , чтобы получить в качестве вывода этот экран:

./03ce57651389022f07e9d80a552d6d1c.png

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, моделлер покажет это:

./910209bde4e5e0b10035eb3e02dbaaa3.png

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

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

  • автор: id (PK) и имя
  • статья: id (PK), title, content и author_id (FK)

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

./704f9151c5e26c7cddb79e88a9132c25.png

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

./8780a2d4917fa2ac68537c2397af86a1.png

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

./f00168baf14c48a522462b760d16ff34.png

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

./01c2f68d3a487e7a42a2b6231fddeec2.png

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

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

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

4.2. Структура отображения

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

  • DataNode(<node>) — модель базы данных, ее содержимое, вся информация, необходимая для подключения к базе данных (имя базы данных, драйвер и учетные данные пользователя).
  • DataMap(<data-map>) — это контейнер постоянных сущностей с их отношениями
  • DbAttribute(<db-attribute>) — представляет столбец в таблице базы данных.
  • DbEntity(<db-entity>) — модель отдельной таблицы или представления базы данных, может иметь DbAttributes и отношения
  • ObjEntity(<obj-entity>) — модель одного персистентного java-класса; состоит из ObjAttributes, которые соответствуют свойствам класса сущностей, и ObjRelationships, которые являются свойствами, имеющими тип другой сущности
  • Embeddable(<embeddable>) — модель класса Java, которая действует как свойство ObjEntity, но соответствует нескольким столбцам в базе данных.
  • Процедура(<процедура>) – для регистрации хранимой процедуры в базе данных
  • Query(<query>) — модель запроса, используемая для сопоставления запроса в файле конфигурации, не забывая, что мы также можем сделать это в коде.

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

5. Кайенский API

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

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

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

@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. Чтение объекта

После сохранения Author мы просто выбираем его среди других с помощью простого запроса по определенному свойству:

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

Мы собираемся сохранить двух авторов и получить коллекцию объектов авторов, чтобы убедиться, что сохранены только эти два:

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

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

@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. Прикрепление объекта

Мы можем назначить статью автору:

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

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

@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. Удалить все записи класса

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

@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. Заключение

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

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