[Перевод] Как упростить доступ к данным с MySQL и Jakarta Data
Многие приложения, особенно в сегменте enterprise, сохраняют или получают доступ к данным в какой-либо форме. Реляционные базы данных по-прежнему остаются самым популярным механизмом для управления данными, несмотря на конкуренцию со стороны таких технологий, как NoSQL базы данных. В этой статье рассмотрим некоторые концепции доступа к данным и то, как новая спецификация Jakarta Data упрощает эту задачу для разработчиков приложений.
Сохранение данных
Я начну обсуждение с обзора основных концепций сохранения данных. Если вы с ними уже знакомы, можете пропустить этот раздел и перейти к следующему, где представлены примеры кода.
CRUD
Чаще всего в приложениях для работы с данными используются следующие операции — создание, чтение, обновление и удаление (CRUD: create, read, update, delete). CRUD-операции обычно применяются с реляционными базами данных, но их можно также применять к любым механизмам управления данными. Написание кода для этих операций, как правило, представляет собой повторяющуюся задачу, которая в основном состоит из шаблонного кода.
ORM
Объектно-реляционное отображение, или преобразование (ORM, Object-relational mapping), как следует из названия, занимается преобразованием объектов в объектно-ориентированном языке программирования в данные реляционной базы данных. Есть множество ORM-фреймворков, которые помогают разработчикам выполнять эту задачу. Jakarta Persistence, ранее известная как JPA, представляет собой спецификацию, которая стандартизирует управление сохранением данных и объектно-реляционное отображение для Java-приложений.
Шаблон Repository
Существует множество шаблонов и стратегий, таких как Data Access Object («объект доступа к данным») (DAO), Repository («репозиторий»), Active Record («активная запись») и другие, которые часто используются для структурирования кода, связанного с CRUD-операциями. В этой статье я использую шаблон Repository.
Цель шаблона Repository (описанного в книге Мартина Фаулера Patterns of Enterprise Application Architecture) — исключить детали, связанные с сохранением данных, из доменной модели приложения. Репозитории представляют собой классы, инкапсулирующие логику доступа к данным, что позволяет разделить механизмы сохранения и доменную модель.
Шаблон Repository стал популярным и широко используется благодаря таким технологиям, как Spring Data. Именно Spring Data вдохновил разработчиков Jakarta Data.
Jakarta Data
Jakarta Data — это новая спецификация, предложенная для включения в Jakarta EE 11 (прим. пер: релиз Jakarta Data 1.0 состоялся 30 сентября 2024 года). Реализуя шаблон Repository, Jakarta Data упрощает доступ к данным и сокращает объём шаблонного кода. Разработчику нужно лишь определить интерфейс, представляющий репозиторий, и сущность, представляющую таблицу базы данных. Реализация Jakarta Data предоставит готовую реализацию репозитория.
Пример с MySQL
Этот простой пример демонстрирует, как Jakarta Data упрощает работу с сохранением данных, устраняя необходимость в шаблонном коде. В этом примере используются следующие технологии:
Также потребуется установить Apache Maven и JDK. Этот код проверен на Java 20, но может работать и с другими версиями.
В качестве среды выполнения в примере используется Open Liberty. Однако, если у вас есть другая реализация, вы сможете заменить Open Liberty без изменения кода.
Шаг 1. Убедитесь, что Apache Maven и JDK установлены.
Вы должны увидеть что-то вроде:
$ mvn --version
Apache Maven 3.8.2 (ea98e05a04480131370aa0c110b8c54cf726c06f)
Maven home: /home/ivar/.sdkman/candidates/maven/current
Java version: 20.0.1, vendor: Eclipse Adoptium, runtime: /home/ivar/.sdkman/candidates/java/20.0.1-tem
Default locale: en_US, platform encoding: UTF-8
Шаг 2. Установите и настройте MySQL.
Скачайте MySQL с сайта или используйте любимый менеджер пакетов. Вот пример команды для Ubuntu:
$ sudo apt-get install mysql-server
Шаг 3. Войдите в MySQL Shell
sudo mysql -u root
Шаг 4. Создайте базу данных и пользователя
mysql> create database dukes_data;
mysql> use dukes_data;
mysql> create user 'duke'@'localhost' identified by 'duke';
mysql> grant all privileges on dukes_data to 'duke'@'localhost';
Шаг 5. Загрузите код из моего репозитория на GitHub, а затем скомпилируйте и запустите его с помощью Maven.
Для этого выполните следующую команду:
$ mvn liberty:run
Теперь приложение готово для использования.
Шаг 6. Доступны три конечных точки (эндпоинта):
Получить список всех приветствий (GET)
Найти приветствие по имени отправителя (GET)
Добавить новое приветствие (POST)
Вот как это работает.
Чтобы получить список всех приветствий, используйте следующий запрос:
http://localhost:9080/dukes-data/api/greetings/
Ожидаемый ответ, так как данных пока нет:
[]
Чтобы найти приветствие от Дьюка, используйте следующий запрос:
http://localhost:9080/dukes-data/api/greetings/duke
Поскольку данных пока нет, вы получите следующий ответ:
duke not found
Чтобы добавить приветствие от Дьюка, выполните запрос POST с необходимыми данными.
$ echo -n '{"message":"Hello from Duke", "name":"Duke"}' | http post :9080/dukes-data/api/greetings
Чтобы снова получить список всех приветствий, используйте:
http://localhost:9080/dukes-data/api/greetings/
Теперь вы должны увидеть ожидаемый ответ, который включает добавленное приветствие.
[
{
id: 1,
message: "Hello from Duke",
name: "Duke"
}
]
Наконец, чтобы снова найти приветствие от Дьюка, используйте следующий запрос:
http://localhost:9080/dukes-data/api/greetings/duke
Ожидаемый ответ:
Hello from Duke
Пример кода
Приложение состоит из четырёх классов: GreetingApplication
, GreetingResource
, Greeting
и GreetingRepository
.
GreetingApplication
Этот класс настраивает приложение Jakarta REST. В данном случае требуется только указание пути приложения.
package dukes.data;
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;
@ApplicationPath("/api")
public class GreetingApplication extends Application {
}
GreetingResource
Этот класс предоставляет три API-метода: для получения всех приветствий, получения одного приветствия и добавления нового приветствия.
package dukes.data;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.List;
@Path("/greetings")
public class GreetingResource {
@Inject
private GreetingRepository greetingRepository;
@GET
@Path("/{name}")
@Produces(MediaType.TEXT_PLAIN)
public String findOne(@PathParam("name") String name) {
return greetingRepository.findByNameIgnoreCase(name)
.map(Greeting::getMessage)
.orElse(name + " not found");
}
@GET
@Produces(MediaType.APPLICATION_JSON)
public List findAll() {
return greetingRepository.findAll()
.toList();
}
@POST()
@Consumes(MediaType.APPLICATION_JSON)
public Response addGreeting(Greeting greeting) {
Greeting saved = greetingRepository.save(greeting);
return Response.ok("Created greeting: " + greeting.getId()).build();
}
}
Greeting
Класс Greeting определяет сущность, которая сохраняется в базе данных — это сущность Jakarta Persistence с тремя полями.Аннотация
@Entity
определяет её как сущность Jakarta Persistence.Аннотации
@Id
и@GeneratedValue
указывают первичный ключ и способ его генерации.
Помимо этого, это обычный Java-класс (POJO).
package dukes.data;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class Greeting {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String message;
// constructor/getters/setters
}
GreetingRepository
Здесь начинается самое интересное. Это простой интерфейс, который расширяет CrudRepository и аннотирован@Repository
. Этого достаточно, чтобы реализация Jakarta Data сгенерировала методы для всех CRUD-операций, а также несколько удобных методов, таких какcount
,existsById
и различные методы поиска.
package dukes.data;
import jakarta.data.repository.CrudRepository;
import jakarta.data.repository.Repository;
import java.util.Optional;
@Repository
public interface GreetingRepository extends CrudRepository {
Optional findByNameIgnoreCase(String name);
}
Единственный метод, определённый разработчиком, — это findByNameIgnoreCase. Как следует из названия, этот метод ищет в базе данных строки с указанным именем. Jakarta Data сгенерирует метод, который выполняет именно это.
Заключение
Jakarta Data — это очень интересное дополнение к Jakarta EE. Оно повышает продуктивность разработчиков и качество кода, избавляет от необходимости писать подверженный ошибкам шаблонный код. Jakarta Data теперь доступна во всех продуктах, совместимых с платформой Jakarta EE 11.
Jakarta Data и Jakarta Persistence полностью абстрагированы от используемого механизма сохранения данных, поэтому можно использовать любую реляционную базу данных. В этом примере была использована MySQL. Она остаётся одной из самых популярных баз данных благодаря ряду причин:
бесплатная и открытая лицензия,
доступность на множестве платформ,
простота установки и начала работы,
наличие обширной документации и ресурсов в интернете.
При необходимости доступны также коммерческие варианты MySQL.
Как видно, Jakarta Data предлагает богатый язык и аннотации для создания методов запросов с широким выбором параметров сортировки. Обязательно ознакомьтесь с этой спецификацией и делитесь своими отзывами с проектом, который занимается её разработкой.
В заключение всех начинающих Java-разработчиков приглашаем на открытые уроки, которые пройдут в январе:
16 января: «Реализация простого HTTP-сервера на Java Core». Урок поможет вам лучше понять принципы работы application server’ов и контейнеров сервлетов. Записаться
28 января: «Алгоритмическая сложность коллекций». Разберём принципы, которые играют ключевую роль для повышения производительности кода. Записаться