Разработка с помощью Apache Derby - тройной выигрыш: Разработка баз данных Java с помощью Apache Derby, Часть 2

Выполнение запроса

Полный обзор создания простого запроса к базе данных Apache Derby и способов обработки выбранных результатов. Для этого потребуется представить три новых класса JDBC: Statement, ResultSet и ResultSetMetaData. Будет показано, как использовать эти классы с JDBC-подключением к базе данных для быстрого и простого извлечения данных из БД Apache Derby в собственное™ Java-приложение.

Роберт Бруннер , ученый-исследователь NCSA, старший преподаватель астрономии, Университет штата Иллинойс, г. Урбана-Шампейн

Роберт Дж. Бруннер (Robert J. Brunner) занимается научными исследованиями в Национальном центре по приложениям для суперкомпьютеров и является старшим преподавателем астрономии в университете штата Иллинойс, город Урбана-Шампейн. Автор нескольких книг и множества статей и практических руководств на различные темы.



03.05.2007

Выполнение JDBC-запроса: обзор

JDBC API разработан на основе иерархии, в которой объект одного типа содержит объекты других типов. Например, создадим подключение к базе данных с помощью JDBC Connection, для отправки SQL-запроса к базе данных создадим объект JDBC Statement из соответствующего объекта Connection. Хотя эта операция кажется логичной, она имеет важное значение: Если подключение к базе данных по какой-либо причине закрыто, закрываются также все объекты базы данных, содержащиеся в этой базе данных. Такая иерархия расшираяется еще на один уровень, поскольку доступ к результатам запроса выполняется с помощью объекта ResultSet, находящегося в соответствующем объекте Statement. Данная иерархия представлена на рисунке 1.

Рисунок 1. Отношение включения JDBC-объектов
Отношение включения 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 articlederby10.zip4KB

Ресурсы

Научиться

Получить продукты и технологии

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Open source, Information Management, Технология Java
ArticleID=216879
ArticleTitle=Разработка с помощью Apache Derby - тройной выигрыш: Разработка баз данных Java с помощью Apache Derby, Часть 2
publish-date=05032007