Статьи

Java 8: запрос баз данных с использованием потоков

Когда я написал свое первое приложение для базы данных Java в конце 90-х, мне пришлось делать все самому. Было много кода, сбор ошибок и преобразование объектов, и этот код быстро становился очень сложным в обслуживании и, я должен признать, подвержен ошибкам.

Даже сегодня, когда вы работаете с базами данных, часто бывает трудно соединить два принципиально разных мира объектно-ориентированного языка, такого как Java, и реляционную базу данных. Часто вам нужно написать собственный слой отображения или вы можете использовать Object Relational Mapper (ORM), например, Hibernate. Часто удобно использовать ORM, но иногда его не так просто настроить правильно. Чаще всего ORM также замедляет работу вашего приложения.

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

Что такое Speedment Open Source?

Speedment Open Source — это новая библиотека, которая предоставляет много интересных функций Java 8. Он полностью написан на Java 8 с самого начала. Speedment использует стандартные потоки для запросов к базе данных, и благодаря этому вам не нужно изучать новый API запросов. Вам вообще не нужно думать о JDBC, ResultSet и других специфических для базы данных вещах.

Speedment анализирует существующую базу данных и генерирует код автоматически. Таким образом, вам не нужно писать одну строку кода для объектов базы данных. Сгенерированный код даже содержит автоматически сгенерированные JavaDocs. Это означает, что вам не нужно писать классы сущностей, такие как «Пользователь» или «Книга», в вашем коде Java. Вместо этого вы просто создаете (или используете существующую) базу данных, подключаете к ней Speedment, и Speedment анализирует структуру базы данных и генерирует классы сущностей, анализируя структуру базы данных.

Шпиль

Поскольку талисман Speedment с открытым исходным кодом — заяц, я использую зайцев во многих моих примерах. Давайте предположим, что у нас есть существующая (MySQL) таблица с именем «hare», которая выглядит следующим образом:

mysql> explain hare;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(45) | NO   |     | NULL    |                |
| color | varchar(45) | NO   |     | NULL    |                |
| age   | int(11)     | NO   |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
4 rows in set (0.01 sec)

Затем Speedment сгенерирует соответствующий объект (для краткости JavaDocs удален):

public interface Hare extends Entity<Hare> {

    public final static ReferenceComparableField<Hare, Integer> ID = new ReferenceComparableFieldImpl<>("id", Hare::getId, Hare::setId);
    public final static ReferenceComparableStringField<Hare> NAME = new ReferenceComparableStringFieldImpl<>("name", Hare::getName, Hare::setName);
    public final static ReferenceComparableStringField<Hare> COLOR = new ReferenceComparableStringFieldImpl<>("color", Hare::getColor, Hare::setColor);
    public final static ReferenceComparableField<Hare, Integer> AGE = new ReferenceComparableFieldImpl<>("age", Hare::getAge, Hare::setAge);

    Integer getId();
    String getName();
    String getColor();
    Integer getAge();

    Hare setId(Integer id);
    Hare setName(String name);
    Hare setColor(String color);
    Hare setAge(Integer age);

    /** Graph-like traversal methods eliminating JOINs */
    Stream<Carrot> findCarrotsByOwner();
    Stream<Carrot> findCarrotsByRival();
    Stream<Carrot> findCarrots();
}

Я объясню методы find * () в отдельном посте. Их можно использовать вместо соединений SQL.

Запросы

Вот пример того, как это может выглядеть при запросе таблицы Hare:

List<Hare> oldHares = hares.stream()
    .filter(AGE.greaterThan(8))
    .collect(toList());

Умные Потоки

Теперь, похоже, что приведенный выше код будет проходить по всем строкам таблицы базы данных «заяц», но на самом деле это не так. Поток является «умным» и по достижении своей терминальной операции  collect () проанализирует предикат фильтра и заключит, что это фактически столбец «hare.age», который сравнивается с 8, и, таким образом, он сможет уменьшить поток зайцев к чему-то, что соответствует «выбрать * из зайца, где возраст> 8». Если вы используете несколько фильтров, они, конечно, дополнительно объединяются, чтобы еще больше уменьшить поток.

Просто чтобы показать принцип, вот еще один поток с большим количеством операций:

long noOldHares = hares.stream()
    .filter(AGE.greaterThan(8))
    .mapToInt(Hare::getAge)
    .sorted()
    .count();

Когда этот поток достигнет своего счета операций терминала (), он будет проверять свой собственный конвейер. Затем он заключит, что он может уменьшить предикат AGE, как в предыдущем примере. Кроме того, будет сделан вывод о том, что операции mapToInt () и sorted () не изменяют результат count (), и, таким образом, эти операции могут быть исключены все вместе. Таким образом, утверждение сводится к «выберите количество (*) от зайца, где возраст> 8».

Это означает, что вы можете использовать потоки Java 8, в то время как вам не нужно особо заботиться о том, как потоки переводятся в SQL.

Как скачать и внести свой вклад

Узнайте больше о Speedment Open Source на www.speedment.org,  и это именно то место, где вы хотите узнать больше о том, как выглядит API и как вы используете Speedment в своих проектах. Скорость  здесь на GitHub. Вы можете внести свой вклад, отправив комментарии на Gitter или скачать исходный код и создавать запросы на извлечение с вашим собственным вкладом кода.

Выводы

Оглядываясь назад на некоторые из моих старых проектов на заре эры Java, один из моих классов баз данных (с более чем 100 строками) теперь может быть сведен к одной строке кода Java 8. Это закон Мура, но перевернутый! За 14 лет (= 7 циклов Мура) число рядов уменьшилось вдвое примерно в 7 раз. Это прогресс!