286 lines
24 KiB
Markdown
286 lines
24 KiB
Markdown
[Вопросы для собеседования](README.md)
|
||
|
||
# JDBC
|
||
+ [Что такое _JDBC_?](#Что-такое-jdbc)
|
||
+ [В чем заключаются преимущества использования JDBC?](#В-чем-заключаются-преимущества-использования-jdbc)
|
||
+ [Что из себя представляет JDBC URL?](#Что-из-себя-представляет-jdbc-url)
|
||
+ [Из каких частей стоит JDBC?](#Из-каких-частей-стоит-jdbc)
|
||
+ [Перечислите-основные-классы-и-интерфейсы-jdbc](#Перечислите-основные-классы-и-интерфейсы-jdbc)
|
||
+ [Опишите основные этапы работы с базой данных с использованием JDBC.](#Опишите-основные-этапы-работы-с-базой-данных-при-использовании-jdbc)
|
||
+ [Перечислите основные типы данных используемые в JDBC. Как они связаны с типами Java?](#Перечислите-основные-типы-данных-используемые-в-JDBC.-Как-они-связаны-с-типами-Java)
|
||
+ [Как зарегистрировать драйвер JDBC?](#Как-зарегистрировать-драйвер-jdbc)
|
||
+ [Как установить соединение с базой данных?](#Как-установить-соединение-с-базой-данных)
|
||
+ [Какие уровни изоляции транзакций поддерживаются в JDBC?](#Какие-уровни-изоляции-транзакций-поддерживаются-в-jdbc)
|
||
+ [При помощи чего формируются запросы к базе данных?](#При-помощи-чего-формируются-запросы-к-базе-данных)
|
||
+ [Чем отличается Statement от PreparedStatement?](#Чем-отличается-statement-от-preparedstatement)
|
||
+ [Как осуществляется запрос к базе данных и обработка результатов?](#Как-осуществляется-запрос-к-базе-данных-и-обработка-результатов)
|
||
+ [Как вызвать хранимую процедуру?](#Как-вызвать-хранимую-процедуру)
|
||
+ [Как закрыть соединение с базой данных?](#Как-закрыть-соединение-с-базой-данных)
|
||
|
||
## Что такое _JDBC_?
|
||
__JDBC, Java DataBase Connectivity (соединение с базами данных на Java)__ — промышленный стандарт взаимодействия Java-приложений с различными СУБД. Реализован в виде пакета `java.sql`, входящего в состав Java SE.
|
||
|
||
JDBC основан на концепции драйверов, которые позволяют получать соединение с базой данных по специально описанному URL. При загрузке драйвер регистрирует себя в системе и в дальнейшем автоматически вызывается, когда программа требует URL, содержащий протокол, за который этот драйвер отвечает.
|
||
|
||
[к оглавлению](#jdbc)
|
||
|
||
## В чем заключаются преимущества использования JDBC?
|
||
Преимуществами JDBC считают:
|
||
|
||
+ Лёгкость разработки: разработчик может не знать специфики базы данных, с которой работает;
|
||
+ Код практически не меняется, если компания переходит на другую базу данных (количество изменений зависит исключительно от различий между диалектами SQL);
|
||
+ Не нужно дополнительно устанавливать клиентскую программу;
|
||
+ К любой базе данных можно подсоединиться через легко описываемый URL.
|
||
|
||
[к оглавлению](#jdbc)
|
||
|
||
## Что из себя представляет JDBC 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://localhost:3306/Test`
|
||
|
||
[к оглавлению](#jdbc)
|
||
|
||
## Из каких частей стоит JDBC?
|
||
JDBC состоит из двух частей:
|
||
|
||
+ __JDBC API__, который содержит набор классов и интерфейсов, определяющих доступ к базам данных. Эти классы и методы объявлены в двух пакетах - `java.sql` и `javax.sql`;
|
||
+ __JDBC-драйвер__, компонент, специфичный для каждой базы данных.
|
||
|
||
JDBC превращает вызовы уровня API в «родные» команды того или иного сервера баз данных.
|
||
|
||
[к оглавлению](#jdbc)
|
||
|
||
## Перечислите основные классы и интерфейсы JDBC.
|
||
+ `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)
|
||
|
||
## Перечислите основные типы данных используемые в JDBC. Как они связаны с типами Java?
|
||
|
||
| 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 |
|
||
|
||
[к оглавлению](#jdbc)
|
||
|
||
## Опишите основные этапы работы с базой данных при использовании JDBC.
|
||
+ Регистрация драйверов;
|
||
+ Установление соединения с базой данных;
|
||
+ Создание запроса(ов) к базе данных;
|
||
+ Выполнение запроса(ов) к базе данных;
|
||
+ Обработка результата(ов);
|
||
+ Закрытие соединения с базой данных.
|
||
|
||
[к оглавлению](#jdbc)
|
||
|
||
## Как зарегистрировать драйвер JDBC?
|
||
Регистрацию драйвера можно осуществить несколькими способами:
|
||
|
||
+ `java.sql.DriverManager.registerDriver(%объект класса драйвера%)`.
|
||
|
||
+ `Class.forName(«полное имя класса драйвера»).newInstance()`.
|
||
|
||
+ `Class.forName(«полное имя класса драйвера»)`;
|
||
|
||
[к оглавлению](#jdbc)
|
||
|
||
## Как установить соединение с базой данных?
|
||
Для установки соединения с базой данных используется статический вызов `java.sql.DriverManager.getConnection(...)` .
|
||
|
||
|
||
В качестве параметра может передаваться:
|
||
|
||
+ URL базы данных
|
||
```java
|
||
static Connection getConnection(String url)
|
||
```
|
||
|
||
+ URL базы данных и набор свойств для инициализации
|
||
```java
|
||
static Connection getConnection(String url, Properties info)
|
||
```
|
||
|
||
+ URL базы данных, имя пользователя и пароль
|
||
```java
|
||
static Connection getConnection(String url, String user, String password)
|
||
```
|
||
|
||
В результате вызова будет установлено соединение с базой данных и создан объект класса `java.sql.Connection` - своеобразная «сессия», внутри контекста которой и будет происходить дальнейшая работа с базой данных.
|
||
|
||
[к оглавлению](#jdbc)
|
||
|
||
## Какие уровни изоляции транзакций поддерживаются в JDBC?
|
||
__Уровень изолированности транзакций__ — значение, определяющее уровень, при котором в транзакции допускаются несогласованные данные, то есть степень изолированности одной транзакции от другой. Более высокий уровень изолированности повышает точность данных, но при этом может снижаться количество параллельно выполняемых транзакций. С другой стороны, более низкий уровень изолированности позволяет выполнять больше параллельных транзакций, но снижает точность данных.
|
||
|
||
Во время использования транзакций, для обеспечения целостности данных, СУБД использует блокировки, чтобы заблокировать доступ других обращений к данным, участвующим в транзакции. Такие блокировки необходимы, чтобы предотвратить:
|
||
|
||
+ _«грязное» чтение (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()`.
|
||
|
||
[к оглавлению](#jdbc)
|
||
|
||
## При помощи чего формируются запросы к базе данных?
|
||
|
||
Для выполнения запросов к базе данных в 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_;
|
||
|
||
[к оглавлению](#jdbc)
|
||
|
||
## Чем отличается Statement от PreparedStatement?
|
||
+ __Statement__: используется для простых случаев запроса без параметров.
|
||
+ __PreparedStatement__: предварительно компилирует запрос, который может содержать входные параметры и выполняться несколько раз с разным набором этих параметров.
|
||
|
||
Перед выполнением СУБД разбирает каждый запрос, оптимизирует его и создает «план» (query plan) его выполнения. Если один и тот же запрос выполняется несколько раз, то СУБД в состоянии кэшировать план его выполнения и не производить этапов разборки и оптимизации повторно. Благодаря этому запрос выполняется быстрее.
|
||
|
||
Суммируя: _PreparedStatement_ выгодно отличается от _Statement_ тем, что при повторном использовании с одним или несколькими наборами параметров позволяет получить преимущества заранее прекомпилированного и кэшированного запроса, помогая при этом избежать SQL Injection.
|
||
|
||
[к оглавлению](#jdbc)
|
||
|
||
## Как осуществляется запрос к базе данных и обработка результатов?
|
||
Выполнение запросов осуществляется при помощи вызова методов объекта, реализующего интерфейс `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()` и им подобных.
|
||
|
||
[к оглавлению](#jdbc)
|
||
|
||
## Как вызвать хранимую процедуру?
|
||
__Хранимые процедуры__ – это именованный набор операторов SQL хранящийся на сервере. Такую процедуру можно вызвать из Java-класса с помощью вызова методов объекта реализующего интерфейс `java.sql.Statement`.
|
||
|
||
Выбор объекта зависит от характеристик хранимой процедуры:
|
||
|
||
+ без параметров → `Statement`
|
||
+ с входными параметрами → `PreparedStatement`
|
||
+ с входными и выходными параметрами → `CallableStatement`
|
||
|
||
> Если неизвестно, как была определена хранимая процедура, для получения информации о хранимой процедуре (например, имен и типов параметров) можно использовать методы `java.sql.DatabaseMetaData` позволяющие получить информацию о структуре источника данных.
|
||
|
||
Пример вызова хранимой процедуры с входными и выходными параметрами:
|
||
|
||
```java
|
||
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();
|
||
}
|
||
```
|
||
|
||
[к оглавлению](#jdbc)
|
||
|
||
## Как закрыть соединение с базой данных?
|
||
Соединение с базой данной закрывается вызовом метода `close()` у соответствующего объекта `java.sql.Connection` или посредством использования механизма try-with-resources при создании такого объекта, появившегося в Java 7.
|
||
|
||
> __NB!__ Предварительно необходимо закрыть все запросы созданные этим соединением.
|
||
|
||
[к оглавлению](#jdbc)
|
||
|
||
# Источники
|
||
+ [Википедия - JDBC](https://ru.wikipedia.org/wiki/Java_Database_Connectivity)
|
||
+ [IBM developerWorks®](http://www.ibm.com/developerworks/ru/library/dm-1209storedprocedures/)
|
||
+ [Документация к пакету java.sql](https://docs.oracle.com/javase/7/docs/api/java/sql/package-summary.html)
|
||
+ [Википедия - Уровень изолированности транзакции](https://ru.wikipedia.org/wiki/Уровень_изолированности_транзакций)
|
||
|
||
[Вопросы для собеседования](README.md)
|