Выполнение JDBC-запроса: обзор
JDBC API разработан на основе иерархии, в которой объект одного типа содержит объекты других типов. Например, создадим подключение к базе данных с помощью JDBC Connection, для отправки SQL-запроса к базе данных создадим объект JDBC
Statement из соответствующего объекта Connection. Хотя эта операция кажется логичной, она имеет важное
значение: Если подключение к базе данных по какой-либо причине закрыто, закрываются также все объекты базы данных,
содержащиеся в этой базе данных. Такая иерархия расшираяется еще на один уровень, поскольку доступ к результатам запроса
выполняется с помощью объекта ResultSet, находящегося в
соответствующем объекте Statement. Данная иерархия представлена
на рисунке 1.
Рисунок 1. Отношение включения JDBC-объектов
Хотя в данной статье этого не делается, но если повторно выполнить JDBC-запрос, любой основной объект ResultSet
используется повторно. Это означает, что необходимо полностью обработать результаты запроса перед повторным использованием
JDBC Statement, например, при повторном запросе к базе данных, иначе будут
потеряны результаты предыдущего запроса.
Для выполнения запроса к базе данных Apache Derby прежде всего необходимо правильно
инициализировать базу данных. Если упражнения данного учебного руководства выполняются после предыдущих статей данной серии, то,
вероятно, у вас уже есть база данных, которую можно использовать в этом занятии. В противном случае, или если
вам требуется начать с чистого листа, можно воспользоваться файлом сценария derby.build.sql
, показанным в листинге 1.
Листинг 1. Инициализация рабочего пространства Apache Derby
rb$ mkdir derbyWork
rb$ cd derbyWork/
rb$ unzip ../derby10.zip Archive: ../derby10.zip
inflating: derby.build.sql
inflating: FirstQuery.java
inflating: SecondQuery.java
inflating: ThirdQuery.java
rb$ java org.apache.derby.tools.ij < derby.build.sql > derby.build.out 2> derby.build.err
rb$ javac *.java
rb$ ls
FirstQuery.class ThirdQuery.class derby.build.sql
FirstQuery.java ThirdQuery.java derby.log
SecondQuery.class derby.build.err test
SecondQuery.java derby.build.out |
Как показано в листинге 1, сначала создается новая рабочая папка, а затем извлекается файл исходного кода,
прилагаемый к данной статье (см. раздел Downloads в конце данного руководства). Следующий шаг заключается в создании базы данных Apache Derby, должным образом
инициализированной, что можно легко сделать, выполнив SQL-сценарий из инструментального средства Apache Derby ij. Наконец, выполняется компилирование трех прилагаемых
файлов Java с исходным кодом. Хотя результаты не представлены в явном виде (некоторые из них представляют собой довольно объемный вывод данных), можно
выполнить каждое из этих Java-приложений, например, с помощью ввода в командную строку java FirstQuery
. В остальной части данного руководства представлен исходный код для каждого из
этих трех примеров.
Как было показано в предыдущем разделе, выполнение запроса к базе данных включает три основных понятия:
- Подключение к базе данных;
- Предложение запроса;
- Результаты запроса.
Для использования этих объектов сначала необходимо импортировать их в приложение (см. листинг 2.
Листинг 2. Запуск приложения JDBC-запроса
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.sql.ResultSet;
public class FirstQuery {
private static final String driver = "org.apache.derby.jdbc.EmbeddedDriver" ;
private static final String url = "jdbc:derby:test" ;
private static final String qry =
"SELECT itemNumber, price, stockDate, description FROM bigdog.products" ;
|
Фрагмент кода в листинге 2 сначала в явной форме импортирует все необходимые классы Java,
а затем определяет несколько важных констант, которые будут использоваться в оставшейся части создаваемой программы
запросов к базе данных. Большая часть кода должна быть знакома, ее можно было видеть в предыдущем
руководстве данной серии. Новые фрагменты кода представляют собой явное включение предложений import для JDBC-интерфейсов Statement и ResultSet
и строку запросов, которая выполняется далее в программе. Этот запрос содержит в явном виде список из
четырех столбцов в таблице bigdog.products и представляет собой лучший метод
(см. врезку Явные запросы). Такой порядок позволяет устранить различные потенциальные ошибки, возможные в случае
неожиданного изменения основной базы данных.
Операции, необходимые для выполнения запросов к базе данных из Java-приложения,
относительно просты, как это показано в листинге 3, представляющем метод doQuery длля программы FirstQuery.java. Обратите внимание, что сигнатура метода включает
предложение throws SQLException, указывающее, что любое SQL-исключение, сформированное из этого метода, должно обрабатываться кодом вызова. (Правильное выполнение обработки представлено в листинге 4.)
Листинг 3. Выполнение запроса к базе данных
static void doQuery(Connection con) throws SQLException {
SQLWarning swarn = null ;
Statement stmt = con.createStatement() ;
ResultSet rs = stmt.executeQuery(qry) ;
while (rs.next()) {
System.out.println("Item Number: " + rs.getString("itemNumber")) ;
System.out.println("Item Price: " + rs.getString("price")) ;
System.out.println("Stock Date: " + rs.getString("stockDate")) ;
System.out.println("Description: " + rs.getString("description") + '\n') ;
swarn = rs.getWarnings() ;
if(swarn != null){
printSQLWarning(swarn) ;
}
}
rs.close() ;
stmt.close() ;
}
|
В методе doQuery сначала создается новый JDBC-объект Statement с помощью метода createStatement объекта Connection
. Метод executeQuery объекта Statement используется
для отправки строки запроса в базу данных Apache Derby, где запрос выполняется. Доступ к результатам запроса
в Java-программе выполняется с помощью реализации ResultSet,
предоставляемой встроенным пакетом JDBC-драйвера Apache Derby.
Интерфейс ResultSet предоставляет различные способы доступа к
данным, возвращаемым запросом. В методе doQuery используется
метод итераций, посредством которого метод next просматривает все строки данных, полученных в результате выполнения запроса. Изначально этот итератор позиционируется перед первой строкой, чтобы после его первого вызова
итератор указывал на первую строку. При каждом выполнении цикла while
выполняется доступ к следующей строке набора полученных данных, и так до достижения последней строки. После обработки
последней строки итератор принимает значение null, поскольку выполнено
перемещение за последнюю строку; выполнение цикла прерывается.
В пределах цикла осуществляется доступ к четырем столбцам каждой строки с помощью метода getString объекта ResultSet, этот метод
извлекает значение столбца в виде Java-объекта String, поэтому можно выводить значения строки
непосредственно. Метод getString может обеспечивать доступ к столбцам двумя способами: с помощью
порядкового числа столбца в запросе или с помощью имени столбца. Например, в
предыдущем запросе методе getString(1) эквивалентен методу getString("itemNumber"). Как обычно, явное выражение является лучшим методом и
обычно предпочтительнее, поскольку позволяет уменьшить вероятность скрытых ошибок в
приложении. Кроме того, доступ к столбцам начинается с 1, а не с 0, что также может стать источником
проблем. То есть еще один стимул явного задания.
Хотя маловероятно, что будут сгенерированы все предупреждения, метод doQuery явно проверяет новые предупреждения в объекте ResultSet после доступа к новой строке. Это
необходимо, поскольку предыдущие предупреждения объекта ResultSet удаляются при
выполнении доступа к новой строке. В этом методе так делать не требуется, но аналогичным способом можно проверить SQL-предупреждения
в объекте Statement.
Для использования метода doQuery необходимо установить подключение к
базе данных и вызвать метод из блока try ... catch, как
это показано в листинге 4.
Листинг 4. Вызов метода
doQuery
public static void main(String[] args) {
Connection con = null ;
try {
Class.forName(driver) ;
con = DriverManager.getConnection(url);
SQLWarning swarn = con.getWarnings() ;
if(swarn != null){
printSQLWarning(swarn) ;
}
doQuery(con) ;
} catch (SQLException se) {
printSQLException(se) ;
}
...
|
Как видно, с помощью инкапсуляции кода выполнения запроса в отдельном методе требуется только
одно изменение метода main кода подключения к базе
данных, представленного в предыдущем
учебном руководстве. После установления подключения к базе данных
и выполнения проверки возможных SQL-предупреждений вызовите метод doQuery, обрабатывающий все детали
запроса. В оставшейся части данного руководства изменим логику в методе doQuery для более подробного представления выполнения запросов select из
Java-программ к базе данных Derby.
В предыдущем разделе было показано, как выполнять запрос и извлекать данные в виде строк символов,
что идеально подходит для вывода или печати данных. Но гораздо чаще
к данным требуется применить бизнес-логику, для которой требуется извлечь данные в
соответствующем типе данных. Например, может потребоваться извлечь значение price
в виде цифровой денежной единицы, а stockDate -
в виде даты.
К счастью, JDBC API предоставляет методы извлечения данных
в виде соответствующих типов данных. Формально, правила соответствия типов довольно длинные, но поскольку
база данных Apache Derby создана на языке Java, соответствие типов значительно упрощается. Полное
описание синтаксиса можно найти в справочном руководстве по Apache Derby, указанном в разделе Ресурсы данного учебного руководства. Обычно ожидается соответствие типов,
показанное в листинге 5, где представлен метод doQuery для
программы SecondQuery.java.
Листинг 5. Соответствие типов
static void doQuery(Connection con) throws SQLException {
int itemNumber = -1 ;
BigDecimal price = null ;
Date stockDate = null ;
String description = null ;
BigDecimal threshold = new BigDecimal(40.00) ;
SQLWarning swarn = null ;
Statement stmt = con.createStatement() ;
ResultSet rs = stmt.executeQuery(qry) ;
while (rs.next()) {
itemNumber = rs.getInt("itemNumber") ;
price = rs.getBigDecimal("price") ;
stockDate = rs.getDate("stockDate") ;
description = rs.getString("description") ;
swarn = rs.getWarnings() ;
if(swarn != null){
printSQLWarning(swarn) ;
}else{
if((itemNumber < 6)&&(price.compareTo(threshold) > 0)){
System.out.println("Item Number: " + itemNumber) ;
System.out.println("Item Price: " + price) ;
System.out.println("Stock Date: " + stockDate) ;
System.out.println("Description: " + description + '\n') ;
}
}
}
rs.close() ;
stmt.close() ;
} |
В данном примере кода показан измененный метод doQuery, который теперь
извлекает результаты запроса в соответствующие типы данных. Столбец price
извлекается как тип данных java.math.BigDecimal, столбец
stockDate - как тип данных java.sql.Date. Хотя в коде, представленном на данном листинге, это не показано (это
только часть полного исходного кода файла SecondQuery.java), необходимо включить
соответствующие предложения import, чтобы обеспечить возможность использования в программе данных двух
классов.
В новой реализации метода doQuery выполняется цикл по
строкам набора результатов запроса, но теперь данные каждого столбца извлекаются в соответствующий
тип данных. Результаты можно выводить как и ранее, но теперь можно выводить только те строки, в которых значения
столбца itemNumber меньше 6, а значение price меньше заданного порогового значения (в данном примере меньше US$40,00). ResultSet может представлять собой сложный объект с множеством важных методов. Как будет показано в следующем
разделе, объект ResultSet имеет собственные метаданные, доступ к которым возможен с помощью
объекта ResultSetMetaData.
Использование метаданных запроса
Два предыдущих Java-приложения создавали длинные листинги результатов
запроса, даже если было выбрано всего несколько строк. Ранее в данной серии учебных руководств инструментальное средство
Apache Derby ij использовалось для выполнения операций с базами данных (оно также
использовалось в начале данного руководства для выполнения встроенного SQL-сценария). Средство ij предоставляет отлично отформатированный вывод результатов запроса, который также можно получить
с помощью объекта ResultSetMetaData и отформатированного предложения
печати, как это показано в листинге 6, представляющем метод doQuery
для программы ThirdQuery.java.
Листинг 6. Выполнение запроса с форматированием
static void doQuery(Connection con) throws SQLException {
int itemNumber = -1 ;
BigDecimal price = null ;
Date stockDate = null ;
String description = null ;
int numRows = 0 ;
String line = "------------------------------------" ;
BigDecimal threshold = new BigDecimal(40.00) ;
SQLWarning swarn = null ;
Statement stmt = con.createStatement() ;
ResultSet rs = stmt.executeQuery(qry) ;
ResultSetMetaData rsmd = rs.getMetaData() ;
System.out.printf("%-11s|", rsmd.getColumnName(1)) ;
System.out.printf("%-8s|", rsmd.getColumnName(2)) ;
System.out.printf("%-10s|", rsmd.getColumnName(3)) ;
System.out.printf("%-40s\n", rsmd.getColumnName(4)) ;
System.out.println(line + line);
while (rs.next()) {
itemNumber = rs.getInt("itemNumber") ;
price = rs.getBigDecimal("price") ;
stockDate = rs.getDate("stockDate") ;
description = rs.getString("description") ;
swarn = rs.getWarnings() ;
if(swarn != null){
printSQLWarning(swarn) ;
}else{
numRows ++ ;
System.out.printf("%-11s|", itemNumber) ;
System.out.printf("%-8s|", price) ;
System.out.printf("%-10s|", stockDate) ;
System.out.printf("%-40s\n", description) ;
}
}
System.out.println("\n" + numRows + " rows selected") ;
rs.close() ;
stmt.close() ;
}
|
В данном примере извлекаются метаданные для заданного объекта ResultSet с помощью объекта ResultSetMetaData. Хотя в данном листинге кода это не
показано, необходимо импортировать интерфей ResultSetMetaData,
представленный в прилагаемом файле исходного кода программы ThirdQuery.java. Эти метаданные содержат множество полезной информации, позволяющей
сделать собственное Java-приложение более удобным для работы с базами данных. Такая универсальность обычно не является необходимой для
Java-приложения, работающего с базой данных Apache Derby, поскольку тесная связь часто существует
между Java-приложением и встроенной базой данных Apache Derby, которая сама является
Java-приложением. Если требуется выполнить произвольную обработку, то для SQL-команд, как и для средства Derby ij,
важным является наличие доступа к этим метаданным.
Другое главное новшество в данном примере заключается в использовании форматированного вывода. Если раньше это не было
показано, теперь форматированное предложение print применяет строку формата к последующим данным для
создания форматированного вывода. В этом случае ограничьте длину каждого отображаемого
столбца и вставьте символ вертикальной черты (|) для разделения столбцов с целью имитация вывода,
создаваемого инструментом ij. Например, "%-11s|"
означает вывод символьной строки, ограниченной 11 символами, за которыми следует вертикальная черта. Знак "минус" означает выравнивание строки по левому краю.
В данном учебном пособии было показано, как выполнять запросы к базе данных и обрабатывать результирующие наборы данных с использованием трех примеров исходного кода Java. В первом примере выполнено подключение к базе данных, выполнен запрос, результаты запроса выведены на экран. Во втором примере результаты извлекались в соответствующих типах данных Java, а затем отфильтровывались перед выводом на экран. Наконец, метаданные результатов запроса использовались для вывода отформатированных результатов запроса аналогично способу, применяемому инструментальным средством Apache Derby ij. В следующем учебном руководстве данной серии будет показано, как применять методы извлечения данных, представленные в данном руководстве, для изменения данных в базе данных Apache Derby.
| Описание | Имя | Размер | Метод загрузки |
|---|---|---|---|
| Derby SQL script for this article | derby10.zip | 4KB | HTTP |
Научиться
- Оригинал статьи Developing with Apache Derby -- Hitting the trifecta: Java database development with Apache Derby, Part 2;
- Просмотрите некоторые связанные руководства данной серии:
- Первое учебное руководство в данной серии "Разработка с помощью Apache Derby - тройной выигрыш: Введение в Apache Derby" (сайт developerWorks, февраль 2006 г.) знакомит читателей с базой данных Apache Derby и содержит основные сведения по различных аспектам данной серии;
- Второе учебное руководство в данной серии "Разработка с помощью Apache Derby - тройной выигрыш: Разработка баз данных Java с помощью Apache Derby, часть 1" (сайт developerWorks, март 2006 г.), знакомит читателей с инструментальным средством ij и показывает, как его использовать для подключения к базе данных Apache Derby;
- Червертое учебное руководство в данной серии
"Разработка с помощью Apache Derby - тройной выигрыш: Разработка баз данных Java с помощью Apache Derby, часть 3
" (сайт developerWorks, май 2006 г.) знакомит читателей с понятием выполнения SQL-сценариев с Apache Derby. В руководстве показано, как
вставлять данные в таблицы базы данных Derby с помощью SQL-предложения
INSERT; - Пятое учебное руководство в данной серии
"Разработка с помощью Apache Derby - тройной выигрыш: Разработка баз данных Java с помощью Apache Derby, часть 4
" (сайт developerWorks, июнь 2006 г.) знакомит читателей с понятием SQL-запрос. В руководстве показано, как извлекать данные из
базы данных Derby с помощью SQL-предложения
SELECT; - В девятом учебном руководстве данной серии "Разработка с помощью Apache Derby - тройной выигрыш: Разработка баз данных Java с помощью Apache Derby, часть 1 " (сайт developerWorks, декабрь 2006 г.) показано, как установить подключение к базе данных Derby из Java-программы.
- Доступ к различным Интернет-проектам Apache Derby: инструкции с подробной информацией об использовании баз данных Derby;
- Узнайте, как
загрузить и установить Apache Derby в данном учебном руководстве проекта Derby;
-
См. раздел Справочное руководство разработчика
Apache Derby содержит массу полезной информации, включая сопоставление типов, указывающее
допустимые преобразования между SQL
типами данными и соответствующими типами данных Java;
- Узнайте, как использовать
JDBC API на
официальном Web-сайте JDBC;
- Посетите раздел проекта Apache Derby сайта developerWorks; вы найдете статьи, практические руководства и другие ресурсы, которые помогут вам начать пользоваться Derby уже сегодня;
- Подробная информация о IBM® Cloudscape™
, которая построена на базе кода Apache Derby;.
- Следите за Техническими событиями и передачами web-вещания на сайте developerWorks;
- Просмотрите все статьи, посвященные Apache и бесплатные практические руководства по Apache, которые доступны в разделе Open source сайта developerWorks;
- Просмотрите книги по этой и другим техническим темам в книжном магазине Safari;
- Посетите Web-сайт института управления ИТ (раздел Open source zone сайта developerWorks: Вы найдете много дополнительных обучающих материалов, инструментов и проектов, которые помогут разрабатывать приложения по методикам с открытым исходным кодом и использовать их с продуктами IBM;
Получить продукты и технологии
-
Загрузите Apache Derby;
- Добавьте новизны в ваши следующие проекты разработки с открытым исходным кодом при помощи Пробного программного обеспечения IBM, доступного в виде файлов для загрузки и на DVD.
Обсудить
- Примите участие в обсуждении материала на форуме.
- Присоединяйтесь к сообществу developerWorks, участвуя в блогах developerWorks.
Роберт Дж. Бруннер (Robert J. Brunner) занимается научными исследованиями в Национальном центре по приложениям для суперкомпьютеров и является старшим преподавателем астрономии в университете штата Иллинойс, город Урбана-Шампейн. Автор нескольких книг и множества статей и практических руководств на различные темы.