PreparedStatement vs. Statement 8 CallableStatement 8 Вопросы, которые не обсуждены 9



Скачать 104.44 Kb.
Дата11.07.2014
Размер104.44 Kb.
ТипДокументы

JDBC (Java Database Connectivity)


Оглавление

JDBC (Java Database Connectivity) 1

Что такое JDBC? 1

JDBC Драйверы 1

Основные классы JDBC 1

Основные приёмы работы 2

Получение соединения 2

Читаем из базы (Select) 3

Пишем в базу (Update, Delete и Insert) 4

Именование колонок 5

Работа с нулевыми значениями (NULL) 5

Освобождение ресурсов 6

Транзакции (Savepoint, Commit, Rollback) 7

Auto-commit 7

Ручное управление 7

JTA Транзакции 8

PreparedStatement vs. Statement 8

CallableStatement 8

Вопросы, которые не обсуждены 9


Что такое JDBC?


JDBC – это стандартное Java API для работы с реляционными СУБД. В реальности оказывается, что некоторые объектные СУБД и иногда даже совсем не СУБД предоставляют JDBC интерфейс для работы с данными.
В этом документе будут приведены самые базовые сведения об использовании JDBC.

Вот небольшой и простенький туториал по JDBC:

http://java.sun.com/docs/books/tutorial/jdbc/index.html

JDBC Драйверы


Как правило, все поставщики СУБД распространяют библиотеки для работы со своими СУБД для разных платформ. Такие библиотеки в Java называются JDBC драйверами.
Раньше, когда Java ещё не занимала достойного места на рынке, считалось, что будут популярны разные виды драйверов, в т.ч. реализованные как вызовы более старого интерфейса ODBC (JDBC-ODBC Bridge). Сегодня практически любой драйвер представляет собой JAR файл с классами, реализующими интерфейсы из пакетов java.sql и javax.sql.
Для того, чтобы начать работать с СУБД в classpath необходимо добавить JDBC драйвер для соответствующей СУБД.

Основные классы JDBC




  • DriverManager – Основная задача данного класса – получение соединения с СУБД по JDBC URL

  • Connection – Соединение с СУБД.

  • Statement – Объекты этого класса можно порождать в контексте соединения и использовать для выполнения запросов.

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

  • CallableStatement – То же самое, что и PreparedStatement, но для исполнения хранимых процедур и функций СУБД

  • ResultSet – Класс, представляющий собой курсор базы данных.
    Другими словами – это итератор по результатам выполненного запроса. Как правило, конкретная реализация
    ResultSet подкачивает строки выборки небольшими буферами для оптимизации.

  • Blob – Один из типов значений колонки в выборке, который соответствует Binary Large Object типу конкретной СУБД. Из него можно читать как из файла с помощью InputStream.

  • Clob – То же самое что и Blob, но для данных типа Character Large Object.

  • DataSource – Данный интерфейс очень важен в JavaEE. Обычно он представляет собой пул соединений с СУБД, которые можно повторно использовать по мере необходимости и после чего возвращать обратно в пул без закрытия сетевого соединения

Основные приёмы работы

Получение соединения


Способы получения соединения с СУБД в Java SE и в Java EE сильно отличаются. В этом документе будет описан только обычный способ, характерный для Java SE.

Обычно в комплекте с драйвером идёт документация, в которой подробно всё описано, но чаще всего пишут что-то такое:


public class LoadDriver {

public static void main(String[] args) {

try {

// The newInstance() call is a work around for some



// broken Java implementations

Class.forName("com.mysql.jdbc.Driver").newInstance();

} catch (Exception ex) {

// handle the error

}

}



}
Сперва необходимо загрузить главный класс драйвера. В нашем случае – com.mysql.jdbc.Driver.
Потом используем DriverManager:
import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.SQLException;
try {

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/test");

// Do something with the Connection

} catch (SQLException ex) {

// handle any errors

System.out.println("SQLException: " + ex.getMessage());

System.out.println("SQLState: " + ex.getSQLState());

System.out.println("VendorError: " + ex.getErrorCode());

}
Судя по всему, при загрузке класса драйвера срабатывает блок статической инициализации, который регистрирует в DriverManager'е себя и что он обрабатывает соединения, URL которых имеет вид jdbc:mysql://*

С соединениями примерно такая же беда, как с Input/Output потоками. Их необходимо закрывать, иначе очень быстро наступит предельно допустимое количество открытых соединений и к ней нельзя будет подключиться.

Итак, соединение получено. Теперь нужно научиться делать самые простые вещи. Выборку данных (как правило это SQL оператор SELECT) и их модификацию (INSERT, UPDATE и DELETE).

В JDBC операции чтения и модификации выполняются немного по-разному, т. к. при чтении нужно возвращать сами данные (ResultSet), а при модификации можно обойтись количеством строк, которые были изменены в хоте выполнения запроса.


Читаем из базы (Select)


Connection conn = null;

try {


сonn = ds.getConnection();

PreparedStatement ps = conn.prepareStatement("SELECT col1, col2, col3 FROM SOME_TABLE WHERE id=? AND name=? AND time=?");

ps.setInteger(1, 10);

ps.setString(2, "Mike");

ps.setLong(3, System.currentTimeMillis());

ResultSet rs = ps.executeQuery();


while (rs.next()) {

// Извлекаем значения колонок текущей строки выборки

String ctxName = rs.getString("col1");

int ctxId = rs.getInteger("col2");

int pending = rs.getLong("col3");

}

} finally {

JdbcDaoHelper.safeClose(conn, log);

}
Обычно стоит написать утилитный метод, который закрывает соединение и игнорирует исключения (JdbcDaoHelper). Очень важно закрывать соединение при любых обстоятельствах.


Пишем в базу (Update, Delete и Insert)


Сonnection conn = null;

try {

conn = connectionPool.getConnection();

PreparedStatement ps = conn.prepareStatement(

"delete from SOME_TABLE where id=?"

);

ps.setInt(1, someId);

ps.execute();

} finally {

JdbcDaoHelper.safeClose(conn, log);

}

Вот ещё один интересный пример:

public int uploadFile(InputStream is, int fileId) throws IOException, SQLException

{

Connection conn = null;

try {

conn = ds.getConnection();

PreparedStatement ps = conn.prepareStatement("insert into FILE_BODIES (id, file_id, data) values (null, ?, ?)", PreparedStatement.RETURN_GENERATED_KEYS);



ps.setInt(1, fileId);

ps.setBlob(2, is);



ps.execute();

ResultSet rs = ps.getGeneratedKeys();

if ( !rs.next() ) {

throw new SQLException("Hmm.. No keys were generated!");

}

return rs.getInt(1);

} finally {

JdbcDaoHelper.safeClose(conn, log);

}

}

Это функция, которая загружает в СУБД файл как Blob-значение. В этом примере стоит обратить внимание на следующее. Во-первых, СУБД сама генерирует значение первичного ключа. В соответствующей схеме данных в таблице FILE_BODIES задан автоматически инкрементирующийся первичный ключ.

Мы просим СУБД вернуть сгенерированные значения ключа (мы собираемся вернуть идентификатор вставленной записи для возможных дальнейших действий).

Работа с UPDATE по сути дела ничем не отличается от DELETE, разве что синтаксисом SQL запроса.

Именование колонок


ResultSet позволяет извлекать данные из колонок по индексу и по имени (в примерах мы использовали только извлечение по имени). Индекс начинается с 1 и соответствует порядку колонок в результирующей выборке или в таблице, если в запросе используется *.

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

Работа с нулевыми значениями (NULL)


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

Для начала посмотрим как проверить, что в ResultSet значение колонки текущей строки результата запроса равно NULL. Это делает с помощью этого метода:



boolean resultSet.wasNull()

Типичные грабли. Колонка типа NUMBER(20,0) (очень большое целое число) может содержать значения NULL. Вы делаете запрос и начинаете извлекать данные:

Long val = resultSet.getLong("myColumn");

if (val == null) {

// Do something

}


Как это ни забавно, но код внутри блока никогда не выполнится, даже если колонка myColumn не содержит ничего кроме нулевых значений. Дело в том, что getLong() возвращает long, а не Long. И если значение в этой колонке NULL, то функция возвращает 0.

Чтобы не напороться на такую проблему, нужно проверять на нулевое значение вызовом wasNull() сразу после чтения значения колонки:

long val = resultSet.getLong("myColumn");

if (resultSet.wasNull()) {

// Do something

}


То же самое относится и к PreparedStatement и установке значений параметров.

PreparedStatement.setNull(int index, int sqlType)

Лично я предпочитаю обернуть get и set методы с помощью утилитного класса JdbcDaoHelper:

public static void setInteger(PreparedStatement stmt, int index, Integer param)

throws SQLException {



if (param == null) {

stmt.setNull(index, Types.INTEGER);

} else {

stmt.setInt(index, param);

}

}

И для извлечения данных:



public static Integer getInteger(ResultSet rs, String colName)

throws SQLException

{

Integer value = rs.getInt(colName);

if (rs.wasNull()) {

value = null;

}

return value;

}

Освобождение ресурсов


Из нескольких примеров может быть видно, что особое внимание уделяется лишь закрытию соединения. Между тем, такие объекты как ResultSet и PreparedStatement тоже захватывают определённые ресурсы Обычно их вручную не освобождают (хотя есть метод close()), При закрытии соединения, все ресурсы, связанные с ResultSet и PreparedStatement освобождаются автоматически.

Это правило нарушалось только в некоторых первых и кривых драйверах. Сейчас оно соблюдается всеми поставщиками СУБД.

Транзакции (Savepoint, Commit, Rollback)


Важнейшей концепцией в СУБД является концепция транзакции. В википедии неплохое определение:

Транза́кция (transaction) — группа последовательных операций, которая представляет собой логическую единицу работы с данными. Транзакция может быть выполнена либо целиком и успешно, соблюдая целостность данных и независимо от параллельно идущих других транзакций, либо не выполнена вообще, и тогда она не должна произвести никакого эффекта.

В JDBC управление транзакциями возможно с помощью класса Connection. В этом классе есть два метода: commit() и rollback();

Утверждается, что после того, как выполнен метод commit(), изменения, произведённые в СУБД фиксируются окончательно. Метод rollback() откатывает произведённые изменения полностью. После выполнения любого из этих методов начинается новая транзакция. Строго говоря, если есть открытое соединение с СУБД, то вы всегда находитесь в контексте какой-либо транзакции. В ручную начинать транзакции в JDBC не нужно.

Существует три режима управления транзакциями – Auto-commit, ручное управление и управление транзакциями с помощью JTA (Java Transaction API).

Можно почитать тут (достаточно поверхностно):

http://java.sun.com/docs/books/tutorial/jdbc/basics/transactions.html

Auto-commit


Примеры кода, приведённые выше не используют управление транзакциями. Можно считать, что они работают с СУБД в режиме auto-commit.

Режим auto-commit означает, что транзакция завершается и начинается новая после выполнения каждого запроса. Это означает, что фактически, транзакций нет, т. е. набор операций, которые или выполняются полностью, или не выполняются вообще состоит из одной операции.

Естественно, такой режим практически никогда не применяется на практике, но очень вероятно, что он включен по умолчанию. Включается и выключается он с помощью

connection.setAutoCommit(boolean);

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


Ручное управление


Ручное управление подразумевает явный вызов commit() и rollback() после того, как выполнен ряд изменений в СУБД. В таком режиме рекомендуется явно сделать commit или rollback перед закрытием соединения, т.к. реакция драйвера на наличие незаконченной транзакции зависит от реализации драйвера.

При ручном управлении есть возможность отката транзакции не полностью, а до некоторый точки (savepoint). Это примерно то же самое, что тебя убирают в Doom и можно начать игру заново с последнего сейва, а не с первого уровня :) Для того, чтобы использовать эту возможность, существуют методы:



Connection.setSavepoint()

Connection.setSavePoint(name)

Connection.rollback(SavePoint)

JTA Транзакции


Это ещё один специальный режим управления транзакциями внутри сервера приложений JavaEE. Об этом будет отдельный документ.

PreparedStatement vs. Statement


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

SELECT * from USERS WHERE ID=10

Вместо этого можно один раз подготовить шаблон запроса:

SELECT * from USERS WHERE ID=?

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

PreparedStatement как раз и служит для того, чтобы разобранный шаблон запроса можно было применять несколько раз с разными параметрами. В целом следует заметить, что просто Statement практически не применяется также и по причинам, связанным с безопасностью. Когда вы используете PreparedStatement, вы избавлены от необходимости конструировать запросы с помощью конкатенации строк:



"SELECT * FROM USERS WHERE ID="+identifier;

т. к. identifier в общем случае может быть строкой, то непроверенная строка, сформированная злым хакером может привести к исполнению нежелательных запросов к вашей СУБД.

PreparedStatement решает эту проблему тем, что об экранировании параметров заботится сам класс PreparedStatement:

Код в данном случае будет такой:



PreparedStatement ps = connection.prepareStatement("SELECT * FROM USERS WHERE ID=?");

ps.setSting(1, identifier);

Номера параметров в JDBC начинаются с 1!

CallableStatement


CallableStatement – это частный случай PreparedStatement и служит для работы с хранимыми процедурами (их пишут на процедурных расширениях SQL, таких как PL/SQL). т. к. я и так уже увлёкся процессом написания того, что легко ищется, то вот тут есть док неплохой:

http://docsrv.sco.com/JDK_guide/jdbc/getstart/callablestatement.doc.html

Суть в том, что в CallableStatement можно указывать не только значения параметров, но и получать значения выходных (OUT) параметров.


Вопросы, которые не обсуждены


  • Выполнение массовых вставок, обновлений и удалений с помощью Batch Update

  • Организация пулов соединений с помощью DataSource.

Похожие:

PreparedStatement vs. Statement 8 CallableStatement 8 Вопросы, которые не обсуждены 9 iconНа дискуссии будут обсуждены вопросы

PreparedStatement vs. Statement 8 CallableStatement 8 Вопросы, которые не обсуждены 9 iconВ рамках Совещания были обсуждены актуальные на сегодняшний день вопросы общественно-политической жизни

PreparedStatement vs. Statement 8 CallableStatement 8 Вопросы, которые не обсуждены 9 iconКорр. Итар-тасс, Страсбург/ Состоявшийся 3-4 апреля в Страсбурге, Келе и Баден-Бадене юбилейный саммит глав государств и правительств не принес никаких ярких сюрпризов
Все вопросы, которые планировалось обсудить, были обсуждены, и все решения оказались именно такими, которых наблюдатели ожидали от...
PreparedStatement vs. Statement 8 CallableStatement 8 Вопросы, которые не обсуждены 9 iconПримерные вопросы («вопросы-задачи») для коллоквиума по курсу «Лаборатория инфокоммуникационных технологий»
Цветом выделены вопросы, которые необязательно знать студентам тех групп, которые не проходили соответствующие темы на занятиях
PreparedStatement vs. Statement 8 CallableStatement 8 Вопросы, которые не обсуждены 9 iconТехнологическая карта №1. Задание №1
«+» вопросы, на которые вы можете дать ответ, знаком «-», на которые не можете ответить. Обсудите в группах вопросы со знаком
PreparedStatement vs. Statement 8 CallableStatement 8 Вопросы, которые не обсуждены 9 iconДоклады Института Европы №114 Москва 2003 Редакционныйсове т: Н. П. Шмелёв (председатель)
Института Европы ран, член-корреспондент ран в. Н. Шенаев. В ходе дискуссии были обсуждены вопросы национальной самоидентификации...
PreparedStatement vs. Statement 8 CallableStatement 8 Вопросы, которые не обсуждены 9 icon26 февраля 2010 года Результаты работы Комиссий рсс по эмс рэс и по радиовещанию
В. Н. Бугаенко на совместном заседании Комиссии рсс по эмс и Комиссии рсс по радиовещанию в числе других были обсуждены вопросы разработки...
PreparedStatement vs. Statement 8 CallableStatement 8 Вопросы, которые не обсуждены 9 iconВопросы актуализации Стратегии развития Алтайского края до 2025 года обсуждены в управлении по образованию и делам молодежи
Сентября состоялся круглый стол по актуализации Стратегии социально-экономического развития Алтайского края до 2025 года в сфере...
PreparedStatement vs. Statement 8 CallableStatement 8 Вопросы, которые не обсуждены 9 icon19-20 марта на семинаре для председателей территориальных избирательных комиссий состоялся «круглый стол»
«круглый стол», где были обсуждены итоги работы избирательных комиссий в ходе подготовки и проведения выборов 4 декабря 2011 года...
PreparedStatement vs. Statement 8 CallableStatement 8 Вопросы, которые не обсуждены 9 iconРэй Дуглас Брэдбери Лето, прощай
Вопросы, которые ставит Дуг, и ответы, которые дает мистер Квотермейн, служат организующим стержнем в отдельных главах и в развязке...
Разместите кнопку на своём сайте:
ru.convdocs.org


База данных защищена авторским правом ©ru.convdocs.org 2016
обратиться к администрации
ru.convdocs.org