Непопулярное в JDBC

О API JDBC достаточно много информации и гайдов, но к сожалению почти все как под копирку. Сам достаточно долгое время осваивал это чудо, переводя ресурсы умных.

Привет, Хабр!


Не так давно узнал о некоторых приемах создания соединения и дальнейшей работы с ним.
На 100% уникальность не претендую, лавры не собираю.

В общем, типичная задача, подключится к БД и запросить данные из таблицы. Что мы делаем для этого:

  1. Создаем (получаем) Connection
  2. Создаем Statement
  3. Выполняем запрос и складываем все в ResulSet
  4. Ну а далее, прокручиваем ResulSet получая из него данные

Connection conn = ConnectionPool.init().getConnection();
Statement stmt = conn.createStatement();
ResultSet rset = stmt.executeQuery("SELECT t.* FROM warehouses t");
while(rset.next()) {
...
}
rset.close();
stmt.close();
ConnectionPool.init().comebackConnection(conn);

Прокручиваемый ResultSet


Начиная с версии JDBC 2.0 появилась возможность направленной прокрутки набора результата.
Для этого, при создании Statement необходимо указать параметр желаемой прокрутки.

Connection conn = ConnectionPool.init().getConnection();
Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ResultSet rset = stmt.executeQuery("SELECT t.* FROM warehouses t");
...
rset.close();
stmt.close();
ConnectionPool.init().comebackConnection(conn);

  1. ResultSet.TYPE_FORWARD_ONLY: значение по умолчанию, прокрутка в одном направлении;
  2. ResultSet.TYPE_SCROLL_INSENSITIVE: прокрутка назад и вперед. При изменении данных в БД, ResultSet не отразит этого.
  3. ResultSet.TYPE_SCROLL_SENSITIVE: тоже самое что и 2, плюс отражает реальное представление данных в базе данных по мере их изменения.

Для перемещения курсора вперед, традиционно вызываем next(), для перемещения назад — previous(). Также можем вызвать first() или last() для перемещения в начало или конец курсора. Можно перейти на конкретную строку указав ее индекс — absolute(5) или переместиться относительно текущего на порядок — relative(3).

Редактируемый ResultSet


Возможно, в коде выше вы заметили второй параметр — ResultSet.CONCUR_READ_ONLY, он означает, что выбранный результат доступен только для чтения.

Вместо создания привычных стейтментов для обновления (вставка, редактирование, удаление) мы можем воспользоваться ранее созданным ResultSet`ом. Но для этого во второй параметр метода по созданию Statement необходимо указать ResultSet.CONCUR_UPDATABLE.

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

Connection conn = ConnectionPool.init().getConnection();
Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet rset = stmt.executeQuery("SELECT t.* FROM warehouses t"); //Псевдоним тут не случайно

// Обновим строку
rset.absolute(5); //Переходим на 5 строку
rset.updateObject("active_date", new java.sql.Date(new Date().getTime())); //Устанавливаем значение в поле
rset.updateRow(); //Фиксируем данные

//Вставим новую строку
rset.moveToInsertRow(); //Добавляем новую строку и переходим на нее
rset.updateObject("id", 123);
rset.updateObject("name", "Склад №4");
rset.updateObject("active_date", new java.sql.Date(new Date().getTime()));
rset.insertRow();  //Фиксируем данные

//Удалим строку
rset.last(); // Идем на последнюю строку
rset.deleteRow(); // Удаляем ее из БД

rset.close();
stmt.close();
ConnectionPool.init().comebackConnection(conn);

  • Вызов метода cancelRowUpdates() отменит все ожидающие изменения.
  • Перед вызовом методов updateRow() или insertRow() необходимо проверять, что текущая строка находится в состоянии обновления или создания соответственно. Иначе возникнет исключение.

Пакетные обновления


Для большей скорости, операций по обновлению данных БД рекомендуется делать в пакетах.
Т.е. это когда вы например добавляете строки в таблицу и не отправляете их каждую по отдельности на сервер, а делаете это в конце.

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

А также, нужно проверить, поддерживает ли версия драйвера пакетные обновления — DatabaseMetaData.supportsBatchUpdates().

Connection conn = ConnectionPool.init().getConnection();
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();

stmt.addBatch("INSERT INTO warehouses(id, name, active_date) VALUES(1,'Склад №1', sysdate)"); //Добавляем строку в пакет
stmt.addBatch("INSERT INTO warehouses(id, name, active_date) VALUES(2,'Склад №2', sysdate)");//Еще одну
stmt.addBatch("INSERT INTO warehouses(id, name, active_date) VALUES(3,'Склад №3', sysdate)");//Еще одну
stmt.executeBatch(); //Выполняем пакет
conn.commit(); //Фиксируем данные

conn.setAutoCommit(true);
stmt.close();
ConnectionPool.init().comebackConnection(conn);

Заключение


Данная статья не является гайдом о том как нужно делать. Большинство примеров представлено в тезисном варианте, цель которого — передать коротко и лаконично основную мысль. В следующей статье напишу про RowSet`ы и что нибудь еще, непопулярное!

Спасибо!
Метки:
java, jdbc