- Что такое JDBC?
- В чем заключаются преимущества использования JDBC?
- Что из себя представляет JDBC URL?
- Из каких частей стоит JDBC?
- Перечислите-основные-классы-и-интерфейсы-jdbc
- Опишите основные этапы работы с базой данных с использованием JDBC.
- Перечислите основные типы данных используемые в JDBC. Как они связаны с типами Java?
- Как зарегистрировать драйвер JDBC?
- Как установить соединение с базой данных?
- Какие уровни изоляции транзакций поддерживаются в JDBC?
- При помощи чего формируются запросы к базе данных?
- Чем отличается Statement от PreparedStatement?
- Как осуществляется запрос к базе данных и обработка результатов?
- Как вызвать хранимую процедуру?
- Как закрыть соединение с базой данных?
JDBC, Java DataBase Connectivity (соединение с базами данных на Java) — промышленный стандарт взаимодействия Java-приложений с различными СУБД. Реализован в виде пакета java.sql
, входящего в состав Java SE.
JDBC основан на концепции драйверов, которые позволяют получать соединение с базой данных по специально описанному URL. При загрузке драйвер регистрирует себя в системе и в дальнейшем автоматически вызывается, когда программа требует URL, содержащий протокол, за который этот драйвер отвечает.
Преимуществами JDBC считают:
- Лёгкость разработки: разработчик может не знать специфики базы данных, с которой работает;
- Код практически не меняется, если компания переходит на другую базу данных (количество изменений зависит исключительно от различий между диалектами SQL);
- Не нужно дополнительно устанавливать клиентскую программу;
- К любой базе данных можно подсоединиться через легко описываемый URL.
JDBC URL состоит из:
<protocol>:
(протокола) - всегдаjdbc:
.<subprotocol>:
(подпротокола) - это имя драйвера или имя механизма соединения с базой данных. Подпротокол может поддерживаться одним или несколькими драйверами. Лежащий на поверхности пример подпротокола - это "odbc", отведенный для URL, обозначающих имя источника данных ODBC. В случае необходимости использовать сервис имен (т.е. имя базы данных в JDBC URL не будет действительным именем базы данных), то подпротоколом может выступать сервис имен.<subname>
(подимени) - это идентификатор базы данных. Значение подимени может менятся в зависимости от подпротокола, и может также иметь под-подимя с синтаксисом, определяемым разработчиком драйвера. Назначение подимени - это предоставление всей информации, необходимой для поиска базы данных. Например, если база данных находится в Интернет, то в состав подимени JDBC URL должен быть включен сетевой адрес, подчиняющийся следующим соглашениям://<hostname>:<port>/<subsubname
.
Пример JDBC URL для подключения к MySQL базе данных «Test» расположенной по адресу localhost и ожидающей соединений по порту 3306: jdbc:mysql:https://localhost:3306/Test
JDBC состоит из двух частей:
- JDBC API, который содержит набор классов и интерфейсов, определяющих доступ к базам данных. Эти классы и методы объявлены в двух пакетах -
java.sql
иjavax.sql
; - JDBC-драйвер, компонент, специфичный для каждой базы данных.
JDBC превращает вызовы уровня API в «родные» команды того или иного сервера баз данных.
-
java.sql.DriverManager
- позволяет загрузить и зарегистрировать необходимый JDBC-драйвер, а затем получить соединение с базой данных. -
javax.sql.DataSource
- решает те же задачи, что и DriverManager, но более удобным и универсальным образом. Существуют такжеjavax.sql.ConnectionPoolDataSource
иjavax.sq1.XADataSource
задача которых - обеспечение поддержки пула соединений. -
java.sql.Connection
- обеспечивает формирование запросов к источнику данных и управление транзакциями. Также предусмотрены интерфейсыjavax.sql.PooledConnection
иjavax.sql.XAConnection
. -
java.sql.Statement
,java.sql.PreparedStatement
иjava.sql.CallableStatement
- эти интерфейсы позволяют отправить запрос к источнику данных. -
java.sql.ResultSet
- объявляет методы, которые позволяют перемещаться по набору данных и считывать значения отдельных полей в текущей записи. -
java.sql.ResultSetMetaData
- позволяет получить информацию о структуре набора данных. -
java.sql.DatabaseMetaData
- позволяет получить информацию о структуре источника данных.
JDBC Type | Java Object Type |
---|---|
CHAR | String |
VARCHAR | String |
LONGVARCHAR | String |
NUMERIC | java.math.BigDecimal |
DECIMAL | java.math.BigDecimal |
BIT | Boolean |
TINYINT | Integer |
SMALLINT | Integer |
INTEGER | Integer |
BIGINT | Long |
REAL | Float |
FLOAT | Double |
DOUBLE | Double |
BINARY | byte[] |
VARBINARY | byte[] |
LONGVARBINARY | byte[] |
DATE | java.sql.Date |
TIME | java.sql.Time |
TIMESTAMP | java.sql.Timestamp |
CLOB | Clob |
BLOB | Blob |
ARRAY | Array |
STRUCT | Struct |
REF | Ref |
DISTINCT | сопоставление базового типа |
JAVA_OBJECT | базовый класс Java |
- Регистрация драйверов;
- Установление соединения с базой данных;
- Создание запроса(ов) к базе данных;
- Выполнение запроса(ов) к базе данных;
- Обработка результата(ов);
- Закрытие соединения с базой данных.
Регистрацию драйвера можно осуществить несколькими способами:
-
java.sql.DriverManager.registerDriver(%объект класса драйвера%)
. -
Class.forName(«полное имя класса драйвера»).newInstance()
. -
Class.forName(«полное имя класса драйвера»)
;
Для установки соединения с базой данных используется статический вызов java.sql.DriverManager.getConnection(...)
.
В качестве параметра может передаваться:
- URL базы данных
static Connection getConnection(String url)
- URL базы данных и набор свойств для инициализации
static Connection getConnection(String url, Properties info)
- URL базы данных, имя пользователя и пароль
static Connection getConnection(String url, String user, String password)
В результате вызова будет установлено соединение с базой данных и создан объект класса java.sql.Connection
- своеобразная «сессия», внутри контекста которой и будет происходить дальнейшая работа с базой данных.
Уровень изолированности транзакций — значение, определяющее уровень, при котором в транзакции допускаются несогласованные данные, то есть степень изолированности одной транзакции от другой. Более высокий уровень изолированности повышает точность данных, но при этом может снижаться количество параллельно выполняемых транзакций. С другой стороны, более низкий уровень изолированности позволяет выполнять больше параллельных транзакций, но снижает точность данных.
Во время использования транзакций, для обеспечения целостности данных, СУБД использует блокировки, чтобы заблокировать доступ других обращений к данным, участвующим в транзакции. Такие блокировки необходимы, чтобы предотвратить:
-
«грязное» чтение (dirty read) — чтение данных, добавленных или изменённых транзакцией, которая впоследствии не подтвердится (откатится);
-
неповторяющееся чтение (non-repeatable read) — при повторном чтении в рамках одной транзакции ранее прочитанные данные оказываются изменёнными;
-
фантомное чтение (phantom reads) — ситуация, когда при повторном чтении в рамках одной транзакции одна и та же выборка дает разные множества строк.
Уровни изоляции транзакций определены в виде констант интерфейса java.sql.Connection
:
-
TRANSACTION_NONE
– драйвер не поддерживает транзакции; -
TRANSACTION_READ_UNCOMMITTED
– позволяет транзакциям видеть несохраненные изменения данных: разрешает грязное, непроверяющееся и фантомное чтения; -
TRANSACTION_READ_COMMITTED
– любое изменение, сделанное в транзакции, не видно вне неё, пока она не сохранена: предотвращает грязное чтение, но разрешает непроверяющееся и фантомное; -
TRANSACTION_REPEATABLE_READ
– запрещает грязное и непроверяющееся, фантомное чтение разрешено; -
TRANSACTION_SERIALIZABLE
– грязное, непроверяющееся и фантомное чтения запрещены.
NB! Сервер базы данных может не поддерживать все уровни изоляции. Интерфейс
java.sql.DatabaseMetaData
предоставляет информацию об уровнях изолированности транзакций, которые поддерживаются данной СУБД.
Уровень изоляции транзакции используемый СУБД можно задать с помощью метода setTransactionIsolation()
объекта java.sql.Connection
. Получить информацию о применяемом уровне изоляции поможет метод getTransactionIsolation()
.
Для выполнения запросов к базе данных в Java используются три интерфейса:
java.sql.Statement
- для операторов SQL без параметров;java.sql.PreparedStatement
- для операторов SQL с параметрами и часто выполняемых операторов;java.sql.CallableStatement
- для исполнения хранимых в базе процедур.
Объекты-носители интерфейсов создаются при помощи методов объекта java.sql.Connection
:
java.sql.createStatement()
возвращает объект Statement;java.sql.prepareStatement()
возвращает объект PreparedStatement;java.sql.prepareCall()
возвращает объект CallableStatement;
- Statement: используется для простых случаев запроса без параметров.
- PreparedStatement: предварительно компилирует запрос, который может содержать входные параметры и выполняться несколько раз с разным набором этих параметров.
Перед выполнением СУБД разбирает каждый запрос, оптимизирует его и создает «план» (query plan) его выполнения. Если один и тот же запрос выполняется несколько раз, то СУБД в состоянии кэшировать план его выполнения и не производить этапов разборки и оптимизации повторно. Благодаря этому запрос выполняется быстрее.
Суммируя: PreparedStatement выгодно отличается от Statement тем, что при повторном использовании с одним или несколькими наборами параметров позволяет получить преимущества заранее прекомпилированного и кэшированного запроса, помогая при этом избежать SQL Injection.
Выполнение запросов осуществляется при помощи вызова методов объекта, реализующего интерфейс java.sql.Statement
:
-
executeQuery()
- для запросов, результатом которых является один набор значений, например запросовSELECT
. Результатом выполнения является объект классаjava.sql.ResultSet
; -
executeUpdate()
- для выполнения операторовINSERT
,UPDATE
илиDELETE
, а также для операторов DDL (Data Definition Language). Метод возвращает целое число, показывающее, сколько записей было модифицировано; -
execute()
– исполняет SQL-команды, которые могут возвращать различные результаты. Например, может использоваться для операцииCREATE TABLE
. Возвращаетtrue
, если первый результат содержит ResultSet иfalse
, если первый результат - это количество модифицированных записей или результат отсутствует. Чтобы получить первый результат необходимо вызвать методgetResultSet()
илиgetUpdateCount()
. Остальные результаты доступны через вызовgetMoreResults()
, который при необходимости может быть произведён многократно.
Объект с интерфейсом java.sql.ResultSet
хранит в себе результат запроса к базе данных - некий набор данных, внутри которого есть курсор, указывающий на один из элементов набора данных - текущую запись.
Используя курсор можно перемещаться по набору данных при помощи метода next()
.
NB! Сразу после получения набора данных его курсор находится перед первой записью и чтобы сделать её текущей необходимо вызвать метод
next()
.
Содержание полей текущей записи доступно через вызовы методов getInt()
, getFloat()
, getString()
, getDate()
и им подобных.
Хранимые процедуры – это именованный набор операторов SQL хранящийся на сервере. Такую процедуру можно вызвать из Java-класса с помощью вызова методов объекта реализующего интерфейс java.sql.Statement
.
Выбор объекта зависит от характеристик хранимой процедуры:
- без параметров →
Statement
- с входными параметрами →
PreparedStatement
- с входными и выходными параметрами →
CallableStatement
Если неизвестно, как была определена хранимая процедура, для получения информации о хранимой процедуре (например, имен и типов параметров) можно использовать методы
java.sql.DatabaseMetaData
позволяющие получить информацию о структуре источника данных.
Пример вызова хранимой процедуры с входными и выходными параметрами:
public vois runStoredProcedure(final Connection connection) throws Exception {
// описываем хранимую процедуру
String procedure = "{ call procedureExample(?, ?, ?) }";
// подготавливаем запрос
CallableStatement cs = connection.prepareCall(procedure);
// устанавливаем входные параметры
cs.setString(1, "abcd");
cs.setBoolean(2, true);
cs.setInt(3, 10);
// описываем выходные параметры
cs.registerOutParameter(1, java.sql.Types.VARCHAR);
cs.registerOutParameter(2, java.sql.Types.INTEGER);
// запускаем выполнение хранимой процедуры
cs.execute();
// получаем результаты
String parameter1 = cs.getString(1);
int parameter2 = cs.getInt(2);
// заканчиваем работу с запросом
cs.close();
}
Соединение с базой данной закрывается вызовом метода close()
у соответствующего объекта java.sql.Connection
или посредством использования механизма try-with-resources при создании такого объекта, появившегося в Java 7.
NB! Предварительно необходимо закрыть все запросы созданные этим соединением.