Статьи

Операции базы данных CRUD в потоках Java 8

Самое большое препятствие, которое нужно преодолеть, когда вы начинаете с новым инструментом, — это научиться делать мелочи. К настоящему времени вы можете быть уверены в том, как работает новый Java 8 Stream API, но вы, возможно, еще не использовали его для запросов к базе данных. Чтобы помочь вам начать создавать, изменять и читать из базы данных SQL с помощью Stream API, я собрал этот краткий справочник. Надеюсь, это поможет вам поднять ваши потоки на следующий уровень!

Фон

Speedment — это набор инструментов с открытым исходным кодом, который можно использовать для создания Java-сущностей и менеджеров для связи с базой данных. Используя графический инструмент, вы подключаетесь к своей базе данных и генерируете полный ORM с учетом вашей модели домена. Но Speedment — это не только генератор кода, но и среда выполнения, которая подключается к вашему приложению и позволяет преобразовывать потоки Java 8 в оптимизированные запросы SQL. Это та часть, на которой я остановлюсь в этой статье.

Генерировать код

Чтобы начать использовать Speedment в проекте Maven, добавьте следующие строки в ваш файл pom.xml. В этом примере я использую MySQL, но вы также можете использовать PostgreSQL или MariaDB. Соединители с частными базами данных, такими как Oracle, доступны для корпоративных клиентов.

pom.xml

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<properties>
  <speedment.version>3.0.1</speedment.version>
  <db.groupId>mysql</db.groupId>
  <db.artifactId>mysql-connector-java</db.artifactId>
  <db.version>5.1.39</db.version>
</properties>
 
<dependencies>
  <dependency>
    <groupId>com.speedment</groupId>
    <artifactId>runtime</artifactId>
    <version>${speedment.version}</version>
    <type>pom</type>
  </dependency>
         
  <dependency>
    <groupId>${db.groupId}</groupId>
    <artifactId>${db.artifactId}</artifactId>
    <version>${db.version}</version>
  </dependency>
</dependencies>
 
<build>
  <plugins>
    <plugin>
      <groupId>com.speedment</groupId>
      <artifactId>speedment-maven-plugin</artifactId>
      <version>${speedment.version}</version>
 
      <dependencies>
        <dependency>
          <groupId>${db.groupId}</groupId>
          <artifactId>${db.artifactId}</artifactId>
          <version>${db.version}</version>
        </dependency>
      </dependencies>
    </plugin>
  </plugins>
</build>

Теперь у вас есть доступ к ряду новых целей Maven, которые упрощают использование инструментария. Запустите интерфейс Speedment, выполните:

1
mvn speedment:tool

Это проведет вас через процесс подключения к базе данных и настройки генерации кода. Самый простой способ в начале — просто запустить настройки по умолчанию. После того как вы нажмете «Создать», Speedment проанализирует метаданные вашей базы данных и наполнит ваш проект новыми источниками, такими как классы сущностей и менеджеров.

Инициализировать ускорение

После того, как вы сгенерировали модель своего домена, настроить Speedment легко. Создайте новый файл Main.java и добавьте следующие строки. Все классы, которые вы видите, генерируются, поэтому их имена будут зависеть от имен ваших схем баз данных, таблиц и столбцов.

Main.java

1
2
3
4
5
6
7
public class Main {
  public static void main(String... param) {
    final HaresApplication app = new HaresApplicationBuilder()
      .withPassword("password")
      .build();
  }
}

Приведенный выше код создает новый экземпляр приложения, используя сгенерированный шаблон компоновщика. Конструктор позволяет устанавливать любые подробности конфигурации во время выполнения, например пароли базы данных.

Получив экземпляр приложения, мы можем использовать его, чтобы получить доступ к сгенерированным менеджерам. В этом случае у меня есть четыре таблицы в базе данных; «Заяц», «морковь», «человек» и «друг». (Вы можете увидеть полное определение базы данных здесь ).

1
2
3
4
final CarrotManager carrots = app.getOrThrow(CarrotManager.class);
final HareManager hares     = app.getOrThrow(HareManager.class);
final HumanManager humans   = app.getOrThrow(HumanManager.class);
final FriendManager hares   = app.getOrThrow(FriendManager.class);

Эти менеджеры теперь могут использоваться для выполнения всех наших операций CRUD.

Создать объекты

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

1
2
3
4
5
6
hares.persist(
  new HareImpl()
    .setName("Harry")
    .setColor("Gray")
    .setAge(8)
);

Метод persist возвращает (потенциально) новый экземпляр Hare, в котором установлены автоматически сгенерированные ключи, такие как «id». Если мы хотим использовать Гарри после сохранения его, мы должны использовать экземпляр, возвращенный persist.

1
2
3
4
5
6
final Hare harry = hares.persist(
  new HareImpl()
    .setName("Harry")
    .setColor("Gray")
    .setAge(8)
);

Если постоянство не удается, например, если внешний ключ или ограничение уникальности не удается, выдается исключение SpeedmentException. Мы должны проверить это и показать ошибку, если что-то помешало нам сохранить зайца.

01
02
03
04
05
06
07
08
09
10
11
try {
  final Hare harry = hares.persist(
    new HareImpl()
      .setName("Harry")
      .setColor("Gray")
      .setAge(8)
  );
} catch (final SpeedmentException ex) {
  System.err.println(ex.getMessage());
  return;
}

Читать сущности

Самая крутая функция в среде исполнения Speedment — это возможность потоковой передачи данных в вашей базе данных с использованием потоков Java 8. «Почему это так круто?» Вы можете спросить себя. «Даже Hibernate в настоящее время поддерживает потоковую передачу

Прекрасная вещь с потоками Speedment состоит в том, что они принимают во внимание промежуточные и завершающие действия при создании потока. Это означает, что если вы добавите фильтр в поток после того, как он будет создан, он все равно будет учитываться при построении оператора SQL.

Вот пример. Мы хотим подсчитать общее количество зайцев в базе данных.

1
2
final long haresTotal = hares.stream().count();
System.out.format("There are %d hares in total.%n", haresTotal);

Будет сгенерирован SQL-запрос:

1
SELECT COUNT(*) FROM hares.hare;

Завершающая операция была .count (), поэтому Speedment знает, что это условие SELECT COUNT (…), которое должно быть создано. Он также знает, что первичным ключом для таблицы «заяц» является столбец «id», что позволяет сократить весь оператор, отправляемый в базу данных, до этого.

Более сложным примером может быть нахождение числа зайцев, у которых есть имя, которое заканчивается буквами «rry» и возрастом больше или равен 5. Это можно записать так:

1
2
3
4
final long complexTotal = hares.stream()
  .filter(Hare.NAME.endsWith("rry"))
  .filter(Hare.AGE.greaterOrEqual(5))
  .count();

Мы используем построители предикатов, сгенерированные нам Speedment для определения фильтров. Это позволяет нам анализировать поток программно и сводить его к следующему оператору SQL:

1
2
3
SELECT COUNT(id) FROM hares.hare
WHERE hare.name LIKE CONCAT("%", ?)
AND hare.age >= 5;

Если мы добавим в поток операцию, которую Speedment не может оптимизировать, она будет разрешена так же, как и любой поток Java 8. Мы никогда не ограничиваемся использованием созданных построителей предикатов, это просто делает поток более эффективным.

1
2
3
final long inefficientTotal = hares.stream()
  .filter(h -> h.getName().hashCode() == 52)
  .count();

Это приведет к следующему крайне неэффективному утверждению, но все равно будет работать.

1
SELECT id,name,color,age FROM hares.hare;

Обновить сущности

Обновление существующих сущностей очень похоже на то, как мы читаем и сохраняем сущности. Изменения, внесенные в локальную копию объекта, не будут влиять на базу данных, пока мы не вызовем метод update () в менеджере.

В этом случае мы берем зайца, созданного Гарри ранее, и хотим изменить его цвет на коричневый:

1
2
harry.setColor("brown");
final Hare updatedHarry = hares.update(harry);

Менеджер возвращает новую копию зайца, если обновление принято, поэтому мы должны продолжать использовать этот экземпляр после этого момента. Как и в примере «create», обновление может завершиться ошибкой. Возможно, цвет был определен как «уникальный» столбец, а «коричневый» заяц уже существовал. В этом случае создается исключение SpeedmentException.

Мы также можем обновить несколько объектов одновременно, объединив их с потоком. Скажем, мы хотим сделать всех зайцев по имени «Гарри» коричневыми. В этом случае мы делаем это:

1
2
3
4
hares.stream()
  .filter(Hare.NAME.equal("Harry"))
  .map(Hare.COLOR.setTo("Brown"))
  .forEach(hares.updater()); // Updates remaining elements in the Stream

Мы также должны обернуть его в try-catch, чтобы убедиться, что мы предупреждаем пользователя в случае сбоя ограничения

1
2
3
4
5
6
7
8
9
try {
  hares.stream()
    .filter(Hare.NAME.equal("Harry"))
    .map(Hare.COLOR.setTo("Brown"))
    .forEach(hares.updater());
} catch (final SpeedmentException ex) {
  System.err.println(ex.getMessage());
  return;
}

Удаление сущностей

Последняя операция CRUD, которую мы должны знать, — это как удалять сущности из базы данных. Это почти идентично «обновлению». Скажем, что мы хотим убрать всех зайцев старше 10 лет. Затем мы делаем это:

1
2
3
4
5
6
7
8
try {
  hares.stream()
    .filter(Hare.AGE.greaterThan(10))
    .forEach(hares.remover()); // Removes remaining hares
} catch (final SpeedmentException ex) {
  System.err.println(ex.getMessage());
  return;
}

Резюме

В этой статье вы узнали, как настроить Speedment в проекте Maven и как создавать, обновлять, считывать и удалять объекты из базы данных, используя потоки Java 8. Это лишь небольшая часть всех вещей, которые вы можете сделать с помощью Speedment, но она служит хорошим вступлением, чтобы начать пачкать руки. Дополнительные примеры и более сложные варианты использования можно найти на странице GitHub .

До скорого!

Ссылка: Операции с базами данных CRUD в Java 8 Потоки от нашего партнера JCG Эмиля Форслунда из блога Age of Java .