Pull to refresh

Непопулярное в 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`ы и что нибудь еще, непопулярное!

Спасибо!
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.