Apache Derbyを組み込みDBとして使う
Apache Derbyは、Javaで実装されたサーバ/クライアント型でも組み込み型でも使用できるデータベース。
今回は公式のマニュアルを参考に、以下の処理を行うプログラムを作ってみた。
まあようするに何の役にも立たないお勉強用プログラム・・・。
- データベースが存在しなければこれを作成する。
- データベースに必要なテーブルが存在しなければこれを作成する。
- データベースに接続し、SELECT/INSERTを行う。
作成手順は以下の通り。
- 公式サイトのダウンロード・ページから、最新バージョンのDerbyを取得。今回は直近の2013年4月15日にリリースされたバージョン10.10.1.1。
- Eclipseで適当なJavaプロジェクトを作成。プロジェクト・ディレクトリ内にlibディレクトリを作成。
- 先にダウンロードした db-derby-10.10.1.1-bin.zip を解凍すると作成されるフォルダに、libディレクトリがある。このディレクトリ内にある、derby.jar と derbyLocale_ja_JP.jar を、前述のJavaプロジェクト内のlibにコピー。プロジェクトのビルドパスに追加する。
- あとは適当にmainメソッドを持つJavaクラスを作成して、Derbyに接続して処理を行うプログラムを書くだけ。
プロジェクトの構成はこんな↓感じになった。
そしてプログラムの方はこんな↓感じ。
package com.m12i.java.derby.study; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Date; public class DerbyStudy { public static void main(String[] args) throws SQLException { // ドライバ名 final String driverName = org.apache.derby.jdbc.EmbeddedDriver.class.getCanonicalName(); // DB名(カレントディレクトリにこの名前でDBインスタンス=ディレクトリが作成される) final String dbName = "jdbcDemoDB"; // DB接続文字列(create=trueでDBが存在しない場合新規作成するように指定している) final String connectionURL = "jdbc:derby:" + dbName + ";create=true"; try { // ドライバをロードする Class.forName(driverName); // DB接続オブジェクトを参照する変数 Connection conn = null; try { // DB接続オブジェクトを取得 conn = DriverManager.getConnection(connectionURL); // DBが初期化されているかどうかを確認 if (!wasDbInitialized(conn)) { // 初期化されていない場合は初期化処理が必要 // アプリで使用するあれこれのDBオブジェクトを定義 // ... // 最後に、初期化判断に使用しているLOGテーブルを作成 final Statement s = conn.createStatement(); s.execute("create table LOG (" + " ID BIGINT generated always as identity constraint LOG_PK primary key" + ",TIMESTAMP TIMESTAMP with default CURRENT_TIMESTAMP" + ",MESSAGE VARCHAR(2000)" + ")"); } // DBの準備はできたので引き続きあれこれ... doSomething(conn); } catch (SQLException e) { // 接続に失敗 System.out.println(e.getMessage()); } finally { // 何はともあれDB接続をクローズ if (conn != null) conn.close(); } } catch (java.lang.ClassNotFoundException e) { // ドライバのロードに失敗 System.out.println(e.getMessage()); } } public static boolean wasDbInitialized(Connection conn) throws SQLException { // チェック用クエリが返す結果セットを参照するための変数 ResultSet rs = null; try { // データベースが初期化されているかどうかを検証するためUPDATE文を発行 final Statement s = conn.createStatement(); // LOGテーブルの件数をカウント // ※ここではこれ以上のチェックをしないことにする(テーブルのあるなしのみで判断することにする) rs = s.executeQuery("select count(1) from LOG"); } catch (SQLException e) { // 例外がスローされた。エラーコードを確認して処理を分岐 // SQLステートコードを取得。 final String state = e.getSQLState(); // ステートコードで処理を分岐。 if (state.equals("42X05")) { // テーブルが存在しない。データベースはまだ初期化されていない。 return false; } else if (state.equals("42X14") || state.equals("42821")) { // テーブル定義が不正。テーブルは存在するも想定した定義ではない... throw e; } else { // その他の予期せぬ例外が発生... throw e; } } finally { // ともかくもResultSetをクローズ if (rs != null) rs.close(); } // テーブルが存在した。(=すでに初期化されている。) return true; } private static void doSomething(Connection conn) throws SQLException { // 試みに、LOGテーブルへのINSERTを実施してみる final PreparedStatement insert = conn .prepareStatement("insert into LOG(MESSAGE) values (?)"); insert.setString(1, String.format("Execute at %s.", new Date())); insert.execute(); // 試みに、LOGテーブルからのSELECTを実施してみる final ResultSet rs = conn.createStatement() .executeQuery("select * from LOG order by ID"); while (rs.next()) { System.out.println( String.format("ID=%s, TIMESTAMP='%s', MESSAGE='%s'", rs.getString(1), rs.getString(2), rs.getString(3))); } } }
ふむ・・・。データベースが初期化済みか否かの判断はもう少しスマートな方法がありそうだけど。とにかく公式マニュアルに紹介されたサンプル・プログラムではこういうことをしていた。
このプログラムは初回起動時にはデータベースとテーブルを新規に作成、その後は実行するたびにテーブルへのINSERTを繰り返す。実行結果の標準出力は以下のようになる。
ID=1, TIMESTAMP='2013-04-27 19:47:12.703', MESSAGE='Execute at Sat Apr 27 19:47:12 JST 2013.' ID=2, TIMESTAMP='2013-04-27 19:47:17.212', MESSAGE='Execute at Sat Apr 27 19:47:17 JST 2013.' ID=3, TIMESTAMP='2013-04-27 19:49:48.608', MESSAGE='Execute at Sat Apr 27 19:49:48 JST 2013.' ID=4, TIMESTAMP='2013-04-27 19:50:16.914', MESSAGE='Execute at Sat Apr 27 19:50:16 JST 2013.'