Самоучитель по SQL-сервер в Linux



Использование 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.




Книжный магазин