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

Развертывание интегрированного приложения

Учимся создавать автономные развертываемые интегрированные приложения баз данных Apache Derby. В этой последней статье данной серии мы выполним отображение таблиц базы данных в Java™-классы, создадим объекты доступа к базе данных (Data Access Objects, DAO) и объединим их и классы бизнес-логики в законченном приложении базы данных. Кроме того, вы узнаете, как объединить приложение и необходимые файлы базы данных Derby в одном сжатом файле, который будет содержать все необходимое для приложения базы данных.

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

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



23.06.2008

Разрабатываем интегрированное приложение Apache Derby

В рамках данной серии статей мы работали с базой данных Apache Derby. Базы данных Derby поддерживают две модели взаимодействий: сетевой режим и интегрированный режим. Более распространенным, вероятно, является сетевой режим взаимодействия, при котором сервер базы данных работает на одном компьютере, а клиенты базы данных подключаются к центральному серверу через сетевые подключения. Этот режим применяется в большинствн коммерческих установок баз данных, как правило, использующих модель обработки транзакций.

Альтернативная модель - интегрированный режим, при котором приложение содержит не только программу, реализующую необходимую бизнес-логику, но и ядро базы данных. В результате получается полностью автономное приложение с упрощенными процедурами установки и настройки. Подчеркнем, что на протяжении всей серии статей мы работали с базой данных Apache Derby исключительно в интегрированном режиме.

Теперь можно приступить к разработке интегрированного приложения базы данных Derby. Формально процесс разработки интегрированного приложения базы данных можно разбить на три этапа:

  1. Отображение логики базы данных в Java-классы;
  2. Разработка Java-кода, который будет управлять специфическими функциональными возможностями базы данных;
  3. Реализация бизнес-логики в Java-приложении.

Далее в этом разделе мы более подробно рассмотрим эти три этапа, а затем перейдем к развертыванию интегрированного приложения базы данных Apache Derby.

Объектно-реляционное отображение

В предыдущей статье этой серии мы представили основные понятия базы данных на примере, разработанном для воображаемой фирмы Bigdog's Surf Shop. Эта простая база данных управляет ассортиментом Интернет-магазина, в первую очередь с помощью таблицы базы данных, содержащей информацию о продуктах Интернет-магазина. В данной статье мы займемся разработкой приложения, которое будет выводить на экран подробный список уникальных товаров, имеющихся в ассортименте Интернет-магазина.

Первый шаг, который вам придется осилить - это реализация Java-класса, который отображается на продукт, представленный строкой в таблице bigdog.products. При этом вы столкнетесь со следующей проблемой: схема таблицы определена на языке SQL, тогда как бизнес-приложение написано на языке Java. При отображении реляционной модели данных SQL в объектную модель Java иногда могут возникать проблемы, особенно при сложных схемах базы данных (или сложных объектных моделях Java), в которых несколько таблиц связаны при помощи первичных/внешних ключей. Формально этот процесс называется объектно-реляционным отображением (Object-Relational Mapping, ORM).

Однако в нашем демонстрационном приложении вас ожидает всего один сложный момент - это правильное отображение типов данных SQL, используемых в схеме таблицы bigdog.products, в типы данных Java. В данном примере этот процесс сравнительно прост, как видно из листинга 1, в котором описывается определение класса Product.

Листинг 1. Отображение таблицы продуктов в Java-класс Product
public class Product {
    
    private int itemNumber = 0 ;
    private BigDecimal price = null ;
    private Date stockDate = null ;
    private String description = null ;
    
    public Product(int itemNumber, BigDecimal price, Date stockDate, String description){
        this.itemNumber = itemNumber ;
        this.price = price ;
        this.stockDate = stockDate ;
        this.description = description ;
    }

    public void printProduct() {
        String line = "------------------------------------" ;
        
        System.out.println("\nBigdog's Surf Shop Product Information") ;
        System.out.println(line + line);
        
        System.out.printf("Item Number      : %-11s\n", this.itemNumber) ;
        System.out.printf("Item Price       : $%-8.2f\n", this.price) ;
        System.out.printf("Item StockDate   : %-10s\n", this.stockDate) ;
        System.out.printf("Item Description : %-40s\n", this.description) ;
        
        System.out.println(line + line + "\n");
    }
...

Отображение, представленное в этом листинге, должно показаться вам знакомым благодаря урокам, изученным при извлечении данных из таблицы bigdog.products в десятой статье этой серии, которая посвящена выполнению запросов. Данный класс определяет атрибуты, соответствующие столбцам таблицы bigdog.products, при помощи корректных типов данных Java. На практике можно добавить для каждого атрибута полнофункциональные методы getter/setter; это не сделано в данном учебном коде, но в наиболее популярных средах разработки часто осуществляется автоматически.

В классе Product два основных метода - это конструктор, который, как и следовало ожидать, инициализирует объект, и метод printProduct, который генерирует хорошо отформатированный вывод информации об определенном продукте. Этот метод вызывается демонстрационным приложением, но мы поместили его в этот класс, чтобы воспользоваться преимуществами жесткой связи – в конце концов, что может лучше отобразить объект, чем определяющий его класс? В общем случае вам нужно определить Java-классы для всех таблиц базы данных, которые необходимо представить в бизнес-приложении. Значит, хотя это и не обязательно для нашего простого учебного приложения, вы можете также создать Java-класс для отображения таблицы bigdog.vendors.

Инкапсулируем логику базы данных

После того как таблицы базы данных будут успешно отображены в Java-классы, можно перейти к следующему этапу - подключению таблиц базы данных к соответствующим Java-классам. Один из возможных способов - разместить код подключения непосредственно в соответствующем Java-классе. —Пример - класс Product из листинга 1. Однако в большинстве случаев это не лучшее решение, потому что при этом создается жесткая связь между базой данных и приложением базы данных. Как уже неоднократно говорилось в статьях этой серии, в большинстве случаев жесткой связанности следует избегать.

Гораздо лучше реализовать уровень интерфейса, в котором можно инкапсулировать специфический для базы данных Java - код. При таком подходе при любых изменениях характеристик базы данных, например, размещения, версии или даже базовой схемы, приложение будет изолировано; все изменения, которые вам придется сделать, будут ограничены DAO-классами. Java-классы в этом связующем уровне называются DAO-объектами; обычно для каждого Java-класса, подключенного к таблице базы данных, создается один DAO-класс. Например, в листинге 2 DAO-объект для ранее определенного класса Product представлен в классе ProductDAO.

Листинг 2. Связывание строки в таблице products с Java-классом Product
SELECT itemNumber, price, stockDate, description " + 
        "FROM bigdog.products WHERE itemNumber = ?" ;
...
    public Product getProduct(int targetItemNumber){

        Product product = null ;

        try{
            pstmt.clearParameters() ;
            pstmt.setInt(1, targetItemNumber) ;

            ResultSet rs = pstmt.executeQuery();

            if (rs.next()) {
                int itemNumber = rs.getInt("itemNumber") ;
                BigDecimal price = rs.getBigDecimal("price") ;
                Date stockDate = rs.getDate("stockDate") ;
                String description = rs.getString("description") ;

                product = new Product(itemNumber, price, stockDate, description) ;
            }
            
            rs.close() ;
            
        } catch(SQLException se) {
            printSQLException(se) ;
        }

        return product;
    }
...

Основное назначение класса ProductDAO - это создание нового объекта Product, состоящего из данных конкретной строки таблицы bigdog.products. Эта строка идентифицируется значением targetItemNumber, которое передается методу getProduct. Хотя это и не отражено в листинге 2, данный класс также определяет максимальный номер элемента (что необходимо для нашего учебного приложения) путем выполнения запроса SELECT MAX(itemNumber) FROM bigdog.products и синтаксического разбора его результатов.

В листинге 2 легко увидеть жесткую связь между базовой схемой базы данных и DAO-классами. В методе getProduct данные извлекаются путем выполнения инструкции PreparedStatement. Добавляя еще один уровень, мы сводим к минимуму возможные влияния изменений базы данных: при небольших изменениях в нашем демонстрационном приложении достаточно будет изменить всего лишь один класс - ProductDAO.

Кроме того, можно поместить код управления базой данных в отдельный класс, чтобы инкапсулировать этот тип функциональности, как показано в листинге 3.

Листинг 3. Код управления базой данных
public class DBManager {
    private static Connection con = null ;    

    private static final String driver = "org.apache.derby.jdbc.EmbeddedDriver" ;
    private static final String url = "jdbc:derby:" ;
    private static final String dbName = "SurfShop" ;
...
    private ProductDAO pdao = null ;

    public DBManager(){
        if(!dbExists()){
            try {
                Class.forName(driver) ;
                con = DriverManager.getConnection(url + dbName + ";create=true");

                processStatement(createProductsSQL) ;

                con.setAutoCommit(false) ;
                batchInsertData(insertProductsSQL) ;
                con.commit() ;

            }catch(BatchUpdateException bue) {
...
            }
        }
        pdao = new ProductDAO(con) ;
    }

    public void close() {
        try {
            con = DriverManager.getConnection(url + ";shutdown=true") ;
        }catch(SQLException se) {
            ; // Пустой цикл. Произошло завершение работы системы.
        }
        con = null ;
    }

    public ProductDAO getProductDAO() {
        return pdao ;
    }

    private Boolean dbExists() {
        Boolean exists = false ;
        try{
            Class.forName(driver) ;
            con = DriverManager.getConnection(url + dbName);
            exists = true ;
        } catch(Exception e){
            ; // Пустой цикл - БД (пока еще) не существует
        }
        return(exists) ;
    }
...

Класс DBManager, представленный в листинге 3 - это самый большой класс в нашем приложении. В какой-то степени это следствие упрощенного характера приложения, но кроме того, еще и отражение сложности успешного осуществления взаимодействия с базой данных из Java-приложения. Данный класс отвечает за управление подключениями к базе данных, а именно за установление первоначального соединения и создание экземпляра класса ProductDAO, к которому будет обращаться демонстрационное приложение.

Поскольку это интегрированное приложение, код также отвечает за создание и инициализацию самой интегрированной базы данных. В данном классе это осуществляется при помощи метода dbExists, который пытается установить подключение к базе данных. Если база данных не существует, то выдается исключение, и класс-конструктор DBManager создает базу данных. Затем он создает таблицу bigdog.products и в завершение выполняет вставку 10 новых строк в ходе одной пакетной операции insert. В соответствии с этой логикой, если приложение выполняется второй раз, метод dbExists успешно устанавливает подключение (при условии, что локальная база данных не будет удалена), и процедура создания базы данных не выполняется. Чтобы упростить различные операции подключения к базе данных, URL JDBC обозначается как jdbc:derby, и к этой команде добавляются различные суффиксы в зависимости от необходимых операций. Например, выражение url + dbName + ";create=true" используется для создания базы данных, а выражение url + dbName - для установления подключения к базе данных.

И напоследок еще одна важная особенность класса DBManager - наличие метода close. Этот метод явно завершает работу интегрированной базы данных Apache Derby при попытке установить соединение с базой данных с добавлением к базовому URL JDBC свойства shutdown=true. Завершая работу базы данных таким образом, вы гарантируете корректное завершение всех транзакций. Это способствует более быстрому перезапуску базы данных, поскольку база данных гарантированно остается в непротиворечивом состоянии.

Разрабатываем интегрированное приложение

Последний формальный этап в этом процессе - разработка Java-приложения. Во избежание лишних сложностей наше приложение:

  • Принимает входные данные от пользователя в цифровом формате через командную строку;
  • Извлекает информацию о продукте, которому соответствует введенный номер товара;
  • Выводит информацию о продукте для просмотра пользователем.

С учетом ранее определенных классов код нашего демонстрационного приложения сравнительно короток и прост для реализации, как показано в листинге 4.

Листинг 4. Код интегрированного приложения Apache Derby
import java.io.BufferedReader ;
import java.io.InputStreamReader ;

public class DemoApp {
    private DBManager dbm = null ;
    private ProductDAO pdao = null ;
    private int maxItemNumber = 0 ;

    public DemoApp() {
        this.dbm = new DBManager() ;
        this.pdao = dbm.getProductDAO() ;
        this.maxItemNumber = pdao.getMaxItemNumber() ;
    }

    public void showProduct() {

        int itemNumber = 0 ;
        Product p = null ;

        BufferedReader cin = new BufferedReader(new InputStreamReader(System.in)) ;

        while(true) {
            try {
                System.out.print("Enter object item number (0 to Exit): ") ;
                itemNumber = Integer.parseInt(cin.readLine()) ;
            }catch(Exception ex){
                ; // Пустой цикл. В этом простом приложении любые 
                  // ошибки ввода игнорируются.
            }    
            
            if(itemNumber == 0)
                break ;
            else if ((itemNumber < 0) || (itemNumber > this.maxItemNumber))
                System.out.println ("Invalid product item number, please try again.") ;
            else{
                p = pdao.getProduct(itemNumber) ;
                p.printProduct() ;
            }
        }
        dbm.close() ;
    }

    public static void main(String[] args) {
        DemoApp da = new DemoApp() ;
        da.showProduct() ;
    }
}

Логика приложения, показанного в листинге 4, содержится в двух методах в этом классе: в конструкторе и методе showProduct. Конструктор создает объект DBManager для установления подключения к базе данных, как говорилось ранее, а затем запрашивает экземпляр класса ProductDAO. Этот экземпляр используется в методе showProduct для извлечения нужных данных из базы данных. Конструктор также извлекает из базы данных значение максимального номера товара, что облегчает обработку ошибок.

Метод showProduct считывает целое число из стандартного потока ввода консоли. Если целое является допустимым значением из таблицы bigdog.products, то при помощи соответствующих данных из таблицы bigdog.products создается объект Product, а значение выводится на экран при помощи метода printProduct. Описанный процесс продолжается до тех пор, пока пользователь не введет 0 для завершения цикла.


Развертываем интегрированное приложение Apache Derby

После успешной реализации и тестирования интегрированного приложения базы данных Apache Derby можно перейти к следующему этапу - объединению всех необходимых файлов в одном архиве, чтобы можно было при необходимости выполнить развертывание приложения. Далее в этом разделе мы подробно рассмотрим этот процесс для только что созданного нами демонстрационного приложения. Первый шаг (если вы еще этого не сделали) - это распаковка кода примера, который можно загрузить по ссылке в разделе Загрузка (см. листинг 5).

Листинг 5. Подготовка к созданию интегрированного приложения Apache Derby
rb$ mkdir derbyWork
rb$ cd derbyWork/
rb$ unzip ../derby14.zip 
Archive:  ../derby14.zip
  inflating: DBManager.java          
  inflating: DemoApp.java            
  inflating: Product.java            
  inflating: ProductDAO.java         
  inflating: manifest.txt  
rb$ more manifest.txt 
Main-Class: DemoApp
Class-Path: lib/derby.jar

rb$

Мы видим, что сначала создается новый рабочий каталог, а затем выполняется распаковка файла кода примера в этот новый каталог. Сжатый файл кода примера содержит пять файлов: четыре файла с исходным Java-кодом для демонстрационного приложения и файл декларации manifest.txt, содержимое которого, как показано в листинге 5, состоит из двух строк, содержащих пару имя-значение. Этот файл декларации используется для создания исполняемого JAR-файла для демонстрационного приложения. Первая пара имя-значение определяет имя класса, являющегося основным для приложения, в нашем случае это класс DemoApp. Вторая пара имя-значение определяет, где следует искать Java-классы базы данных Apache Derby, которые при интегрированном режиме полностью содержатся в файле derby.jar.

Следующий шаг - это объединение необходимых файлов в одном .jar-файле, как показано в листинге 6.

Листинг 6. Создание интегрированного приложения Apache Derby
rb$ mkdir lib
rb$ cp $DERBY_INSTALL/lib/derby.jar lib/
rb$ javac *.java
rb$ jar cvmf manifest.txt demoapp.jar *.class
added manifest
adding: DBManager.class(in = 5087) (out= 2792)(deflated 45%)
adding: DemoApp.class(in = 1460) (out= 881)(deflated 39%)
adding: Product.class(in = 1684) (out= 919)(deflated 45%)
adding: ProductDAO.class(in = 2625) (out= 1369)(deflated 47%)
rb$ ls
DBManager.class         Product.class           demoapp.jar
DBManager.java          Product.java            lib
DemoApp.class           ProductDAO.class        manifest.txt
DemoApp.java            ProductDAO.java
rb$ rm -rf *.java *.class manifest.txt 
rb$ ls
demoapp.jar     lib
rb$ zip demoapp.zip demoapp.jar lib/derby.jar 
  adding: demoapp.jar (deflated 6%)
  adding: lib/derby.jar (deflated 8%)
rb$ rm -rf demoapp.jar lib 
rb$ ls
demoapp.zip

Для этого нужно сначала создать каталог lib, а затем скопировать в этот каталог файл derby.jar, как указано в паре имя-значение CLASS-PATH: lib/derby.jar в файле декларации (manifest file). Если вы измените это значение, то соответственно нужно изменить и размещение файла derby.jar; в противном случае наше демонстрационное приложение не будет работать (потому что не сможет найти необходимые файлы классов Apache Derby).

Затем необходимо скомпилировать прилагаемые исходные коды для демонстрационного приложения, а потом создать JAR-файл. JAR-файл создается посредством вызова команды jar с флагами cvmf, которые задают следующие действия: create - создание архива, verbose - подробное протоколирование всех выполняемых шагов, использование указанного файла декларации - manifest file, присвоение готовому файлу (JAR file) заданного имени. При вызове команды этим способом необходимо сначала указать файл декларации с добавлением имени, которое вы хотите присвоить JAR-файлу. И наконец, вы должны явно перечислить все файлы или каталоги, которые следует включить в новый JAR-файл.

После того, как JAR-файл, содержащий код приложения, будет создан, можно спокойно удалить эти файлы (конечно, необходимо иметь резервную копию, сохраненную в другой папке), а затем создать новый файл архив, содержащий JAR-файл приложения и необходимые вспомогательные классы (в нашем примере это только один файл - derby.jar). И, наконец, чтобы продемонстрировать автономный характер нашего нового интегрированного приложения базы данных Apache Derby, можно удалить все остальные файлы. В заключение стоит коснуться еще одного момента: создавая архив интегрированного приложения в этом примере, вы не распространяете ПО Apache Derby. Следовательно, вам не нужно включать в архив лицензионную информацию Apache Derby. Если вы когда-либо будете распространять программное обеспечение базы данных Apache Derby , например, захотите переслать этот пример другому лицу на другой компьютер, то вам придется соблюдать требования к лицензированию Apache Derby, о которых рассказывалось во врезке Распространение ПО Apache Derby.

К этому моменту мы создали новый файл архива, содержащий все файлы интегрированного приложения Apache Derby ; теперь остается только одно - запустить это демонстрационное приложение, как показано в листинге 7.

Листинг 7. Запуск интегрированного приложения Apache Derby
rb$ unzip demoapp.zip 
Archive:  demoapp.zip
  inflating: demoapp.jar             
   creating: lib/
rb$ rm demoapp.zip 
rb$ CLASSPATH=  
rb$ export CLASSPATH
rb$ echo $CLASSPATH

rb$ java -jar demoapp.jar 
Enter object item number (0 to Exit): -1
Invalid product item number, please try again.
Enter object item number (0 to Exit): 11
Invalid product item number, please try again.
Enter object item number (0 to Exit): 1

Bigdog's Surf Shop Product Information
------------------------------------------------------------------------
Item Number      : 1          
Item Price       : $19.94   
Item StockDate   : 2006-03-31
Item Description : Hooded sweatshirt                       
------------------------------------------------------------------------

Enter object item number (0 to Exit): 10

Bigdog's Surf Shop Product Information
------------------------------------------------------------------------
Item Number      : 10         
Item Price       : $34.95   
Item StockDate   : 2006-01-24
Item Description : Open-toed sandal                        
------------------------------------------------------------------------

Enter object item number (0 to Exit): 0
rb$ ls
SurfShop        demoapp.jar     derby.log       lib

Для запуска демонстрационного приложения распакуйте файл архива и запустите файл demoapp.jar. Кроме всего прочего, данный пример удаляет сжатый файл приложения и значение переменной среды CLASSPATH в вашей операционной системе. Это говорит о том, что данное демонстрационное приложение будет работать на любом компьютере, даже если на нем не установлена полная версия ПО СУБД Apache Derby.

После запуска приложение создает базу данных Surf Shop, затем таблицу bigdog.products, содержащую 10 строк, и далее извлекает информацию о продукте до тех пор, пока пользователь не введет 0, чтобы завершить работу. Несмотря на то, что наше демонстрационное приложение не отличается сложностью, оно иллюстрирует основные требования к интегрированному приложению Apache Derby. Можно взять этот пример и дополнить его функциями, позволяющими пользователям выполнять обновление, вставку или удаление строк таблицы bigdog.products. Кроме того, можно использовать его как основу для создания интегрированного приложения базы данных Apache Derby.


Заключение

Изучая эту статью, последнюю в серии, вы использовали многие уроки из предыдущих статей для разработки и развертывания законченного интегрированного приложения базы данных Apache Derby. Вы узнали, как отобразить таблицы базы данных Derby в Java-классы, создать DAO-объекты и успешно объединить их в одном приложении. Вы также узнали, как объединить Derby-приложение с Java-кодом базы данных Apache Derby в автономном развертываемом Java-приложении. Знания, которые вы получили, изучая статьи этой серии, помогут вам в создании интегрированных приложений базы данных Apache Derby.—Итак, можно приступать к работе!


Загрузка

ОписаниеИмяРазмер
JJava-код для данной статьиderby14.zip4КБ

Ресурсы

Научиться

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

  • Загрузите Apache Derby.
  • Полный список инструментов объектно-реляционного отображения, совместимых с базами данных Apache Derby, можно найти в Web-энциклопедии Apache Derby под заголовком Persistence/Object Relational Mapping (Персистентность/Объектно-реляционное отображение);
  • Лицензия на Apache версии 2.0;(EN)
  • Добавьте новизны в ваши следующие проекты разработки с открытым исходным кодом при помощи ознакомительных версий ПО IBM, которые можно загрузить с Web-сайта или заказать на DVD.(EN)

Обсудить

Комментарии

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, Технология Java, Information Management
ArticleID=315946
ArticleTitle=Разработка c помощью Apache Derby -- тройной выигрыш: Часть 6. Разработка баз данных при помощи Apache Derby
publish-date=06232008