目次


Apache Derbyを使用した開発 - 3連単を当てる

Apache Derbyを使用したJavaデータベース開発、パート1

接続する

Comments

コンテンツシリーズ

このコンテンツは全#シリーズのパート#です: Apache Derbyを使用した開発 - 3連単を当てる

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:Apache Derbyを使用した開発 - 3連単を当てる

このシリーズの続きに乞うご期待。

JDBC の紹介

今までこのシリーズでは、Apache Derby データベースとの接続および操作に ij ツールを使って、データベースのさまざまな概念を紹介してきました。そのときにははっきりとわからなかったかもしれませんが、組み込み Apache Derby データベースとの接続と操作は、実は JDBC アプリケーション・プログラミング・インターフェース (API) を使用した Java アプリケーションで行っていたのです。独自の Java アプリケーションを作成して ij ツールの基本的な機能を再現する方法については、この先数回の記事で説明することにして、今回はデータベース接続を確立する方法、そして考えられるデータベース・エラーおよび警告を処理する方法に話題を絞ります。

Java コードに取り組む前に、まず JDBC API の特性について興味を持たれるかもしれません。JDBC とは正式な Java Database Connectivity API のことで、Java Development Kit の 1.1 リリースから出回っています。java.sql パッケージに含まれている JDBC API をよく調べてみると、この API がインターフェースの大部分を占めていることがわかるはずです。そのため、実際にあるデータベースの JDBC ドライバーを作成するのは、これらのインターフェースを実装するための Java クラスを提供する必要があるデータベース・ベンダー (あるいは進取的なサード・パーティー) の仕事となります。javax.sql パッケージには、より高度な機能を提供する JDBC API の拡張も用意されていますが、今後の記事では標準 JDBC パッケージの大部分を取り上げるため、拡張については基本要素をすべて説明するまで待ってください。

JDBC に関して最後に言っておきたいのは、Java アプリケーションとデータベースとの接続は、JDBC ドライバーによって制御されるという点です。本来、JDBC ドライバーには 4 つのタイプがあり、1、2、3、4 のタイプ番号で区別されています。ドライバーのタイプは、Java アプリケーションとデータベースとの通信方法に対応します。Derby データベースとの接続に使うドライバーをはじめ、今日のほとんどのドライバーは、タイプ 4 です。タイプ 4 のドライバーは完全に Java 言語で作成されていて、JDBC API をベンダー固有のデーターベース・プロトコルに直接変換します。Derby データベースの場合、この変換プロセスは簡易化されます。なぜなら、Derby も同じく Java 言語で作成されているためです。

Apache Derby と JDBC

JDBC の基本がわかったところで、いよいよ Java プログラミング言語を使って組み込み Apache Derby データベースに接続する手順を開始します。ただし、それにはまず Apache Derby データベース・ソフトウェアが実際にインストールされていなければなりません (シリーズ第 1 回で説明)。この肝心なステップをまだ実行していない場合は、第 1 回の記事に従って Derby ソフトウェアのダウンロードとインストールを行ってください。Derby データベース・ソフトウェアのインストールが完了したら、この記事に付属のサンプル・コードを使って、Derby データベースに接続できるようになります (リスト 1 を参照)。

リスト 1. サンプル・コードの実行
rb$ mkdir derbyWork
rb$ cd derbyWork/
rb$ unzip ../derby9.zip 
Archive:  ../derby9.zip
  inflating: FirstConnect.java       
rb$ ls    
FirstConnect.java
rb$ javac FirstConnect.java 
rb$ java FirstConnect 

----------------------------------------------------
Database Name    = Apache Derby
Database Version = 10.1.2.1
Driver Name      = Apache Derby Embedded JDBC Driver
Driver Version   = 10.1.2.1
Database URL     = jdbc:derby:test
----------------------------------------------------
rb$ java FirstConnect
SQLWarning: State=01J01, Severity = 10000
Database 'test' not created, connection made to existing database instead.

----------------------------------------------------
Database Name    = Apache Derby
Database Version = 10.1.2.1
Driver Name      = Apache Derby Embedded JDBC Driver
Driver Version   = 10.1.2.1
Database URL     = jdbc:derby:test
----------------------------------------------------
rb$ ls
FirstConnect.class      derby.log
FirstConnect.java       test

この最初の例では、データベース・アプリケーション・コードを開発して実行するための空のワークスペースを作成しています。まず初めに新しいディレクトリーを作成し、ここに記事に付属のコード・ファイルを解凍します。コンパイルして実行する前に、ls コマンドを使ってディレクトリー内に記事で説明する Java ソース・コードだけが含まれていることを確認します。

次のステップは、Java コンパイラーを使って、この後のステップで Java 仮想マシン (JVM) が実行する Java バイトコード・ファイルを作成することです。ソース・コードのコンパイルが完了したら、バイトコードを JVM 内で実行します。これによって FirstConnect クラスの main メソッドが呼び出され、上記のような出力が生成されます。この手順で問題が発生した場合は、囲み記事「実行できない場合の対処方法」を参照してください。この Java コードは、test データベースを作成してから Apache Derby 組み込み JDBC ドライバーを使って test データベースとの接続を作成しています。このコードが SQL 警告およびエラーを正しく処理することを確かめるには、コードを再実行してください。すると、データベースが作成されなかったという警告情報が表示され (データベースはすでに存在するため)、続いて前に表示されたデータベースおよび JDBC ドライバーに関する一般情報が表示されます。

この例では最後に、作業ディレクトリーのコンテンツを再表示し、コンパイル済み Java クラス・ファイルと新規データベース・ファイルを示しています。この最後の操作は重要です。Apache Derby を組み込みデータベースとして使用すると、データベース・ファイルのデフォルト・ロケーションはコードと同じディレクトリーとなります。このロケーションが適切でない場合は、JDBC URL を変更してデータベース・ファイルの作成場所を指定しなければなりません。これについてはこの記事では説明しませんので、Derby Developer's Guide を参照してください。このガイドには、「参考文献」セクションに記載した Derby オンライン・マニュアルのリンクからアクセスできます。

Java アプリケーションと Derby データベースを接続する

ここからは、前のセクションで実行方法を説明した簡潔な Java コードについて詳しく説明していきます。実動環境では、Java データベース・アプリケーションを開発するのが困難な場合があります。詳細は今後の記事で説明するのでここで深く掘り下げることはしませんが、その代わり、この記事では Java アプリケーションと組み込み Apache Derby データベースとの接続を確立する最も基本的な手法を取り上げます。リスト 2 に示すこの手法には、JDBC ドライバーを使用して接続プロトコルを実装する必要があります。

リスト 2. JDBC を使った Derby データベースとの接続
private static final String driver = "org.apache.derby.jdbc.EmbeddedDriver" ;
private static final String url = "jdbc:derby:test;create=true" ;
 
public static void main(String[] args) {
    Connection con = null ;
    DatabaseMetaData dbmd = null ;
        
    try {
        Class.forName(driver) ;
        con = DriverManager.getConnection(url);
       
       // Use the database connection somehow.
       
    } catch (SQLException se) {
        printSQLException(se) ;
    } catch(ClassNotFoundException e){
        System.out.println("JDBC Driver " + driver + " not found in CLASSPATH") ;
    }finally {
        if(con != null){
            try{
                con.close() ;
            } catch(SQLException se){
                printSQLException(se) ;
            }
        }
    }
}

上記のサンプル・コードではまず、Apache Derby 組み込み JDBC ドライバーの Java クラス名と JDBC データベース接続 URL が含まれる 2 つの定数を定義しています。この接続 URL はお馴染みのはずです。以前の記事では、これとまったく同じ接続 URL を使って、ij ツールで connect コマンドを実行しました。driver に割り当てる値は、組み込みドライバー・クラスの完全修飾名です。

main メソッドに含まれた残りのコードは、JVM のデフォルト・クラス・ローダーを使用して前に定義された driver クラスを検出し、インスタンス化しますが、これは Class.forname メソッドを使用して行っています。Class.forname メソッドは、CLASSPATH 内のどこかにあるはずの Derby 組み込みドライバーのクラス・ファイルを検出して JVM にロードします。ロード・プロセス中にクラスの静的セクションが処理され、このドライバーが JDBC の DriverManager オブジェクトに登録されます。

DriverManager はファクトリー・オブジェクトとして機能します。データベース URL が指定されると、DriverManager は該当する JDBC ドライバーを使用してデータベース接続を返します。このステップが実行されるのは次の行のコードです。このコードでは、前に定義された URL を使って DriverManager の JDBC Connection を要求します。

上記で見逃しようのない点は、接続要求が try ... catch ブロック内にラップされていること、そして finally ブロック内の接続で close メソッドを呼び出していることです。いずれについても、この後の「何かがうまくいかない場合」のセクションで説明します。スペースが限られているため、この記事では JDBC によってデータベース接続を確立するさまざまな方法を詳しく説明できません。例えば、この例ではユーザー名やパスワードなどのセキュリティー情報が一切使用されていませんが、今後の記事でこの代わりとなるデータベース接続手法を取り上げる予定です (その一例として、囲み記事「DetaSource について」を参照してください)。それまで待ちきれないという方は、「参考文献」セクションにリンクが記載されている Derby Developer's Guide を参照してください。

データベース・メタデータ

メタデータに一度も遭遇したことがなければ奇妙に思えるかもしれませんが、メタデータとは単にデータを記述するデータのことです。データベースの場合、メタデータはデータベースの名前、バージョン番号、あるいは接続を行う JDBC ドライバーの名前など、特定のデータベースについて記述します。JDBC でデータベース・メタデータにアクセスするには、リスト 3 のように該当するメソッドを DatabaseMetaData オブジェクトから呼び出します。

リスト 3. データベース・メタデータの操作
DatabaseMetaData dbmd = null ;
dbmd = con.getMetaData() ;

System.out.println("\n----------------------------------------------------") ;
System.out.println("Database Name    = " + dbmd.getDatabaseProductName()) ;
System.out.println("Database Version = " + dbmd.getDatabaseProductVersion()) ;
System.out.println("Driver Name      = " + dbmd.getDriverName()) ;
System.out.println("Driver Version   = " + dbmd.getDriverVersion()) ;
System.out.println("Database URL     = " + dbmd.getURL()) ;
System.out.println("----------------------------------------------------") ;

該当するメタデータにアクセスするため、まず前の例で示した現行の JDBC Connection から新規 DatabaseMetaData オブジェクトを作成しています。これで、Apache Derby JDBC ドライバーに用意されたさまざまなメタデータ関数のいずれかを使用できるようになります (関数の一覧は、JDBC 仕様を参照してください。この仕様には「参考文献」セクションからアクセスできます)。上記の例では、アクセス中のデータベースの名前と製品バージョン番号、データベースへのアクセスに使用している JDBC ドライバーの名前とバージョン番号、そして接続が確立されたデータベースを識別する完全 JDBC URL を抽出しています。データベース・メタデータは、多種多様なデータベースあるいは JDBC ドライバーとやりとりをしなければならないデータベース・アプリケーションを開発する際に最も役立ちます。そのような場合、実行時にメタデータを使って特定のデータベースや JDBC ドライバーの機能を判断できるからです。

何かがうまくいかない場合

このシリーズの以前の記事に従ったにせよ、独自のやり方を採ったにせよ、Apache Derby データベースを操作する際にデータベース警告とデータベース・エラーの両方を経験したはずです。データベースに保管する情報の重要性を考えると、データベースのエラーと警告をどちらも適切に処理することがビジネスにとって極めて重要になります。幸い、これから説明するように、適切な処理をするのは難しいことではありません。最初のステップは、アプリケーションに提供されるデータベースのエラー情報を表示する方法を学ぶことです。リスト 4 にその方法を示します。

リスト 4. 例外処理コード
private static void printSQLException(SQLException se) {
    while(se != null) {
        System.out.print("SQLException: State:   " + se.getSQLState());
        System.out.println("Severity: " + se.getErrorCode());
        System.out.println(se.getMessage());            
        
        se = se.getNextException();
    }
 }

private static void printSQLWarning(SQLWarning sw) {
    while(sw != null) {
        System.out.print("SQLWarning: State=" + sw.getSQLState()) ;
        System.out.println(", Severity = " + sw.getErrorCode()) ;
        System.out.println(sw.getMessage()); 
        
        sw = sw.getNextWarning();
    }
}

リスト 4 を見るとわかるように、SQL 例外と SQL 警告の処理は似ています。SQLWarning クラスはSQLException クラスからの継承ですが、SQL 警告は SQL 例外ほど深刻ではないため別々に処理したほうが得策です。この 2 つの関数はいずれも private static として宣言されます。そのため、これらの関数は main メソッド内から呼び出すことができますが、その他のクラスからは呼び出せません。実動コードでは、プログラムの要件に従ってこれを変更することになります。

両方の関数で警告または例外をループしています。一般的なエラー処理の感覚からすると奇妙に思えるかもしれませんが、データベース・プログラミングでは不測の事態を予測してください。このようなチェーニングを行う理由は単純です。データベース内では、複数のイベントが結合されていることがよくあるからです。例えば、データベースに複数の行を挿入している場合、列のデータ型や名前の不一致によってすべての行の挿入が失敗すると複数のエラーが発生します。すべてをきちんと考慮に入れるには、例外同士を結びつけて、データベースに発生しているすべての問題が呼び出し側のコードに通知されるようにしなければなりません。これを助けるため、Apache Derby は常に最も重要な例外を例外チェーンの先頭に配置します。

ループ内では、エラーまたは警告情報を表示します。SQL 状態は 5 文字の文字列で、この文字列は X/OPEN CAE (Common Application Environment) 仕様、Data Management: SQL, Version 2 SQL 仕様、または SQL99 標準の規則に沿って、プログラムが特定のデータベース・エラー状態から復旧できるようにしています。SQL 状態の先頭の 2 文字はクラス値で、残りの 3 文字はサブクラス値です。値が 00000 の SQL 状態は成功を表し、クラス値 01 はデータ切り捨てなどの警告状態を表します。この SQL コードはデータベース固有の値です。

SQL 例外の処理は簡単明瞭で、リスト 5 のように標準 Java try ... catch メカニズムを使って JDBC メソッド呼び出しをラップします。

リスト 5. SQL 例外のキャッチ
try{
    // Execute a JDBC operation
} catch (SQLException se) {
    printSQLException(se) ;
}

上記のサンプル・コードが示すように、SQL 例外を処理するには、SQLException オブジェクトを前に定義した printSQLException メソッドに渡します。すると、このメソッドがすべてのエラー・レポート作成を処理します。SQL 例外を処理する上で唯一難しい点は、データベース接続などのデータベース・リソースを適切に再利用することです。接続またはデータベース・カーソルなどのリソースは、データベース・アプリケーション・コードを実行している JVM の外部で管理されている場合があるため、アプリケーションの中でリソースを明示的にクローズする必要があります。Connection オブジェクトの close メソッドは SQLException をスローできるので、このメソッドを finally ブロックに配置すると、エラーが発生したとしても Java アプリケーションが確実にデータベース接続をクローズしようとします。

一方、SQL 警告は標準例外処理メカニズムでは伝搬されないので、明示的なチェックが必要となります。それには、リスト 6 で示すように該当する getWarnings メソッドを関連 JDBC オブジェクトで呼び出します。

リスト 6. SQL 警告のチェック
SQLWarning swarn = con.getWarnings() ;

if(swarn != null){
    printSQLWarning(swarn) ;
}

前にも説明したように、SQL 警告は printSQLWarning メソッドによって処理されます。このメソッドを呼び出す前に、少なくとも 1 つの SQL 警告があることを確認します。警告がある場合は、最初の SQLWarning オブジェクトを適切なメソッドに渡します。今後の記事で説明しますが、多数の JDBC オブジェクトが SQL 警告を生成することがあるので、エラー処理および警告処理コードをカプセル化すると、データベース・アプリケーション・コードを開発して管理するタスクを単純化することができます。

すべてを合体させる

ここまでのところで、Apache Derby と JDBC API を使用してデータベース接続を確立するために必要なコンポーネントの数々について説明しました。これらのコンポーネントを 1 つにまとめると、必要な機能のほとんどが提供されます。リスト 7 に、完成した接続の例を記載します。

リスト 7. 完成した接続コードの例
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.DatabaseMetaData;

public class FirstConnect {
    private static final String driver = "org.apache.derby.jdbc.EmbeddedDriver" ;
    private static final String url = "jdbc:derby:test;create=true" ;
    
    static void printSQLException(SQLException se) {
        while(se != null) {

            System.out.print("SQLException: State:   " + se.getSQLState());
            System.out.println("Severity: " + se.getErrorCode());
            System.out.println(se.getMessage());            
            
            se = se.getNextException();
        }
    }
        
    static void printSQLWarning(SQLWarning sw) {
        while(sw != null) {

            System.out.print("SQLWarning: State=" + sw.getSQLState()) ;
            System.out.println(", Severity = " + sw.getErrorCode()) ;
            System.out.println(sw.getMessage()); 
            
            sw = sw.getNextWarning();
        }
    }

    public static void main(String[] args) {
        Connection con = null ;
        DatabaseMetaData dbmd = null ;
        
        try {
            Class.forName(driver) ;
            con = DriverManager.getConnection(url);

            SQLWarning swarn = con. getWarnings() ;
            
            if(swarn != null){
                printSQLWarning(swarn) ;
            }
            
            dbmd = con.getMetaData() ;
            
            System.out.println("\n----------------------------------------------------") ;
            System.out.println("Database Name    = " + dbmd.getDatabaseProductName()) ;
            System.out.println("Database Version = " + dbmd.getDatabaseProductVersion()) ;
            System.out.println("Driver Name      = " + dbmd.getDriverName()) ;
            System.out.println("Driver Version   = " + dbmd.getDriverVersion()) ;
            System.out.println("Database URL     = " + dbmd.getURL()) ;
            System.out.println("----------------------------------------------------") ;
            
        } catch (SQLException se) {
            printSQLException(se) ;
        } catch(ClassNotFoundException e){
            System.out.println("JDBC Driver " + driver + " not found in CLASSPATH") ;
        }
        finally {
            if(con != null){
                try{
                    con.close() ;
                } catch(SQLException se){
                    printSQLException(se) ;
                }
            }
        }
    }
}

JDBC で組み込み Apache Derby データベースへの接続を確立するために必要なのはこのクラスだけで、この記事の冒頭でコンパイルして実行したサンプル・コードの内容とまったく同じです。主な新規コードとして、プログラム・コードの先頭に 5 つの import 文が組み込まれています。代わりに import java.sql.* を使うこともできますが、各 import 文を別々にリストすることで、アプリケーションで使用している JDBC オブジェクトをはっきりと認識できるようにしています。

このアプリケーションにはリスト 4 で説明したエラー処理関数が組み込まれています。リスト 3 のデータベース・メタデータ操作を含め、すべてのデータベース操作は単一の try ... catch ブロック内にあります。データベース・アプリケーションの多くは、アプリケーションとデータベースの管理と保守を簡単にするため、このようなデータベース・アプリケーション・コードとデータベース間の密結合を避けるものですが、Apache Derby データベースの組み込み機能を考えると、アプリケーションとデータベースを分ける境界線は曖昧です。そのため、データベース・アプリケーション・コードとデータベースとの分離に厳格に従う必要はありません。

まとめ

この記事では、組み込み Apache Derby データベースに接続する Java アプリケーションの作成方法を説明しました。このプロセスでは Apache Derby プロジェクトの Type 4 JDBC ドライバーを使用して、データベースとの接続、データベースからのメタデータ抽出、そしてデータベース接続のクローズを行っています。また、Derby が生成する可能性のある Java アプリケーションでの SQL 警告と SQL 例外をどのように処理するかについても説明しました。今後の記事では、この記事で紹介した手法に基づいて、Derby データベースに対するクエリーを実行し、Java アプリケーション内からの結果を処理する方法を取り上げる予定です。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source, Information Management
ArticleID=247882
ArticleTitle=Apache Derbyを使用した開発 - 3連単を当てる: Apache Derbyを使用したJavaデータベース開発、パート1
publish-date=12122006