LiteCoding

Заметки о программировании

FDB — JDBC с человеческим лицом

5 комментариев

Тот, кто имел дело с JDBC, наверное, согласится, что работать с этим API не так уж и просто. Поэтому его изучение, как правило, сводится к постижению азов и поиску решения, которое возьмет на себя большую часть рутинной работы. Есть ORM-решения, которые позволяют забыть про SQL, и работать непосредственно с сущностями системы. Яркий пример такого фреймворка — hibernate. Есть решения, берущие на себя только заботу об открытии/закрытии соединений, управлении ресурсами, например, jdbcTemplates.

У каждого из таких подходов есть свои плюсы и минусы. Недаром говорят, что настройка hibernate способна вынуть душу даже из админа с каменным сердцем. А в случае с jdbcTemplates все равно приходится совершать массу дополнительных действий, что не совсем подходит для написания маленьких утилит и простых проектов.

Осенью 2008-го года, когда мне понадобилось такое решение, я обратился за помощью к своему другу и коллеге Вадиму Шилову, который согласился взяться за эту задачу. Спустя две недели первая версия FDB была готова. Во всех своих последующих проектах мы использовали эту библиотеку, постоянно дорабатывая ее и исправляя ошибки.

Использовать эту библиотеку просто:

1. Зависимости

2. JDBC-драйвер
На данный момент поддерживаются PostgreSQL и MySQL (есть возможность работать с SQLite, указав соответствующий JDBC-драйвер и задав тип БД postgre).

2. Инициализация соединения С СУБД
Соединение с СУБД можно инициализировать через конфигурационный xml-файл или непосредственно из кода, создав контекст соединения.

2.1. Конфигурация через XML
XML-конфигурация выглядит следующим образом:

<?xml version="1.0" encoding="UTF-8"?>
<dbconf>
    <servertype>postgre</servertype>
    <driver>org.postgresql.Driver</driver>
    <url>jdbc:postgresql://localhost/test</url>
    <user>postgres</user>
    <pass>adminadmin</pass>
    <maxconnactive>30</maxconnactive>
</dbconf>

servertype — тип СУБД, в данный момент postgre или mysql. От правильности указания этого параметра зависит способ получения значения автоинкрементного поля после вставки.
driver — полное имя класса JDBC-драйвера.
url — JDBC URI конкретной БД.
maxconnactive — количество соединений в пуле.

Конфигурационный файл по умолчанию называется fdbconfig.xml и загружается из /WEB-INF/classes веб-проекта. Если требуется указать какой-то другой путь к конфигурационному файлу или указать другое имя (например, если вы используете его в консольном приложении), можно указать эти параметры перед началом работы с БД следующим образом.

FDBConfig.setConfigPath("/opt/config");
FDBConfig.setConfigFileName("fdb-console-mysql.xml");

2.2. Конфигурация из кода
Также есть возможность задать параметры соединения непосредственно в коде, создав экземпляр класса FDBPropertiesConfig, и заполнив его данными. Затем достаточно назначить этот экземпляр класса как источник данных для класса FDBConfig.

FDBConfig.setConfigProperties(aConfigProperties);

Загруженная такими способами (2.1 и 2.2) конфигурация является контекстом соединения по умолчанию (т.е. если контекст не указан, то используются именно эти параметры соединения). В процессе работы поменять контекст по умолчанию нельзя.

2.3. Создание контекста соединения
Если требуется работать с несколькими БД, имеет смысл создать на каждое соединение по контексту. Контекст можно создать как из конфигурационного файла, так из из класса свойств.

FDBContext context1 = FDBConfig.createContext(context1, "/opt/config/fdbconfig.xml");

FDBPropertiesConfig config2 = new FDBPropertiesConfig();
/*тут заполняем config2*/
FDBContext context2 = FDBConfig.createContext(context2, config2);

Зная имя контекста, можно получить его в любое время из FDBConfig с помощью статического метода getContext().

3. Запросы к БД
FDB поддерживает как обычные запросы, так и «подготовленные» (prepared statements).

FDBStatement query = FDB.query("SELECT * FROM users WHERE id > 10");

FDBPreparedStatement request = FDB.query("SELECT * FROM users WHERE id > ?");
request.setInt(10);

«Подготовленные» запросы заполняются данными в порядке их объявления в самом запросе.
Запросы можно выполнять и в контексте какого-либо соединения, указав первым параметром контекст.

3.1. «Соль» подготовленных запросов
Вы, конечно, знаете, что этот тип запросов нужен не для того, чтобы безопасным образом собрать запрос из непроверенных данных, поступивших извне. Эта «фишечка» всего лишь приятный побочный эффект. Основная мощь таких запросов заключается в том, что они переводятся из понятного для разработчика вида в удобную для СУБД форму лишь единожды. Т.е. этот запрос можно повторно использовать, минимизируя накладные расходы. FDB позволяет это сделать:

FDBPreparedStatement request = FDB.query("SELECT * FROM users WHERE id > ? LIMIT 10");
request.start();
request.setInt(10);
FDBResult result = request.selectRows();
//обрабатываем result
request.setInt(50);
result = request.selectRows();
//снова обрабатываем result
request.stop();

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

4. Получение результата
Результат запроса можно получить несколькими способами в зависимости от того, что конкретно требуется.

4.1. Получение значения
Если требуется получить единственное значение (например, параметр конфигурации, идентификатор или название), можно воспользоваться методом selectValue().

FDBPreparedStatement request = FDB.query("SELECT username FROM users WHERE id = ?");
request.setInt(10);
String username = (String)request.selectValue();

4.2. Получение строки выборки в виде карты
Другой специфичный случай — получение строки выборки. Как и пункте 4.2 добавлять в запрос LIMIT необязательно.

FDBPreparedStatement request = FDB.query("SELECT * FROM users WHERE id = ?");
request.setInt(10);
Map<String, Object> row = request.selectRow();

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

4.3. Получение набора результатов
В самом общем случае выборка возвращает несколько строк, которые нужно каким-то образом обработать. Это лишь ненамного сложнее, чем случаи, описанные в пунктах 4.1 и 4.2.

FDBPreparedStatement request = FDB.query("SELECT * FROM users WHERE TRUE");
FDBResult result = request.selectRows();
while(result.next())
{
    Map<String, Object> row = result.getRow();
    if("John".equalsIgnoreCase((String)row.get("username")))
    {
        result.close();
        break;
    }
}

Набор результатов следует принудительно закрывать, если по каким-то причинам вы не «проходите» по всем результатам набора. Во всех остальных случаях он закрывается самой библиотекой. В противном случае ваше приложение рискует исчерпать доступный резерв соединений.

5. Транзакции
FDB частично поддерживает транзакции. На данный момент не реализована только работа с точками сохранения.
Все транзакции создаются в контексте подключения по умолчанию к БД.

FDBTransaction transaction = FDB.begin();
FDBPreparedStatement request = FDB.query(transaction, "SELECT * FROM users WHERE id > ? LIMIT 10");
//дальнейшие манипуляции над запросом
transaction.commit();
//или transaction.rollback();

На этом возможности FDB не исчерпываются. Более подробно о них можно узнать из javadoc-документации (на данный момент она доступна вместе с исходными текстами библиотеки). За все время использования, а это уже больше 2х лет, эта библиотека зарекомендовала себя как решение для быстрой и безболезненной интеграции приложения с СУБД.

Скачать FDB можно отсюда.
О найденных ошибках можно сообщить сюда.

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • LinkedIn
  • Tumblr

Written by Дмитрий Воробьев

Четверг, Март 3rd, 2011 at 14:10

5 комментариев to 'FDB — JDBC с человеческим лицом'

Subscribe to comments with RSS or TrackBack to 'FDB — JDBC с человеческим лицом'.

  1. Здравствуйте!
    Я начинающий Java программист. Мне очень понравился ваш фреймворк для работы с б.д. и сразу захотелось подключить его к проекту, но не тут то было, кидает данное исключение — java.lang.ClassNotFoundException: org.apache.commons.lang.exception.NestableException, хотя я сделал так, как описывалось в данной статье.

    Очень нужна ваша помощь. Заранее огромное спасибо!!!

    vkontakte.ru Тимур Оруджов

    15 Янв 12 at 17:27

  2. Тимур, очень похоже на то, что вы подключили apache commons lang 3.1, а не 2.6. Дело в том, что эта библиотека очень сильно изменилась при переходе к третьей версии. Вдобавок она спроектирована так, чтобы ее можно было использовать в одном проекте с предыдущими (2.xx) версиями. Если чуть подробнее, то базовый путь к классам там org.apache.lang3, а не org.apache.lang. Так что подключайте библиотеку версии 2.6, и все заработает.

  3. Дмитрий, спасибо, именно в этом и была ошибка.

    vkontakte.ru Тимур Оруджов

    15 Янв 12 at 18:26

  4. Извините за назойливость, но своими силами решить проблему не удалось, а использовать ваш фреймворк очень охото. Я подключил все зависимости, которые требовались , но все равно кидается следующее исключение:
    java.lang.NullPointerException
    at com.fastcoreproject.common.db.FDBPool.setConnection(FDBPool.java:93)
    at
    com.fastcoreproject.common.db.FDB.query(FDB.java:141)

    Буду очень благодарен за помощь!

    vkontakte.ru Тимур Оруджов

    21 Янв 12 at 20:52

  5. Тимур, судя по ошибке, у вас проблема с конфигурацией подключения. Проверьте параметр servertype. Если вы его инициализируете через XML, то вероятно, что в названии тега допущена ошибка. Если же через FDBPropertiesConfig, то вы наверняка забыли вызвать setServerType().

Leave a Reply

You must be logged in to post a comment.