В основе любой программы лежит какая-то определённая предметная область. Например, графический редактор имеет дело с геометрическими примитивами, такими как отрезки, круги или квадраты, а сервисы доставки еды занимаются логистикой.
Предметная область включает в себя набор сущностей, которые взаимодействуют друг с другом, а также правила их взаимодействия, называемые бизнес-логикой. Правила, работающие в конкретной предметной области, никак не связаны ни с языком ни с фреймворком, на которых пишется программа. Более того, они вообще не имеют никакого отношения к программированию и существуют независимо.
Для упрощения описания предметной области и хранения её данных в приложениях применяют ORM. ORM — это специализированный фреймворк,который помогает моделировать предметную область и связывать её с базой данных
В этом уроке мы познакомимся с концепциями ORM на примере фреймворка Ebean, так как он имеет простой интерфейс и лёгок в настройке.
// Файл build.gradle
// Подключаем плагин, который расширяет возможности Ebean
plugins {
id 'io.ebean' version '12.11.5'
}
// Добавляем необходимые зависимости
dependencies {
implementation 'io.ebean:ebean:12.11.5'
// Позволяет генерировать миграции
implementation 'io.ebean:ebean-ddl-generator:12.11.5'
// Query builder. Позволяет строить запросы
implementation 'io.ebean:ebean-querybean:12.11.5'
// Позволяет автоматически выполнять миграции
implementation 'io.ebean:ebean-migration:12.11.2'
// Добавляет различные аннотации
implementation 'io.ebean:ebean-annotation:7.3'
// Обработчик аннотаций используется для генерации компонента запроса
annotationProcessor 'io.ebean:querybean-generator:12.11.5'
}
Конфигурирование ORM Ebean выполняется в файле application.yaml
# Файл src/main/resources/application.yml
datasource:
# Задаём базу данных, к которой будет происходить подключение
db:
# Имя пользователя и пароль
username: ""
password: ""
# Драйвер базы данных и URL
# Используем embedded вариант базы H2
# БД располагается в файле в текущей директории
url: "jdbc:h2:./hexlet"
Миграция – это SQL-запрос, который выполняется в базе данных при любом её изменении. Например, в данном случае запрос создаёт таблицу customer
:
create table customer (
id bigint generated by default as identity not null,
name varchar(255),
constraint pk_article primary key (id)
);
Ebean имеет встроенную поддержку для генерации миграций БД и их запуска. Для генерации миграции нужно выполнить метод main()
генератора миграций:
// Файл src/main/java/project/MigrationGenerator.java
public final class MigrationGenerator {
public static void main(String[] args) throws IOException {
// Создаём миграцию
DbMigration dbMigration = DbMigration.create();
// Указываем платформу, в нашем случае H2
dbMigration.addPlatform(Platform.H2, "h2");
// Генерируем миграцию
dbMigration.generateMigration();
}
}
Чтобы не запускать метод вручную, можно создать для этого таску Gradle:
// Файл build.gradle
task generateMigrations(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
mainClass = 'exercise.MigrationGenerator'
}
Запуск этой таски gradle generateMigrations
сгенерирует файл миграции на основании модели.
Чтобы миграции выполнялись автоматически при старте Ebean, нужно добавить в файл application.yaml следующий код:
# Файл application.yaml
ebean:
migration:
run: true
Модель — это воплощение понятия предметной области в коде приложения. В Ebean каждая модель представлена классом в коде и таблицей в базе данных
// Файл src/main/java/project/domain/Customer.java
// Аннотация @Entity обозначает, что класс является моделью
@Entity
// Класс модели наследуется от класса io.ebean.Model
// Благодаря этому у модели появляется метод save() для добавления сущности в БД
public class Customer extends Model {
// Аннотация обозначает, что поле класса является автогенерируемым первичным ключом
@Id
long id;
String name;
public Customer(String name) {
this.name = name;
}
// Геттеры для получения значения полей
}
Query builder — это конструктор запросов. Он предоставляет удобный, выразительный интерфейс для создания и выполнения запросов к базе данных. Цепочка вызовов методов поэтапно конструирует необходимый запрос.
// Вставка данных в таблицу
// Создаём новый экземпляр модели
Customer customer = new Customer("Andrey");
// И вызываем наследуемый метод save()
customer.save()
// Выборка данных
// Для каждой сущности автоматически создается компонент запроса
// с тем же именем, но с префиксом Q
// Например для Customer создается компонент запроса с именем QCustomer
Customer customer = new QCustomer()
.name.equalTo("Ivan") // Ищем совпадение по имени
.findOne();
// Метод findOne() указывает на окончание запроса и выполняет его в базе данных
// Ebean автоматически конвертирует вернувшиеся данные в объекты модели
// и возвращает их наружу.
// Получение списка сущностей
List<Customer> customers = new QCustomer()
.name.equalTo("Ivan")
.orderBy() // Сортировка по id в порядке возрастания
.id.asc()
.findList();
// Модификация данных
new QCustomer()
.id.equalTo(10)
.asUpdate() // Преобразует запрос в UPDATE
.set("name", "Rob") // Устанавливаем новое значение поля
.update(); // Выполняем обновление
// Другие примеры запросов можно посмотреть в документации
// по ссылке в дополнительных материалах
import exercise.domain.query.QCompany;
// Пейджинг
int companiesPerPage = 10; // Количество компаний на странице
int offset = 20; // Смещение
PagedList<Company> pagedCompanies = new QCompany()
// Устанавливаем смещение
.setFirstRow(offset)
// Устанавливаем максимальное количество записей в результате
.setMaxRows(companiesPerPage)
// Задаём сортировку по имени компании
.orderBy()
.name.asc()
// Получаем список PagedList, который представляет одну страницу результата
.findPagedList();
// Получаем список компаний
List<Company> companies = pagedCompanies.getList();
// Получение данных одной компании по её имени
Company company = new QCompany()
.name.equalTo("Google")
.findOne();
// Получение списка всех компаний из базы
List<Company> companies = new QCompany().findList();
// Добавление новой компании в базу данных
Company newCompany = new Company("Apple");
// Благодаря наследованию класса модели от класса io.ebean.Model,
// на модели можно вызывать метод save() для добавления новой записи в базу
newCompany.save();
Вам ответят команда поддержки Хекслета или другие студенты.
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
Наши выпускники работают в компаниях:
С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.
Зарегистрируйтесь или войдите в свой аккаунт
Задавайте вопросы, если хотите обсудить теорию или упражнения. Команда поддержки Хекслета и опытные участники сообщества помогут найти ответы и решить задачу