Использование JDBC
В этом разделе рассматриваются основные принципы использования JDBC, некоторые технические аспекты, потенциальные проблемы и т. д. За дополнительными сведениями обращайтесь на сайт JDBC (http://java.sun.com/products/jdbc/), на котором всегда приводится самая свежая информация и имеются ссылки на множество полезных ресурсов. Подробные описания конкретных классов, методов и полей приведены в документации API, входящей в комплект поставки JDK. Обращайтесь к пакету java.sql.
Классы JDBC представляют основные компоненты взаимодействия программы с SQL. У всех основных классов JDBC — Connection, Statement, ResultSet, Blob и Clob — имеются прямые аналоги в SQL. Кроме того, в JDBC включены вспомогательные классы — например, классы ResultsSetMetaData и DatabaseMetaData предназначены для работы с метаданными. В частности, они используются для получения информации о возможностях базы данных, для проверки типа результата запроса, в процессе отладки и просто в ситуациях, когда вы не располагаете информацией о данных, с которыми работаете.
Интерфейс JDBC в PostgreSQL также содержит классы для работы с нестандартными
расширениями PostgreSQL. К их числу относятся Fastpath, геометрические
типы, большие объекты и классы, упрощающие сериализацию объектов Java в базе данных.
Принципы использования JDBC
В листинге 12.2 приведен пример использования объекта Connection, представляющего физическое подключение к базе данных. Объект Connection требуется для создания объектов Statement, при помощи которых в JDBC базе данных передаются команды SQL.
Существует три разновидности объектов Statement: базовый класс Statement, классы PreparedStatement и CallableStatement.
Объект Statement создается методом createStatement (листинг 12.3).
Листинг 12.3. Создание объекта Statement
Statement s = c.createStatement():
В листинге 12.3 создается объект класса Statement с именем s для объекта Connecti on с именем с. Далее созданный объект Statement может использоваться для выполнения запросов и обновлений базы данных.
В классе Statement особенно важны два метода. Первый, executeQuery, получает один аргумент (код выполняемой команды SQL) и возвращает объект класса ResultSet, о котором речь пойдет ниже. Метод executeQuery предназначен для выполнения команд, возвращающих наборы данных, например запросов SELECT. Возвращаемый объект ResultSet представляет данные, полученные в ходе запроса.
Пример выборки данных из базы booktown приведен в листинге 12.4.
Листинг 12.4. Простая выборка в JDBC
Statement s = nul 1: try
{
s = c.createStatementO;
} catch (SQLException se) {
System.out.printlnC'We got an exception while creating a statement:" +
"that probably means we're no longer connected."):
se.printStackTrace();
System, exit(l);
}
ResultSet rs = null:
try {
rs = s.executeQuery("SELECT * FROM books");
} catch (SQLException se)
{
System.out.printlnC'We got an exception while executing our query:" +
"that probably means our SQL is invalid"):
se.pnntStackTrace():
System.exit(l):
}
int index = 0:
try { while (rs.nextO)
{
System.out.printlnC'Here's the result of row " + index++ + ":"):
System.out.pri ntln(rs.getStri ng(1)):
}
} catch (SQLException se) {
System.out.pnntlnC'We got an exception while getting a result:this " +
"shouldn't happen: we've done something really bad.");
se.pnntStackTrace();
System.exit(l):
}
Сначала мы создаем объект Statement, а затем используем метод executeQuery этого объекта для выполнения запроса SELECT * FROM books. Возвращенный запросом объект ResultSet используется для вывода полученной информации.
Объект Resul tSet предоставляет основной интерфейс выборки из базы данных. Он обладает двумя основными возможностями: возможностью последовательного перебора полученных записей и возможностью возвращения значения заданного поля текущей записи. Принцип перебора такой же, как в стандартных перечислениях Java: вы начинаете перебор в позиции перед первым элементом и последовательно переходите к следующему элементу методом next.
Метод next возвращает true в том случае, если объект ResultSet успешно перешел к следующей записи (то есть в итоговом наборе еще остались необработанные записи). Цикл whi I e в листинге 12.4 выводит значение первого поля каждой из возвращаемых записей. Если итоговый набор не содержит ни одной записи, первый же вызов next вернет fal se и программа ничего не выведет.
Класс ResultSet может возвращать значения различных типов; в листинге 12.4 первое поле интерпретируется как строка (Stri ng). К счастью, все стандартные типы данных SQL могут быть представлены в строковом виде, поэтому независимо от типа данных вы всегда сможете получить значение первого поля и вывести его. Класс ResultSet содержит множество других методов, включая методы выборки для всех типов данных SQL и преобразования их к типам Java. За дополнительной информацией обращайтесь к описанию ResultSet в документации API.
Другой важный метод, executeUpdate, тоже вызывается с одним аргументом — выполняемой командой SQL Различие между executeQuery и executeUpdate состоит в том, что метод executeUpdate предназначен для выполнения команд, изменяющих состояние данных в базе. Например, при вызове executeUpdate для команды CREATE, INSERT или UPDATE возвращается число типа int, определяющее количество модифицированных записей.
В листинге 12.5 метод executeUpdate используется для вставки новой записи в таблицу books.
Листинг 12.5. Простая вставка в JDBC
Statement s - null: try {
s = c.createStatement():
} catch (SQLException se) {
System.out.printlnC'We got an exception while creating a statement:" +
"that probably means we're no longer connected."):
se.printStackTrace();
System.exlt(1):
}
int m = 0;
try {
m = s.executeUpdateC1 INSERT INTO books VALUES " +
"(41472. 'Practical PostgreSGl'. 1212. 4)"): >
} catch (SQLException se) {
System.out.println("We got an exception while executing our query:" +
"that probably means our SQL is invalid"): >
se.printStackTrace():
System.exit(l):
}
System.out.println("Successfully modified " + m + " rows.\n"):
Нетривиальные возможности JDBC
Как упоминалось выше , кроме базового объекта Statement в JDBC существует два других типа объектов для представления команд: PreparedStatement и Cal lableStatement. Они будут описаны ниже.
В данном подразделе также будет рассказано об объектах ResultsSetMetaData и DatabaseMetaData. С их помощью можно запросить у JDBC описание результатов запроса или базы данных. Возможность получения такой информации на стадии выполнения программы позволяет динамически выполнять команды SQL — даже такие, параметры которых были неизвестны на момент написания программы.
Объект CallableStatement
Объект CallableStatement позволяет выполнять хранимые процедуры в JDBC-co-вместимых базах данных. Лучшим источником информации по этому вопросу является сайт Sun Javasoft (http://java.sun.com/products/jdbc/), поскольку стандарт вызываемых команд (callable statements) изменяется и развивается и его практические применения зависят от версии Java и JDBC.
Объект PreparedStatement
Объект PreparedStatement представляет подготовленные (prepared) команды SQL, многократно выполняемые с разными исходными данными — например, если вам потребовалось вставить в таблицу несколько записей, одну за другой. Главное преимущество PreparedStatement заключается в том, что команда проходит предварительную компиляцию, что избавляет от затрат на повторную обработку команд SQL при каждом выполнении. Пример использования объекта PreparedStatement приведен в листинге 12.6.
Листинг 12.6. Использование подготовленных команд в JDBC
PreparedStatement ps = null:
try {
ps = c.prepareStatementC'INSERT INTO authors VALUES (?. ?. ?)");
ps.setlntd. 495):
ps.setString(2. "Light-Williams"):
ps.setStringO. "Corwin"): } catch (SQLException se) {
System.out.printlnC"We got an exception while preparing a statement:" +
"Probably bad SQL."):
se.printStackTrace();
System.exit(l);
}
try {
ps.executeUpdate():
} catch (SQLException se) {
System.out.printlnC'We got an exception while executing an update:" +
"possibly bad SQL. or check the connection."):
se.pnntStackTrace():
System.exit(l):
}
Как видно из листинга, подготовленная команда выглядит вполне привычно, разве что все переменные величины заменяются в ней вопросительными знаками (?). Присваивание выполняется методами класса PreparedStatement (setlnt, setString и т. д.). Выбор метода для каждого поля зависит от типа данных этого поля.
Объекты PreparedStatement удобны тем, что они обеспечивают автоматическое преобразование типов данных Java в типы SQL. Например, при переходе к типу text вам не нужно беспокоиться об экранировании символов или кавычках.
Обратите внимание: первый аргумент метода set идентифицирует номер позиции переменной (вопросительного знака), которой присваивается значение. Единица означает первый вопросительный знак, двойка — второй и т. д.
Другая сильная сторона PreparedStatement связана с тем, что объект можно снова и снова использовать с новыми данными, не создавая нового объекта Statement для каждого набора параметров. Конечно, такой подход более эффективен, поскольку он ограничивается созданием одного объекта, а новые значения переменных задаются методами set.
ResultSetMetaData
У JDBC можно запросить подробную информацию об итоговом наборе запроса. Класс Resul tsSetMetaData возвращает описание объекта Resul tSet, полученного при вызове executeQuery. В нем содержится информация о количестве полей, типе данных, именах полей и т. д.
Из всех методов класса ResultSetMetaData чаще всего используются методы getCol umnName и getCol umnTypeName. Они возвращают соответственно имя поля и имя его типа данных в виде значения типа String.
ПРИМЕЧАНИЕ
Не путайте метод getCol umnType с методом getCol umnTypeName. Метод getCol umnType возвращает значение типа int, соответствующее внутреннему идентификатору типа данных в JDBC, тогда как getCol umnTypeName возвращает имя типа в формате String.
Использование JDBC 363
В листинге 12.7 класс ResultSetMetaData используется для получения имени и типа данных первого поля объекта ResultSet с именем rs. Программа является логическим продолжением листинга 12.4, в котором был создан объект rs.
Даже если отвлечься от соображений эффективности, механизм PreparedStatement гораздо надежнее подготовки нескольких команд в объектах Statement.
Листинг 12.7. Использование объекта ResultSetMetaData
ResultSetMetaData rsmd - null:
try {
rsmd = rs.getMetaData():
} catch (SQLException se) {
System.out.printlnC'We got an exception while getting the metadata:" +
"check the connection."):
se.printStackTrace();
System.exit(l):
}
String columnName = null.
columnType = null:
try {
columnName = rsmd.getColumnName(1):
columnType - rsmd.getColumnTypeName(l):
} catch (SQLException se) {
System.out.printlnC'We got an exception while getting the column name:" +
"check the connection."):
se.printStackTrace():
System.exit(l):
}
System.out.printC'The name of the first column is: '"):
System.out.print(columnName);
System.out.printlrK.....):
System.out.printC'The data type of the first column is: "):
System.out.println(columnType):
Класс ResultSetMetaData содержит много других полезных методов. Описания приведены в документации JDK API.
DatabaseMetaData
Наконец, класс DatabaseMetaData предназначен для получения информации о базе данных, с которой вы работаете. В частности, он позволяет получить ответ па перечисленные ниже вопросы.
- Какие каталоги присутствуют в базе данных?
- С каким типом базы я работаю?
- Под каким именем пользователя я работаю с базой данных?
В листинге 12.8 объект DatabaseMetaData используется для получения у драйвера JDBC имени пользователя, с которым было создано подключение, и URL-адреса базы данных.
Листинг 12.8. Использование объекта DatabaseMetaData
DatabaseMetaData dbmd = null:
try {
dbmd = c.getMetaData():
} catch (SQLException se) {
System.out.printlnC'We got an exception while getting the metadata:" +
" check the connection."):
se.printStackTrace():
System.exit(l): }
String username = null:
try {
username = dbmd.getUserName():
} catch (SQLException se) {
System.out.printlnC'We got an exception while getting the username:" +
"check the connection."); se.printStackTraceO;
System.exit(l):
}
String url = null;
try {
url = dbmd.getURLO;
} catch (SQLException se) {
System.out.printlnC'We got an exception while getting the URL:" +
"check the connection.");
se.printStackTrace();
System.exit(l):
}
System.out.printlnC'You are connected to '" + url +
'" with user name '" + username + .....);
Как было сказано выше, лучшим источником информации о других методах DatabaseMetaData является документация JDK API.