JDBC — средство общения между Java и базами данных

JDBC — средство общения между Java и базами данных Базы данных... они повсюду. От них просто нет никакого спасения. Сегодня мало кто не сталкивался с программированием приложений, которые используют базы данных. Начиная от простых (а порой и сложных) текстовых файлов с собственной структурой, заканчивая до боли знакомыми SQL-ориентированными СУБД. Тут собственно разработчики Java не могли остаться в стороне (ну, а как иначе?) и написали интерфейс для взаимодействия Java-приложений с базами данных. Тут и далее под базами данных будем понимать СУБД, использующие SQL.

Случилось это сравнительно давно. Потому JDBC немного изменился, в лучшую сторону.
Рассматривая данный интерфейс, можно провести параллель с известным DBI/DBD, языка Perl. Сходство весьма заметно. И тот и другой работают по практически идентичной схеме: имея единый интерфейс, подключаем драйвер для работы с определенной СУБД и, собственно, начинаем писать. Ежели нам нужно сменить СУБД — меняем драйвер. Как это реализовано в Perl, нам пока мало интересно, а вот JDBC рассмотрим поподробнее;).
Для начала установим себе драйвер используемой нами базы данных. Пусть это будет PostgreSQL. Любой JDBC драйвер может представлять собой jar-архив. Чтобы из своей программы можно было его использовать, нужно, чтобы переменная окружения CLASSPATH содержала путь к нему. Делается это примерно так:

export CLASSPATH=$CLASSPATH:/usr/local/ lib/postgresql.jar

Это в том случае, если у вас стоит Linux. В любом другом случае можно обратиться к документации :).
Перейдем непосредственно к использованию нашего JDBC по назначению. Любая программа, которая предполагает использовать JDBC, должна импортировать пакеты java.sql.*:
import java.sql.*;
Но при помощи этих пакетов мы не сможем осуществить связь с нашей СУБД. Для этого нам нужен тот самый драйвер, который мы так аккуратно положили в /usr/local/lib/.
Для загрузки/использования нашего драйвера можно пойти двумя путями. Первый — это вписать имя драйвера в код программы. В этом случае мы не сможем изменить СУБД без перекомпиляции. Второй — это подключать драйвер из командной строки Java (JVM).
Следующей строчкой мы загружаем драйвер, и он автоматически зарегистрирует себя для использования вместе с JDBC.

Class.forName("postgresql.Driver");

Во втором же случае мы используем ключ -D для подключения драйвера.

java -Djdbc.drivers=postgresql.Driver myClass

Выбирайте любой способ, ориентируясь на то, нужно ли вам будет в дальнейшем переходить на другую СУБД. Если да, то, я думаю, второй способ будет предпочтительнее. Особенно для тех, кто пишет программы для широкой публики. А ей свойственно желание самостоятельно выбирать, чем пользоваться.
Внимание, если вы все сделали одним из этих способов, но при выполнении вашей программы вы получаете ошибку No driver available — это, скорее всего, означает, что вы просто не внесли путь к драйверу в переменную CLASSPATH.
Для соединения с базой данных используют класс Connection:

Connection dbh = DriverManager.getConnection(url, user, passwd);

Здесь url — это строка, по которой JDBC определяет, куда, где и чем устанавливать соединение. Она имеет следующий формат:

jdbc:postgresql:база_данных
jdbc:postgresql://сервер/база_данных
jdbc:postgresql://сервер:порт/база_данных
где сервер — это адрес сервера, где расположена СУБД, порт — это tcp-порт, на котором будет устанавливаться соединение, и база_данных — это имя базы данных, которую мы хотим использовать. Следует отметить, что в DBI/DBD эта строчка имела вид:

dbi:postgres:dbname=база_данных;host=сервер;port=порт;

Как видно, они очень схожи. Вместо postgres/postgresql может стоять любой выбранный вами драйвер, для работы с вашей СУБД, например mySQL.
В результате имеем, например:

Connection dbh = DriverManager.getConnection("jdbc:postgresql://127.0.0.1/flowers_db","alex","123456");

Установив соединение, мы можем выполнять sql-запросы к нашей базе данных. Для примера рассмотрим небольшой кусок кода на Java:

Statement st = dbh.createStatement();
ResultSet rs = st.executeQuery("select * from flowers_tbl where (flowers_id in (1,2,3,4,5,6,7,8,9))");
while (rs.next())
{
System.out.println(rs.getString(1));
}
rs.close();
st.close();

Из этого примера хорошо видно, что для того, чтобы посылать запросы к базе данных, необходимо создать экземпляр класса Statement st. Метод executeQuery этого класса отправляет переданный ему запрос к базе данных и в качестве ответа возвращает результат в виде класса ResultSet.
В общем случае вы можете использовать только один экземпляр класса Statement, если, конечно, вам по какой-либо серьезной причине не понадобится второй. В случае же работы с потоками дело обстоит иначе. Здесь обязательно иметь на каждый поток один экземпляр Statement. О потоках речь пойдет в других статьях.
Перед тем как получать данные результата запроса, которые хранятся в нашем случае в переменной rs, необходимо выполнить метод next(). После выполнения этого метода, вы получаете очередную запись виртуально SQL-таблицы ответа на запрос. Метод next() возвращает true, если на запрос были получены данные или еще есть данные в стеке ответа. В противном случаете next() возвращает false.
Ну, а метод getString() возвращает строковую переменную значения поля записи с заданным номером. В нашем случае это 1. После использования результатов переменную rs надо закрыть методом close(), чтобы потом можно было повторно ее использовать для других запросов.
В спецификации JDBC вы можете использовать ResultSet, только один раз последовательно прочитав значения. Это означает, что, взяв однажды значение, вы не можете получить его снова. Для этого лучше использовать дополнительную переменную.
Хотя многие драйвера СУБД позволяют снять это ограничение, я все же советую вам использовать вспомогательную переменную, нежели полагаться на драйвер.
Это просто накладывает ограничение на переносимость между различными СУБД.
Метод executeQuery() удобен лишь в том случае, когда мы ожидаем ответа от базы данных. Специально для запросов, на которые ответ не обязателен, в JDBC был введен еще один метод класса Statement — это executeUpdate(). К запросам такого типа относится UPDATE, CREATE, INSERT и пр. Приведем пример использования:

st.executeUpdate("create database flowers");
st.executeUpdate("create table flowers_tbl (id integer, name char(25))");

Выше мы рассмотрели возможности JDBC для выполнения SQL-запросов обычным образом. Однако этим возможности JDBC не ограничиваются.
Очень многие останутся так и не затронутыми в этой статье, о них вы можете узнать из официальной спецификации JDBC.
А нам еще надо рассмотреть возможность предварительной подготовки SQL запросов с последующей подстановкой в них значений. Для этого вместо Statement нужно создать экземпляр класса PreparedStatement:

PreparedStatement ps = dbh.prepareStatement("insert into flowers_tbl values(?,?)");

После чего методом setString() заменяем знаки ? на значения:

int a = 1;
String b = "rose";
ps.setString(1,a);
ps.setString(2,b);

После чего выполняем метод executeUpdate(). Однако, если бы нам нужно было вернуть ответ базы данных, то вместо метода executeUpdate() мы используем executeQuery() и тоже без параметров. Вот как это выглядит в обоих случаях:

ps.executeUpdate()
// или
ResultSet rs = ps.exexuteQuery();

Далее вы знаете. Такой механизм в DBI/DBD называется методом курсоров. С другой стороны, можно было бы просто сформировать строчку с нужными переменными и выполнить простой Statement, но это куда больше работы, чем с использованием PreparedStatement.
Это все — та малая часть интерфейса JDBC, которая все же позволит вам использовать средства СУБД в своих Java-приложениях. Для более детального ознакомления с JDBC я бы посоветовал всем ознакомиться с его спецификацией.
С вопросами и предложениями — пишите.

Алексей Литвинюк, litvinuke@tut.by



Компьютерная газета. Статья была опубликована в номере 08 за 2002 год в рубрике программирование :: java

©1997-2024 Компьютерная газета