Рубрики
Без рубрики

Путеводитель по Jdbi

Узнайте, как запросить реляционную базу данных с помощью библиотеки Java с открытым исходным кодом – jdbi.

Автор оригинала: Alessio Stalla.

1. Введение

В этой статье мы посмотрим, как запросить реляционную базу данных с помощью jdbi .

Jdbi — это библиотека Java с открытым исходным кодом (лицензия Apache), которая использует выражения и отражения lambda для предоставления более дружелюбного интерфейса более высокого уровня, чем JDBC, для доступа к базе данных.

Jdbi, однако, не ORM; несмотря на то, что он имеет дополнительный модуль отображения объектов, он не имеет сеанса с прикрепленными объектами, слоем независимости базы данных и любыми другими колоколами и свистками типичного ORM.

2. Установка Jdbi

Jdbi организован в ядро и несколько дополнительных модулей.

Чтобы начать работу, мы просто должны включить основной модуль в наши зависимости:


    
        org.jdbi
        jdbi3-core
        3.1.0
    

В течение этой статьи мы покажем примеры с использованием базы данных HS’L:


    org.hsqldb
    hsqldb
    2.4.0
    test

Мы можем найти последнюю версию jdbi3-основной , ХСЛДБ и другие модули Jdbi на Maven Central.

3. Подключение к базе данных

Во-первых, нам нужно подключиться к базе данных. Для этого мы должны указать параметры соединения.

Отправной точкой является Ддби класс:

Jdbi jdbi = Jdbi.create("jdbc:hsqldb:mem:testDB", "sa", "");

Здесь мы указываем URL-адрес соединения, имя пользователя и, конечно же, пароль.

3.1. Дополнительные параметры

Если нам нужно предоставить другие параметры, мы используем перегруженный метод, Недвижимость объект:

Properties properties = new Properties();
properties.setProperty("username", "sa");
properties.setProperty("password", "");
Jdbi jdbi = Jdbi.create("jdbc:hsqldb:mem:testDB", properties);

В этих примерах мы сохранили Ддби например, в локальной переменной. Это потому, что мы будем использовать его для отправки заявлений и запросов в базу данных.

На самом деле, просто создать не устанавливает никакой связи с DB. Это просто сохраняет параметры соединения на потом.

3.2. Использование данныхИсточник

Если мы подключаемся к базе данных с помощью ДанныеИсточник , как это обычно бывает, мы можем использовать соответствующие создать перегрузка:

Jdbi jdbi = Jdbi.create(datasource);

3.3. Работа с ручками

Фактические подключения к базе данных представлены экземплярами Ручка класс.

Самый простой способ работы с ручками, и иметь их автоматически закрыты, с помощью выражений lambda:

jdbi.useHandle(handle -> {
    doStuffWith(handle);
});

Мы называем useHandle когда мы не должны вернуть значение.

В противном случае, мы используем сHandle :

jdbi.withHandle(handle -> {
    return computeValue(handle);
});

Кроме того, возможно, хотя и не рекомендуется, вручную открыть ручку соединения; в этом случае, мы должны закрыть его, когда мы сделали:

Jdbi jdbi = Jdbi.create("jdbc:hsqldb:mem:testDB", "sa", "");
try (Handle handle = jdbi.open()) {
    doStuffWith(handle);
}

К счастью, как мы видим, Ручка реализует Близкие , так что он может быть использован с попробовать с ресурсами .

4. Простые заявления

Теперь, когда мы знаем, как получить соединение давайте посмотрим, как его использовать.

В этом разделе мы создадим простую таблицу, которую мы будем использовать на протяжении всей статьи.

Отправить такие операторы, как создание таблицы в базу данных, мы используем выполнить метод:

handle.execute(
  "create table project "
  + "(id integer identity, name varchar(50), url varchar(100))");

выполнить возвращает количество строк, затронутых утверждением:

int updateCount = handle.execute(
  "insert into project values "
  + "(1, 'tutorials', 'github.com/eugenp/tutorials')");

assertEquals(1, updateCount);

На самом деле, выполнить это просто метод удобства.

Мы будем смотреть на более сложные случаи использования в более поздних разделах, но прежде чем делать это, мы должны узнать, как извлечь результаты из базы данных.

5. Запрос базы данных

Наиболее простым выражением, которое дает результаты от DB, является запрос S’L.

Чтобы выдать запрос с jdbi Handle, мы должны, по крайней мере:

  1. создать запрос
  2. выбрать, как представлять каждую строку
  3. итерировать над результатами

Теперь мы посмотрим на каждую из точек выше.

5.1. Создание запроса

Неудивительно, Jdbi представляет запросы как экземпляры Запрос класс.

Мы можем получить один из ручки:

Query query = handle.createQuery("select * from project");

5.2. Сопоставление результатов

Jdbi абстрагации от JDBC РезультатСет , который имеет довольно громоздкий API.

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

Мы можем представить каждую строку как карту:

query.mapToMap();

Ключами карты будут выбранные имена столбца.

Или, когда запрос возвращает один столбец, мы можем сопоставить его с желаемым типом Java:

handle.createQuery("select name from project").mapTo(String.class);

Jdbi имеет встроенные картографы для многих общих классов. Те, которые специфичны для некоторых библиотек или баз данных системы предоставляются в отдельных модулях.

Конечно, мы также можем определить и зарегистрировать наши картографы. Мы поговорим об этом в более позднем разделе.

Наконец, мы можем сопоставить строки с фасолью или другим пользовательским классом. Опять же, мы увидим более продвинутые варианты в специальном разделе.

5.3. Итерирование над результатами

После того, как мы решили, как сопоставить результаты, позвонив в соответствующий метод, мы получаем РезультатИбируемые объект.

Затем мы можем использовать его для итерации результатов, по одной строке за раз.

Здесь мы посмотрим на наиболее распространенные варианты.

Мы можем просто аккумулировать результаты в списке:

List> results = query.mapToMap().list();

Или в другую Коллекционая тип:

List results = query.mapTo(String.class).collect(Collectors.toSet());

Или мы можем итерировать над результатами в качестве потока:

query.mapTo(String.class).useStream((Stream stream) -> {
    doStuffWith(stream)
});

Здесь мы явно набрали поток переменной для ясности, но это не обязательно делать.

5.4. Получение единого результата

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

Если мы хотим не более одного , мы можем использовать findFirst :

Optional> first = query.mapToMap().findFirst();

Как мы видим, он возвращает Необязательный значение, которое присутствует только в том случае, если запрос возвращает хотя бы один результат.

Если запрос возвращается более чем на одну строку, возвращается только первая.

Если вместо этого, мы хотим один и только один результат , мы используем findOnly :

Date onlyResult = query.mapTo(Date.class).findOnly();

Наконец, если есть нулевые результаты или более одного, findOnly бросает Незаконное государствоИсключаемость .

6. Параметры связывания

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

  • безопасности: избегая конкатенации струн, мы предотвращаем инъекцию S’L
  • легкость: мы не должны помнить точный синтаксис сложных типов данных, таких как метки времени
  • производительность: статическая часть запроса может быть разобрана один раз и кэширована

Jdbi поддерживает как позиционные, так и названные параметры.

Мы вставляем позиционные параметры в качестве вопросительные знаки в запросе или заявлении:

Query positionalParamsQuery =
  handle.createQuery("select * from project where name = ?");

Названные параметры, вместо этого, начинаются с толстой кишки:

Query namedParamsQuery =
  handle.createQuery("select * from project where url like :pattern");

В любом случае, чтобы установить значение параметра, мы используем один из вариантов связать метод:

positionalParamsQuery.bind(0, "tutorials");
namedParamsQuery.bind("pattern", "%github.com/eugenp/%");

Обратите внимание, что, в отличие от JDBC, индексы начинаются от 0.

6.1. Связывание нескольких названных параметров одновременно

Мы также можем связать несколько названных параметров вместе с помощью объекта.

Допустим, у нас есть такой простой запрос:

Query query = handle.createQuery(
  "select id from project where name = :name and url = :url");
Map params = new HashMap<>();
params.put("name", "REST with Spring");
params.put("url", "github.com/eugenp/REST-With-Spring");

Затем, например, мы можем использовать карту:

query.bindMap(params);

Или мы можем использовать объект по-разному. Вот, например, мы связываем объект, который следует конвенции JavaBean:

query.bindBean(paramsBean);

Но мы также можем связать поля или методы объекта; для всех поддерживаемых вариантов см . документация Jdbi .

7. Выдача более сложных заявлений

Теперь, когда мы видели запросы, значения и параметры, мы можем вернуться к заявлениям и применить те же знания.

Напомним, что выполнить метод, который мы видели ранее, это просто удобный ярлык.

На самом деле, подобно запросам, Операторы DDL и DML представлены в качестве экземпляров классовой обновлять.

Мы можем получить один, позвонив метод создатьПодготовку на ручке:

Update update = handle.createUpdate(
  "INSERT INTO PROJECT (NAME, URL) VALUES (:name, :url)");

Затем, на Обновление у нас есть все обязательные методы, которые у нас есть в Запрос , так раздел 6. применяется для обновления, а также.url

Заявления выполняются, когда мы звоним, удивляемся, выполнить :

int rows = update.execute();

Как мы уже видели, он возвращает количество затронутых строк.

7.1. Извлечение значений колонки авто-приращения

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

Тогда мы не звоним выполнить , но executeAndReturnGeneratedKeys :

Update update = handle.createUpdate(
  "INSERT INTO PROJECT (NAME, URL) "
  + "VALUES ('tutorials', 'github.com/eugenp/tutorials')");
ResultBearing generatedKeys = update.executeAndReturnGeneratedKeys();

РезультатНосные тот же интерфейс, реализованный Запрос класс которые мы видели ранее, так что мы уже знаем, как его использовать:

generatedKeys.mapToMap()
  .findOnly().get("id");

8. Сделки

Нам нужна транзакция всякий раз, когда мы должны выполнять несколько операторов в качестве одной атомной операции.

Как и в случае с ручками соединения, мы вводим транзакцию, вызывая метод с закрытием:

handle.useTransaction((Handle h) -> {
    haveFunWith(h);
});

И, как и в случае с ручками, транзакция автоматически закрывается, когда возвращается закрытие.

Тем не менее, мы должны совершить или откат транзакции перед возвращением:

handle.useTransaction((Handle h) -> {
    h.execute("...");
    h.commit();
});

Если, однако, исключение выбрасывается из закрытия, Jdbi автоматически откатывает транзакцию.

Как и в случае с ручками, у нас есть специальный метод, inTransaction , если мы хотим вернуть что-то из закрытия:

handle.inTransaction((Handle h) -> {
    h.execute("...");
    h.commit();
    return true;
});

8.1. Ручное управление транзакцией

Хотя в общем случае это не рекомендуется, мы также можем начать и закрыть транзакция вручную:

handle.begin();
// ...
handle.commit();
handle.close();

9. Выводы и дальнейшее чтение

В этом учебнике мы представили ядро Jdbi: запросы, выписки и транзакции.

Мы оставили некоторые расширенные функции, такие как пользовательские строки и отображение столбца и обработка пакетов.

Мы также не обсуждали ни один из дополнительных модулей, в первую очередь расширение объекта S’L.

Все подробно представлено в Jdbi документации .

Реализация всех этих примеров и фрагментов кода можно найти в проект GitHub – это проект Maven, поэтому его легко импортировать и запускать как есть.