1. Введение
Ebean — это инструмент объектно-реляционного отображения, написанный на Java.
Он поддерживает стандартные аннотации JPA для объявления сущностей. Однако он предоставляет гораздо более простой API для сохранения. На самом деле, стоит упомянуть об архитектуре Ebean, что она не использует сеансы, то есть не полностью управляет сущностями.
Кроме того, он также поставляется с API запросов и поддерживает написание запросов на собственном SQL. Ebean поддерживает все основные поставщики баз данных, такие как Oracle, Postgres, MySql, H2 и т. д.
В этом руководстве мы рассмотрим, как мы можем создавать, сохранять и запрашивать сущности с помощью Ebean и H2.
2. Настройка
Для начала давайте получим наши зависимости, а также некоторую базовую конфигурацию.
2.1. Зависимости Maven
Прежде чем мы начнем, давайте импортируем необходимые зависимости:
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.22.4</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.196</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.6</version>
</dependency>
Последние версии Ebean , H2 и Logback можно найти на Maven Central.
2.2. Улучшения
Ebean необходимо модифицировать компоненты управления данными, чтобы они могли управляться сервером. Таким образом, мы добавим плагин Maven для выполнения этой работы:
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>11.11.2</version>
<executions>
<execution>
<id>main</id>
<phase>process-classes</phase>
<configuration>
<transformArgs>debug=1</transformArgs>
</configuration>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
Нам также необходимо предоставить подключаемому модулю Maven имена пакетов, содержащих сущности и классы, использующие транзакции. Для этого мы создаем файл ebean.mf:
entity-packages: com.foreach.ebean.model
transactional-packages: com.foreach.ebean.app
2.3. логирование
Давайте также создадим logback.xml
и установим уровни ведения журнала для некоторых пакетов на TRACE
, чтобы мы могли видеть выполняемые операторы:
<logger name="io.ebean.DDL" level="TRACE"/>
<logger name="io.ebean.SQL" level="TRACE"/>
<logger name="io.ebean.TXN" level="TRACE"/>
3. Настройка сервера
Нам нужно создать экземпляр EbeanServer
для сохранения сущностей или выполнения запросов к базе данных. Есть два способа создать экземпляр сервера — используя файл свойств по умолчанию или программно.
3.1. Использование файла свойств по умолчанию
Файл свойств по умолчанию может иметь тип properties
или yaml
. Ebean будет искать конфигурацию в файлах с именами application.properties
, ebean.properties
или application.yml
.
Помимо предоставления сведений о подключении к базе данных, мы также можем указать Ebean создавать и запускать операторы DDL.
Теперь давайте рассмотрим пример конфигурации:
ebean.db.ddl.generate=true
ebean.db.ddl.run=true
datasource.db.username=sa
datasource.db.password=
datasource.db.databaseUrl=jdbc:h2:mem:customer
datasource.db.databaseDriver=org.h2.Driver
3.2. Использование конфигурации сервера
Далее давайте посмотрим, как мы можем создать тот же сервер программно, используя EbeanServerFactory
и ServerConfig
:
ServerConfig cfg = new ServerConfig();
Properties properties = new Properties();
properties.put("ebean.db.ddl.generate", "true");
properties.put("ebean.db.ddl.run", "true");
properties.put("datasource.db.username", "sa");
properties.put("datasource.db.password", "");
properties.put("datasource.db.databaseUrl","jdbc:h2:mem:app2";
properties.put("datasource.db.databaseDriver", "org.h2.Driver");
cfg.loadFromProperties(properties);
EbeanServer server = EbeanServerFactory.create(cfg);
3.3. Экземпляр сервера по умолчанию
Один экземпляр EbeanServer
сопоставляется с одной базой данных. В зависимости от наших требований мы также можем создать более одного экземпляра EbeanServer
.
Если создается только один экземпляр сервера, по умолчанию он регистрируется как экземпляр сервера по умолчанию. Доступ к нему можно получить из любого места приложения с помощью статического метода класса Ebean
:
EbeanServer server = Ebean.getDefaultServer();
При наличии нескольких баз данных можно зарегистрировать один из экземпляров сервера как экземпляр по умолчанию:
cfg.setDefaultServer(true);
4. Создание сущностей
Ebean обеспечивает полную поддержку аннотаций JPA, а также дополнительные функции с использованием собственных аннотаций.
Давайте создадим несколько сущностей, используя аннотации JPA и Ebean. Во- первых, мы создадим BaseModel
, которая содержит свойства, общие для всех сущностей :
@MappedSuperclass
public abstract class BaseModel {
@Id
protected long id;
@Version
protected long version;
@WhenCreated
protected Instant createdOn;
@WhenModified
protected Instant modifiedOn;
// getters and setters
}
Здесь мы использовали аннотацию MappedSuperClass
JPA для определения BaseModel.
И две аннотации Ebean io.ebean.annotation.WhenCreated
и io.ebean.annotation.WhenModified
для целей аудита.
Далее мы создадим две сущности Customer
и Address
, которые расширяют BaseModel
:
@Entity
public class Customer extends BaseModel {
public Customer(String name, Address address) {
super();
this.name = name;
this.address = address;
}
private String name;
@OneToOne(cascade = CascadeType.ALL)
Address address;
// getters and setters
}
@Entity
public class Address extends BaseModel{
public Address(String addressLine1, String addressLine2, String city) {
super();
this.addressLine1 = addressLine1;
this.addressLine2 = addressLine2;
this.city = city;
}
private String addressLine1;
private String addressLine2;
private String city;
// getters and setters
}
В Customer
мы определили сопоставление один к одному с Address
и добавили set cascade type для ALL
, чтобы дочерние объекты также обновлялись вместе с родительскими объектами.
5. Основные операции CRUD
Ранее мы видели, как настроить EbeanServer
и создали две сущности. Теперь давайте проведем над ними некоторые базовые операции CRUD.
Мы будем использовать экземпляр сервера по умолчанию для сохранения и доступа к данным. Класс Ebean
также предоставляет статические методы для сохранения и доступа к данным, которые проксируют запрос к экземпляру сервера по умолчанию:
Address a1 = new Address("5, Wide Street", null, "New York");
Customer c1 = new Customer("John Wide", a1);
EbeanServer server = Ebean.getDefaultServer();
server.save(c1);
c1.setName("Jane Wide");
c1.setAddress(null);
server.save(c1);
Customer foundC1 = Ebean.find(Customer.class, c1.getId());
Ebean.delete(foundC1);
Во- первых, мы создаем объект Customer
и используем экземпляр сервера по умолчанию, чтобы сохранить его с помощью save()
.
Затем мы обновляем информацию о клиенте и снова сохраняем ее с помощью save()
.
Наконец, мы используем статический метод find()
в Ebean
, чтобы получить клиента и удалить его с помощью delete()
.
6. Запросы
API запросов также можно использовать для создания графа объектов с фильтрами и предикатами. Мы можем использовать Ebean
или EbeanServer
для создания и выполнения запросов.
Давайте рассмотрим запрос, который находит клиента
по городу и возвращает объект клиента
и адреса
с заполненными только некоторыми полями:
Customer customer = Ebean.find(Customer.class)
.select("name")
.fetch("address", "city")
.where()
.eq("city", "San Jose")
.findOne();
Здесь с помощью find()
мы указываем, что хотим найти сущности типа Customer
. Затем мы используем select()
, чтобы указать свойства, которые должны быть заполнены в объекте Customer .
Позже мы используем fetch()
, чтобы указать, что мы хотим получить объект Address
, принадлежащий Customer
, и что мы хотим получить поле города
.
Наконец, мы добавляем предикат и ограничиваем размер результата до 1.
7. Транзакции
Ebean по умолчанию выполняет каждый оператор или запрос в новой транзакции.
Хотя в некоторых случаях это может и не быть проблемой. Бывают случаи, когда мы можем захотеть выполнить набор операторов в рамках одной транзакции.
В таких случаях, если мы аннотируем метод с помощью io.ebean.annotations.Transactional,
все операторы внутри метода будут выполняться внутри одной и той же транзакции:
@Transactional
public static void insertAndDeleteInsideTransaction() {
Customer c1 = getCustomer();
EbeanServer server = Ebean.getDefaultServer();
server.save(c1);
Customer foundC1 = server.find(Customer.class, c1.getId());
server.delete(foundC1);
}
8. Создание проекта
Наконец, мы можем собрать проект Maven с помощью команды:
compile io.ebean:ebean-maven-plugin:enhance
9. Заключение
Подводя итог, мы рассмотрели основные функции Ebean, которые можно использовать для сохранения и запроса объектов в реляционной базе данных.
Наконец, этот код доступен на Github .