Symfoware

Symfowareについての考察blog

WindowsでFirebird embeddedにJava1.7 + Jaybirdから接続する

UbuntuでFirebirdのembeddedモードを試してみました。
Firebird embeddedにJava1.7 + Jaybird + Ubuntuで接続する

Windowsでも動作を確認してみます。

Java 1.7.0.51 + Jaybird 2.2.4で試してみます。
OSはWindows 7 32bitを使用しました。





ダウンロード



JDBCドライバであるJaybirdを以下のURLからダウンロードします。
http://www.firebirdsql.org/en/jdbc-driver/

352_01.png


「Jaybird-2.2.4JDK_1.7.zip」をダウンロードしました。


解凍したフォルダの中に「jaybird-full-2.2.4.jar」があるので、
これを取り出しておきます。


もうひとつ、「jaybird22.dll」も取り出しておきます。

OSに応じて、使用するファイルは異なります。

Windows 32bit:jaybird22.dll
Windows 64bit:jaybird22_x64.dll
Linux 32bit:libjaybird22.so
Linux 64bit:libjaybird22_x64.so




Firebird 32-bit Embeddedを以下のURLからダウンロードします。
http://www.firebirdsql.org/en/firebird-2-5-2-upd1/

352_02.png


今回はWindows 7 32bitで動かすので、
「Firebird-2.5.2.26540-0_Win32_embed.zip」をダウンロードしました。

解凍したフォルダの中から以下のファイルを取り出しておきます。

fbembed.dll
ib_util.dll
icudt30.dll
icuin30.dll
icuuc30.dll






サンプルプログラム



サンプルはUbuntuで試した時と同じです。


  1. import java.sql.Connection;
  2. import java.sql.DriverManager;
  3. import java.sql.ResultSet;
  4. import java.sql.Statement;
  5. public class Sample {
  6.     
  7.     public static void main(String... args) throws Exception {
  8.         String url = "jdbc:firebirdsql:embedded:C:\\Develop\\fbemb\\post.fdb?encoding=UTF8";
  9.         
  10.         try (Connection con = DriverManager.getConnection(url);
  11.                 Statement stmt = con.createStatement()) {
  12.             
  13.             // テーブル作成
  14.             stmt.execute("create table embedtest (id int, name varchar(100))");
  15.             
  16.             // データ登録
  17.             stmt.execute("insert into embedtest(id,name) values(1,'テストだよー')");
  18.             
  19.             // 登録したデータを検索
  20.             ResultSet rs = stmt.executeQuery("select * from embedtest");
  21.             while(rs.next()) {
  22.                 System.out.println(rs.getString("name"));
  23.             }
  24.         }
  25.     }
  26.     
  27. }




今回は統合開発環境がない端末で動作確認しているので、
手動でコンパイルしました。

上記の内容を「Sampl.java」というファイル名で保存しておきます。

ファイルを保存した階層に「lib」というフォルダを作成し、
・jaybird-full-2.2.4.jar
・jaybird22.dll
をコピーしました。


また「dll」というフォルダを作成し、
・fbembed.dll
・ib_util.dll
・icudt30.dll
・icuin30.dll
・icuuc30.dll
をコピーしました。


データベースファイルであるfdbファイルは、
FreeBSD 10.0 + Firebird 2.5.2 データベースファイルを作成する
こちらで作成したものをコピーして使いました。


ソースコードのあるフォルダの内容はこんな感じ。

352_03.png

build.batはコンパイル用のバッチファイルです。


libフォルダの内容はこちら。

352_04.png


dllフォルダの内容はこちら。

352_05.png


コンパイル用のバッチファイル「build.bat」はこんな感じです。


@echo off
javac -cp .;lib\jaybird-full-2.2.4.jar Sample.java




実行すると、Sample.classが出力されます。


プログラムを実行するためのバッチファイルの内容はこんな感じ。


@echo off
set PATH=%PATH%;C:\Develop\fbemb\dll
java -cp .;lib\jaybird-full-2.2.4.jar -Djava.library.path=C:\Develop\fbemb\lib\ Sample




環境変数PATHに、Firebird embeddedのdll一式を保存したパスを設定。
-Djava.library.pathで「jaybird22.dll」を保存したパスを指定します。

実行するとちゃんと登録したデータが表示されました。

352_06.png





実行時のエラーについて



実行時によく発生するエラーについてメモしておきます。

エラー1


Exception in thread "main" java.sql.SQLException: No suitable driver found for j
dbc:firebirdsql:embedded:C:\Develop\fbemb\post.fdb?encoding=UTF8
        at java.sql.DriverManager.getConnection(Unknown Source)
        at java.sql.DriverManager.getConnection(Unknown Source)
        at Sample.main(Sample.java:11)




これは「-cp .;lib\jaybird-full-2.2.4.jar」のように
クラスパスにjaybird-full-2.2.4.jarを指定するのを忘れている場合。



エラー2


Exception in thread "main" java.lang.UnsatisfiedLinkError: no jaybird22 in java.
library.path
        at java.lang.ClassLoader.loadLibrary(Unknown Source)
        at java.lang.Runtime.loadLibrary0(Unknown Source)
        at java.lang.System.loadLibrary(Unknown Source)
        at org.firebirdsql.gds.impl.jni.JniGDSImpl.initJNIBridge(JniGDSImpl.java:64)
        at org.firebirdsql.gds.impl.jni.JniGDSImpl.<clinit>(JniGDSImpl.java:25)
        at org.firebirdsql.gds.impl.jni.EmbeddedGDSFactoryPlugin.getGDS(EmbeddedGDSFactoryPlugin.java:40)
        at org.firebirdsql.gds.impl.GDSFactory.getGDSForType(GDSFactory.java:275)
        at org.firebirdsql.jca.FBManagedConnectionFactory.getGDS(FBManagedConnectionFactory.java:114)
        at org.firebirdsql.jdbc.AbstractDriver.connect(AbstractDriver.java:120)
        at java.sql.DriverManager.getConnection(Unknown Source)
        at java.sql.DriverManager.getConnection(Unknown Source)
        at Sample.main(Sample.java:11)

Exception in thread "main" java.sql.SQLException: No suitable driver found for j
dbc:firebirdsql:embedded:C:\Develop\fbemb\post.fdb?encoding=UTF8
        at java.sql.DriverManager.getConnection(Unknown Source)
        at java.sql.DriverManager.getConnection(Unknown Source)
        at Sample.main(Sample.java:11)




これは、「-Djava.library.path=」でjaybird22.dllを保存したパスを
指定するのを忘れた時に発生します。



エラー3


Exception in thread "main" java.lang.RuntimeException: Failed to initialize Jaybird native library.
This is most likely due to a failure to load the firebird client library.
        at org.firebirdsql.gds.impl.jni.JniGDSImpl.attemptToLoadAClientLibraryFromList(JniGDSImpl.java:106)
        at org.firebirdsql.gds.impl.jni.EmbeddedGDSImpl.<init>(EmbeddedGDSImpl.java:31)
        at org.firebirdsql.gds.impl.jni.EmbeddedGDSImpl.<init>(EmbeddedGDSImpl.java:21)
        at org.firebirdsql.gds.impl.jni.EmbeddedGDSFactoryPlugin.getGDS(EmbeddedGDSFactoryPlugin.java:40)
        at org.firebirdsql.gds.impl.GDSFactory.getGDSForType(GDSFactory.java:275)
        at org.firebirdsql.jca.FBManagedConnectionFactory.getGDS(FBManagedConnectionFactory.java:114)
        at org.firebirdsql.jdbc.AbstractDriver.connect(AbstractDriver.java:120)
        at java.sql.DriverManager.getConnection(Unknown Source)
        at java.sql.DriverManager.getConnection(Unknown Source)
        at Sample.main(Sample.java:11)




これは、「SET PATH=」で、Firebirdのdllへのパスの通し忘れ。
もしくは、必要なFirebirdのdllが不足している時です。

fbembed.dllだけでなく、
・ib_util.dll
・icudt30.dll
・icuin30.dll
・icuuc30.dll
も存在している必要があります。

環境によっては、
・msvcp80.dll
・msvcr80.dll
・Microsoft.VC80.CRT.manifest
も必要かもしれません。





テーマ:データベース - ジャンル:コンピュータ

  1. 2014/03/05(水) 22:07:19|
  2. Firebird
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Firebird embeddedにJava1.7 + Jaybird + Ubuntuで接続する

Firebirdにはembeddedモードがあるようです。
このモードだと、サーバーをインストールすることなく
データベースファイルに接続できる模様。

SQLiteと同様に扱えるという解釈でいいのかな?と思います。

Java 1.7 + Jaybirdで試してみます。
OSはUbuntu 12.04 64bitを使用しました。





ダウンロード



JDBCドライバであるJaybirdを以下のURLからダウンロードします。
http://www.firebirdsql.org/en/jdbc-driver/

351_01.png


「Jaybird-2.2.4JDK_1.7.zip」をダウンロードしました。


解凍したフォルダの中に「jaybird-full-2.2.4.jar」があるので、
これを取り出しておきます。


もうひとつ、「libjaybird22_x64.so」も取り出しておきます。

OSに応じて、使用するファイルは異なります。

Windows 32bit:jaybird22.dll
Windows 64bit:jaybird22_x64.dll
Linux 32bit:libjaybird22.so
Linux 64bit:libjaybird22_x64.so





Firebird 64-bit Embeddedを以下のURLからダウンロードします。
http://www.firebirdsql.org/en/firebird-2-5-2-upd1/

351_02.png


今回はUbuntu 12.04 64bitで動かすので、
「FirebirdCS-2.5.2.26540-0.amd64.tar.gz」をダウンロードしました。

解凍したフォルダの中に「buildroot.tar.gz」という圧縮ファルがあるので、更に解凍。


opt/firebird/libの中にあるファイルを全て取り出しておきます。


libfbclient.so
libfbclient.so.2
libfbclient.so.2.5.2
libfbembed.so
libfbembed.so.2.5
libfbembed.so.2.5.2
libib_util.so
libicudata.so
libicudata.so.30
libicudata.so.30.0
libicui18n.so
libicui18n.so.30
libicui18n.so.30.0
libicuuc.so
libicuuc.so.30
libicuuc.so.30.0



libfbembed.so.2.5.2だけでいいのかな?と思っていましたが、他のファイルも必要です。
このセットがないとエラーになりました。








サンプルプログラム



こちらを参考にしました。
How to use Firebird embedded with Java (via JDBC)?

libfbembed.so.2.5.2だけあればいいように思いますが、
上記の通り全てのファイルが必要っぽいです。

こんなエラーが発生します。


Exception in thread "main" java.lang.RuntimeException: Failed to initialize Jaybird native library. This is most likely due to a failure to load the firebird client library.
    at org.firebirdsql.gds.impl.jni.JniGDSImpl.attemptToLoadAClientLibraryFromList(JniGDSImpl.java:106)
    at org.firebirdsql.gds.impl.jni.EmbeddedGDSImpl.<init>(EmbeddedGDSImpl.java:31)
    at org.firebirdsql.gds.impl.jni.EmbeddedGDSImpl.<init>(EmbeddedGDSImpl.java:21)
    at org.firebirdsql.gds.impl.jni.EmbeddedGDSFactoryPlugin.getGDS(EmbeddedGDSFactoryPlugin.java:40)
    at org.firebirdsql.gds.impl.GDSFactory.getGDSForType(GDSFactory.java:275)
    at org.firebirdsql.jca.FBManagedConnectionFactory.getGDS(FBManagedConnectionFactory.java:114)
    at org.firebirdsql.jdbc.AbstractDriver.connect(AbstractDriver.java:120)
    at java.sql.DriverManager.getConnection(DriverManager.java:579)
    at java.sql.DriverManager.getConnection(DriverManager.java:243)
    at sample.FBEmbedded.main(FBEmbedded.java:15)




http://sourceforge.net/p/firebird/code/HEAD/tree/client-java/tags/Jaybird_2_2_4/src/native/jaygds/source/generic_unix/platform.cpp
ここらへんを読んで、dlopenを調べてやっとわかりました。


サンプルプログラムは簡単です。


  1. import java.sql.Connection;
  2. import java.sql.DriverManager;
  3. import java.sql.ResultSet;
  4. import java.sql.Statement;
  5. public class FBEmbedded {
  6.     
  7.     public static void main(String[] args) throws Exception {
  8.         
  9.         String url = "jdbc:firebirdsql:embedded:/home/baranche/post.fdb?encoding=UTF8";
  10.         
  11.         try (Connection con = DriverManager.getConnection(url);
  12.                 Statement stmt = con.createStatement()) {
  13.             
  14.             // テーブル作成
  15.             stmt.execute("create table embtest (id int, name varchar(100))");
  16.             
  17.             // データ登録
  18.             stmt.execute("insert into embtest(id,name) values(1,'テストだよー')");
  19.             
  20.             // 登録したデータを検索
  21.             ResultSet rs = stmt.executeQuery("select * from embtest");
  22.             while(rs.next()) {
  23.                 System.out.println(rs.getString("name"));
  24.             }
  25.         }
  26.     }
  27. }




ただ、実行するまでが長かった。
Eclipseで実行する場合、「実行の構成」に手を加える必要があります。

確認するポイントは2つ。

「引数」タブの「VM引数」に、「libjaybird22_x64.so」を保存したディレクトリのパスを指定します。
私は「/home/baranche/workspace/fbsample/lib/libjaybird22_x64.so」に保存したので、
こういう指定を行いました。


-Djava.library.path=/home/baranche/workspace/fbsample/lib



351_03.png


「環境」タブに変数を追加します。
名前が「LD_LIBRARY_PATH」
値は「libfbclient.soやlibfbembed.soをコピーしたパス」

私は「/home/baranche/workspace/fbsample/embed」にファイルをコピーしたので、
こんな指定になりました。

351_04.png


これでサンプルが実行できるはずです。
fdbファイルは、
FreeBSD 10.0 + Firebird 2.5.2 データベースファイルを作成する
こちらで作成したものをコピーして使いました。

サーバーで作成したファイルにもテーブルの作成が行えます。
ただし、ID、パスワード付きでログインして作成したテーブルは
読み取り、書き込み共にアクセス権がないというエラーになりました。


Exception in thread "main" org.firebirdsql.jdbc.FBSQLException: GDS Exception. 335544352. no permission for read/select access to TABLE POST_NORMAL
    at org.firebirdsql.jdbc.AbstractStatement.execute(AbstractStatement.java:869)
    at sample.FBEmbedded.main(FBEmbedded.java:21)
Caused by: org.firebirdsql.gds.GDSException: no permission for read/select access to TABLE POST_NORMAL
    at org.firebirdsql.gds.impl.jni.JniGDSImpl.native_isc_dsql_prepare(Native Method)
    at org.firebirdsql.gds.impl.jni.BaseGDSImpl.iscDsqlPrepare(BaseGDSImpl.java:576)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.firebirdsql.gds.impl.jni.GDSSynchronizationPolicy$AbstractSynchronizationPolicy.invoke(GDSSynchronizationPolicy.java:119)
    at com.sun.proxy.$Proxy0.iscDsqlPrepare(Unknown Source)
    at org.firebirdsql.gds.impl.GDSHelper.prepareStatement(GDSHelper.java:190)
    at org.firebirdsql.jdbc.AbstractStatement.prepareFixedStatement(AbstractStatement.java:1441)
    at org.firebirdsql.jdbc.AbstractStatement.internalExecute(AbstractStatement.java:1423)
    at org.firebirdsql.jdbc.AbstractStatement.execute(AbstractStatement.java:867)
    ... 1 more






【参考URL】

Failed to load Firebird client library: Embedded database with Jaybird

Using Firebird with IBX under Linux

テーマ:データベース - ジャンル:コンピュータ

  1. 2014/03/04(火) 22:53:50|
  2. Firebird
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Firebird 2.5.2にJava1.7 + JaybirdでMap形式のデータ登録方法の考察

XML、JSON、MessagePackを使用して、Firebirdに
Map形式のデータを登録してみました。

Firebird 2.5.2にJava1.7 + JaybirdでXMLの登録と取得を行う
Firebird 2.5.2にJava1.7 + JaybirdでJSONの登録と取得を行う(JSONIC使用)
Firebird 2.5.2にJava1.7 + JaybirdでMessagePackの登録と取得を行う

もうひとつ案を思いついたので試してみます。



メインテーブルとサブテーブル



シリアルキーを保持するメインテーブルを定義。
サブテーブルでは、シリアルキー + key名をPrimary Keyとして、
値を保持する方法を考えてみます。

作成するテーブルはこの2つ。


CREATE TABLE post_hash_owner (
id int,
primary key(id)
);

CREATE TABLE post_hash (
id int,
hash_key varchar(100),
hash_value varchar(100),
primary key(id, hash_key)
);




post_hash_ownerにキーを束ねるためのシリアルを連番で登録。
post_hashに実体を登録します。




登録サンプル



郵便番号データを登録するサンプルは以下のようになりました。


  1. package com.fc2.blog68.symfoware.fb.hash;
  2. import java.io.BufferedReader;
  3. import java.io.FileInputStream;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.io.InputStreamReader;
  7. import java.sql.Connection;
  8. import java.sql.DriverManager;
  9. import java.sql.PreparedStatement;
  10. import java.util.HashMap;
  11. import java.util.Map;
  12. public class PostUpdateHash {
  13.     public static void main(String[] args) {
  14.         String url = "jdbc:firebirdsql:192.168.1.101/3050:/usr/local/firebird/post.fdb?encoding=UTF8";
  15.         String user = "sysdba";
  16.         String password = "masterkey";
  17.         String sqlOwner = "INSERT INTO post_hash_owner(id) VALUES (?)";
  18.         String sqlHash = "INSERT INTO post_hash(id, hash_key, hash_value) VALUES (?,?,?)";
  19.         try (Connection con = DriverManager.getConnection(url, user, password)) {
  20.             con.setAutoCommit(false);
  21.             try (PreparedStatement stmtOwner = con.prepareStatement(sqlOwner);
  22.                     PreparedStatement stmtHash = con.prepareStatement(sqlHash)) {
  23.                 load(con, stmtOwner, stmtHash);
  24.             } catch (Exception e) {
  25.                 con.rollback();
  26.                 throw e;
  27.             }
  28.             con.commit();
  29.         } catch (Exception e) {
  30.             
  31.             e.printStackTrace();
  32.         }
  33.     }
  34.     private static void load(Connection con, PreparedStatement stmtOwner, PreparedStatement stmtHash)
  35.             throws Exception {
  36.         try (InputStream in = new FileInputStream("data.csv");
  37.                 InputStreamReader isr = new InputStreamReader(in, "utf8");
  38.                 BufferedReader reader = new BufferedReader(isr)) {
  39.             
  40.             // 連番
  41.             int id = 0;
  42.             String line;
  43.             while ((line = reader.readLine()) != null) {
  44.                 String[] ary = line.split(",");
  45.                 // 他のプログラムと似たような処理にするため、
  46.                 // 一旦Mapに展開
  47.                 Map<String, String> map = new HashMap<String, String>();
  48.                 map.put("全国地方公共団体コード", ary[0]);
  49.                 map.put("郵便番号", ary[1]);
  50.                 map.put("都道府県名カナ", ary[2]);
  51.                 map.put("市区町村名カナ", ary[3]);
  52.                 map.put("町域名カナ", ary[4]);
  53.                 map.put("都道府県名", ary[5]);
  54.                 map.put("市区町村名", ary[6]);
  55.                 map.put("町域名", ary[7]);
  56.                 // シリアルID登録
  57.                 stmtOwner.setInt(1, ++id);
  58.                 stmtOwner.execute();
  59.                 stmtOwner.clearParameters();
  60.                 
  61.                 // ハッシュ情報登録
  62.                 for(Map.Entry<String, String> ent : map.entrySet()) {
  63.                     
  64.                     stmtHash.setInt(1, id);
  65.                     stmtHash.setString(2, ent.getKey());
  66.                     stmtHash.setString(3, ent.getValue());
  67.                     
  68.                     stmtHash.addBatch();
  69.                     stmtHash.clearParameters();
  70.                 }
  71.                 
  72.                 stmtHash.executeBatch();
  73.             }
  74.         } catch (IOException e) {
  75.             e.printStackTrace();
  76.         }
  77.     }
  78. }



実行したところ、想定通りの件数が登録されました。






検索サンプル



データを復元するサンプルはこんな感じになります。


  1. package com.fc2.blog68.symfoware.fb.hash;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.ResultSet;
  5. import java.sql.SQLException;
  6. import java.sql.Statement;
  7. import java.util.HashMap;
  8. import java.util.Iterator;
  9. import java.util.Map;
  10. public class PostSearchHash {
  11.     
  12.     public static void main(String[] args) throws Exception {
  13.         
  14.         String url = "jdbc:firebirdsql:192.168.1.101/3050:/usr/local/firebird/post.fdb?encoding=UTF8";
  15.         String user = "sysdba";
  16.         String password = "masterkey";
  17.         try (Connection con = DriverManager.getConnection(url,user, password);
  18.                 Statement stmt = con.createStatement()) {
  19.             StringBuilder query = new StringBuilder();
  20.             query.append("SELECT");
  21.             query.append(" post_hash.*");
  22.             query.append(" FROM");
  23.             query.append(" post_hash_owner,");
  24.             query.append(" post_hash");
  25.             query.append(" WHERE");
  26.             query.append(" (post_hash_owner.id = post_hash.id)");
  27.             query.append(" ORDER BY");
  28.             query.append(" post_hash.id");
  29.             // データ取得
  30.             ResultSet rs = stmt.executeQuery(query.toString());
  31.             
  32.             // イテレーター作成
  33.             PostList list = (new PostSearchHash()).new PostList(rs);
  34.             
  35.             for (Map<String, String> map : list) {
  36.                 
  37.                 if (map.get("町域名").indexOf("銀座") == -1) {
  38.                     continue;
  39.                 }
  40.                 StringBuilder sb = new StringBuilder();
  41.                 sb.append(map.get("都道府県名"));
  42.                 sb.append(map.get("市区町村名"));
  43.                 sb.append(map.get("町域名"));
  44.                 System.out.println(sb.toString());
  45.             }
  46.         } catch (Exception e) {
  47.             e.printStackTrace();
  48.         }
  49.         
  50.     }
  51.     
  52.     public class PostList implements Iterable<Map<String, String>> {
  53.         
  54.         private PostIterater it;
  55.         
  56.         public PostList(ResultSet rs) throws SQLException {
  57.             it = new PostIterater(rs);
  58.         }
  59.         
  60.         @Override
  61.         public Iterator<Map<String, String>> iterator() {
  62.             return it;
  63.         }
  64.         
  65.     }
  66.     
  67.     class PostIterater implements Iterator<Map<String, String>> {
  68.         
  69.         private ResultSet rs;
  70.         private boolean hasNext = false;
  71.         
  72.         public PostIterater(ResultSet rs) throws SQLException {
  73.             this.rs = rs;
  74.             hasNext = rs.next();
  75.         }
  76.         
  77.         @Override
  78.         public boolean hasNext() {
  79.             return hasNext;
  80.         }
  81.         @Override
  82.         public Map<String, String> next() {
  83.             
  84.             Map<String, String> map = new HashMap<String, String>();
  85.             
  86.             try {
  87.             
  88.                 int id = rs.getInt("id");
  89.                 
  90.                 while(hasNext) {
  91.                     
  92.                     map.put(rs.getString("hash_key"), rs.getString("hash_value"));
  93.                     
  94.                     hasNext = rs.next();
  95.                     
  96.                     if (id != rs.getInt("id")) {
  97.                         break;
  98.                     }
  99.                 }
  100.             
  101.             } catch (Exception e) {
  102.                 hasNext = false;
  103.             }
  104.             
  105.             
  106.             return map;
  107.         }
  108.         @Override
  109.         public void remove() {
  110.         }
  111.         
  112.     }
  113.     
  114. }








直接検索



キーと値を各々フィールドで持っているので、上記の地名に「銀座」を
含む値の検索は、直接SQLで問い合わせることも可能です。



  1. package com.fc2.blog68.symfoware.fb.hash;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.ResultSet;
  5. import java.sql.SQLException;
  6. import java.sql.Statement;
  7. import java.util.HashMap;
  8. import java.util.Iterator;
  9. import java.util.Map;
  10. public class PostSearchDirectHash {
  11.     
  12.     public static void main(String[] args) throws Exception {
  13.         
  14.         String url = "jdbc:firebirdsql:192.168.1.101/3050:/usr/local/firebird/post.fdb?encoding=UTF8";
  15.         String user = "sysdba";
  16.         String password = "masterkey";
  17.         try (Connection con = DriverManager.getConnection(url,user, password);
  18.                 Statement stmt = con.createStatement()) {
  19.             StringBuilder query = new StringBuilder();
  20.             query.append("SELECT");
  21.             query.append(" a.*");
  22.             query.append(" FROM");
  23.             query.append(" post_hash a");
  24.             query.append(" ,post_hash b");
  25.             
  26.             query.append(" WHERE");
  27.             // 「銀座」を含むデータを取得
  28.             query.append(" (b.hash_key = '町域名')");
  29.             query.append(" AND (b.hash_value LIKE '%銀座%')");
  30.             query.append(" AND (a.id = b.id)");
  31.             query.append(" ORDER BY");
  32.             query.append(" a.id");
  33.             // データ取得
  34.             ResultSet rs = stmt.executeQuery(query.toString());
  35.             
  36.             // イテレーター作成
  37.             PostList list = (new PostSearchDirectHash()).new PostList(rs);
  38.             
  39.             for (Map<String, String> map : list) {
  40.                 StringBuilder sb = new StringBuilder();
  41.                 sb.append(map.get("都道府県名"));
  42.                 sb.append(map.get("市区町村名"));
  43.                 sb.append(map.get("町域名"));
  44.                 System.out.println(sb.toString());
  45.             }
  46.         } catch (Exception e) {
  47.             e.printStackTrace();
  48.         }
  49.         
  50.     }
  51.     
  52.     public class PostList implements Iterable<Map<String, String>> {
  53.         
  54.         private PostIterater it;
  55.         
  56.         public PostList(ResultSet rs) throws SQLException {
  57.             it = new PostIterater(rs);
  58.         }
  59.         
  60.         @Override
  61.         public Iterator<Map<String, String>> iterator() {
  62.             return it;
  63.         }
  64.         
  65.     }
  66.     
  67.     class PostIterater implements Iterator<Map<String, String>> {
  68.         
  69.         private ResultSet rs;
  70.         private boolean hasNext = false;
  71.         
  72.         public PostIterater(ResultSet rs) throws SQLException {
  73.             this.rs = rs;
  74.             hasNext = rs.next();
  75.         }
  76.         
  77.         @Override
  78.         public boolean hasNext() {
  79.             return hasNext;
  80.         }
  81.         @Override
  82.         public Map<String, String> next() {
  83.             
  84.             Map<String, String> map = new HashMap<String, String>();
  85.             
  86.             try {
  87.             
  88.                 int id = rs.getInt("id");
  89.                 
  90.                 while(hasNext) {
  91.                     
  92.                     map.put(rs.getString("hash_key"), rs.getString("hash_value"));
  93.                     
  94.                     hasNext = rs.next();
  95.                     
  96.                     if (id != rs.getInt("id")) {
  97.                         break;
  98.                     }
  99.                 }
  100.             
  101.             } catch (Exception e) {
  102.                 hasNext = false;
  103.             }
  104.             
  105.             
  106.             return map;
  107.         }
  108.         @Override
  109.         public void remove() {
  110.         }
  111.         
  112.     }
  113.     
  114. }







通常のテーブル構成



速度を比較するため、一般的なテーブルを作成し、データを登録してみました。


CREATE TABLE post_normal(
lasdeccode CHAR(5),
zip_code CHAR(7),
prefkn VARCHAR(20),
citykn VARCHAR(100),
areakn VARCHAR(100),
pref VARCHAR(20),
city VARCHAR(100),
area VARCHAR(100)
)




ソースは割愛。





登録速度比較



まずは登録から。単位はmsです。

パターン1回目2回目3回目4回目5回目平均
XML(BLOB)57,23472,97773,54572,43872,58669,756
XML(VARCHAR)59,28159,34758,68858,72059,04659,016
JSON(BLOB)53,70762,68962,21161,70061,13960,289
JSON(VARCHAR)31,49653,59251,98652,27054,83848,836
MessagePack51,47358,54659,41058,99757,93057,271
HASH290,553293,528296,088291,952291,940292,812
NORMAL22,61422,53521,64322,61222,48022,377


350_01.png

いいアイデアだと思ったテーブル分割方式は芳しくなかったです。
やはり通常の登録方法が最速。
ほかはどれを選んでも大勢に影響はない感じかと思います。





検索速度比較



次は検索速度の比較です。

パターン1回目2回目3回目4回目5回目平均
XML(BLOB)38,30037,48036,87536,76337,32837,349
XML(VARCHAR)4,4843,8053,7313,7483,7003,894
JSON(BLOB)39,67239,54939,07039,14837,68639,025
JSON(VARCHAR)4,4404,2994,3694,3994,4024,382
MessagePack45,48745,13145,29845,55745,24445,343
HASH17,26917,16717,71017,37617,15817,336
HASH(DIRECT)7,6977,6737,5727,6757,6857,660
NORMAL2,1452,0892,2162,1072,1722,146
NORMAL(DIRECT)216213209209198209


350_02.png

HASH、NORMALのDIRECTとはクエリで絞り込みを行った結果です。
それ以外は全件フェッチして、取得したいデータを探した時の時間です。

登録では、純粋に登録レコード数が多いためテーブル分割は不利でしたが、
検索時はなかなかの性能を見せていれています。

だた、XMLやJSONをVARCHARに登録したものには及びません。

なかなかいい勉強になりました。




サンプルプログラムはここに置いておきます。
https://bitbucket.org/symfo/firebird-map-sample/src/

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2014/03/03(月) 00:32:15|
  2. Firebird
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Firebird 2.5.2にJava1.7 + JaybirdでMessagePackの登録と取得を行う

JavaからFirebirdに接続してみました。
Firebird 2.5.2にJava1.7からJaybirdで接続する

今回はMessagePackなバイナリを登録してみます。


テーブルの作成



BLOBのSUB_TYPEに「BINARY」を指定したフィールドを作成。
ここにMessagePackなバイナリを登録してみることにしました。


CREATE TABLE post_mp(
address BLOB SUB_TYPE BINARY
)






登録



こちらで実行した内容を参考にします。
MessagePackでシリアライズした内容をPostgreSQLのbytea列にJavaで登録する

同様に郵便番号情報約12万件を登録してみます。
サンプルはこんな感じになりました。


  1. package com.fc2.blog68.symfoware.fb.mp;
  2. import java.io.BufferedReader;
  3. import java.io.ByteArrayOutputStream;
  4. import java.io.FileInputStream;
  5. import java.io.IOException;
  6. import java.io.InputStream;
  7. import java.io.InputStreamReader;
  8. import java.sql.Connection;
  9. import java.sql.DriverManager;
  10. import java.sql.PreparedStatement;
  11. import java.util.HashMap;
  12. import java.util.Map;
  13. import org.msgpack.MessagePack;
  14. import org.msgpack.packer.Packer;
  15. public class PostUpdateMsgPack {
  16.     public static void main(String[] args) {
  17.         String url = "jdbc:firebirdsql:192.168.1.101/3050:/usr/local/firebird/post.fdb?encoding=UTF8";
  18.         String user = "sysdba";
  19.         String password = "masterkey";
  20.         String sql = "INSERT INTO post_mp(address) VALUES (?)";
  21.         try (Connection con = DriverManager.getConnection(url, user, password)) {
  22.             con.setAutoCommit(false);
  23.             try (PreparedStatement stmt = con.prepareStatement(sql)) {
  24.                 load(con, stmt);
  25.             } catch (Exception e) {
  26.                 e.printStackTrace();
  27.             }
  28.             con.commit();
  29.         } catch (Exception e) {
  30.             e.printStackTrace();
  31.         }
  32.     }
  33.     private static void load(Connection con, PreparedStatement stmt)
  34.             throws Exception {
  35.         try (InputStream in = new FileInputStream("data.csv");
  36.                 InputStreamReader isr = new InputStreamReader(in, "utf8");
  37.                 BufferedReader reader = new BufferedReader(isr)) {
  38.             MessagePack msgpack = new MessagePack();
  39.             String line;
  40.             while ((line = reader.readLine()) != null) {
  41.                 String[] ary = line.split(",");
  42.                 Map<String, String> map = new HashMap<String, String>();
  43.                 map.put("全国地方公共団体コード", ary[0]);
  44.                 map.put("郵便番号", ary[1]);
  45.                 map.put("都道府県名カナ", ary[2]);
  46.                 map.put("市区町村名カナ", ary[3]);
  47.                 map.put("町域名カナ", ary[4]);
  48.                 map.put("都道府県名", ary[5]);
  49.                 map.put("市区町村名", ary[6]);
  50.                 map.put("町域名", ary[7]);
  51.                 // シリアライズ実行
  52.                 ByteArrayOutputStream out = new ByteArrayOutputStream();
  53.                 Packer packer = msgpack.createPacker(out);
  54.                 packer.write(map);
  55.                 byte[] bytes = out.toByteArray();
  56.                 stmt.setBytes(1, bytes);
  57.                 stmt.execute();
  58.                 stmt.clearParameters();
  59.             }
  60.         } catch (IOException e) {
  61.             e.printStackTrace();
  62.         }
  63.     }
  64. }




特にエラーもなく登録完了です。




検索



検索も行なってみます。


  1. package com.fc2.blog68.symfoware.fb.mp;
  2. import static org.msgpack.template.Templates.TString;
  3. import static org.msgpack.template.Templates.tMap;
  4. import java.io.ByteArrayInputStream;
  5. import java.sql.Connection;
  6. import java.sql.DriverManager;
  7. import java.sql.ResultSet;
  8. import java.sql.Statement;
  9. import java.util.Map;
  10. import org.msgpack.MessagePack;
  11. import org.msgpack.template.Template;
  12. import org.msgpack.unpacker.Unpacker;
  13. public class PostSearchMsgPack {
  14.     public static void main(String[] args) throws Exception {
  15.         
  16.         String url = "jdbc:firebirdsql:192.168.1.101/3050:/usr/local/firebird/post.fdb?encoding=UTF8";
  17.         String user = "sysdba";
  18.         String password = "masterkey";
  19.         try (Connection con = DriverManager.getConnection(url,user, password);
  20.                 Statement stmt = con.createStatement()) {
  21.             String query = "SELECT * FROM post_mp";
  22.             // nameタグの値を取得
  23.             ResultSet rs = stmt.executeQuery(query);
  24.             
  25.             MessagePack msgpack = new MessagePack();
  26.             // 復元用の定義体
  27.             Template<Map<String, String>> mapTmpl = tMap(TString, TString);
  28.             while (rs.next()) {
  29.                 
  30.                 byte[] bytes = rs.getBytes("address");
  31.                 
  32.                 // デシリアライズ
  33.                 ByteArrayInputStream in = new ByteArrayInputStream(bytes);
  34.                 Unpacker unpacker = msgpack.createUnpacker(in);
  35.                 
  36.                 Map<String, String> map =unpacker.read(mapTmpl);
  37.                 
  38.                 if (map.get("町域名").indexOf("銀座") == -1) {
  39.                     continue;
  40.                 }
  41.                 StringBuilder sb = new StringBuilder();
  42.                 sb.append(map.get("都道府県名"));
  43.                 sb.append(map.get("市区町村名"));
  44.                 sb.append(map.get("町域名"));
  45.                 System.out.println(sb.toString());
  46.             }
  47.         } catch (Exception e) {
  48.             e.printStackTrace();
  49.         }
  50.         
  51.     }
  52. }



想定通りの結果が得られました。



速度



登録と検索の速度を測定してみます。

まず、登録速度。単位はmsです。

1回目2回目3回目4回目5回目平均
51,47358,54659,41058,99757,93057,271



次は検索。

1回目2回目3回目4回目5回目平均
45,48745,13145,29845,55745,24445,343


全件フェッチに、登録と同じくらい時間がかかるようです。



サンプルプログラムはここに置いておきます。
https://bitbucket.org/symfo/firebird-map-sample/src/

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2014/03/02(日) 21:38:08|
  2. Firebird
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Firebird 2.5.2にJava1.7 + JaybirdでJSONの登録と取得を行う(JSONIC使用)

JavaからFirebirdに接続してみました。
Firebird 2.5.2にJava1.7からJaybirdで接続する

今回はJSONなテキストを登録してみます。


テーブルの作成



BLOBのSUB_TYPEに「TEXT」を指定したフィールドを作成。
ここにJSONな文字列を登録してみることにしました。


CREATE TABLE post_json(
address BLOB SUB_TYPE TEXT
)







JSONIC



JSONのエンコード、デコードには「JSONIC」を使用することにしました。
http://jsonic.sourceforge.jp/

ライブラリをダウンロードして解凍すると、「jsonic-1.3.2.jar」が入ってますので、
これをビルドパスに含めておきます。

Map形式のオブジェクトをエンコード、デコードするサンプルはこんな感じになりました。


  1. import java.util.HashMap;
  2. import java.util.Map;
  3. import net.arnx.jsonic.JSON;
  4. import net.arnx.jsonic.TypeReference;
  5. public class FBJSONSample {
  6.     
  7.     public static void main(String[] args) throws Exception {
  8.         
  9.         // エンコードしたいオブジェクトを作成
  10.         Map<String, String> map = new HashMap<String, String>();
  11.         map.put("key", "value");
  12.         map.put("日本語のキー", "日本語の値");
  13.         
  14.         // 変換してくれるオブジェクト
  15.         JSON json = new JSON();
  16.         // --- エンコード
  17.         // formatでJSON文字列に変換
  18.         String text = json.format(map);
  19.         // デバッグ用に変換後の内容を表示
  20.         System.out.println(text);
  21.         
  22.         
  23.         // --- デコード
  24.         Map<String, String> ret = JSON.decode(text, new TypeReference<Map<String, String>>() {});
  25.         // 元に戻せたか表示
  26.         System.out.println("key:" + ret.get("key"));
  27.         System.out.println("日本語のキー:" + ret.get("日本語のキー"));
  28.     }
  29.     
  30. }




実行してみるとこんな結果が得られます。


{"日本語のキー":"日本語の値","key":"value"}
key:value
日本語のキー:日本語の値




これはお手軽で便利です。




登録



こちらで行ったのと同様、郵便番号情報約12万件を登録してみます。
Firebird 2.5.2にJava1.7 + JaybirdでXMLの登録と取得を行う

サンプルソースはこんな感じになりました。


  1. package com.fc2.blog68.symfoware.fb.json;
  2. import java.io.BufferedReader;
  3. import java.io.FileInputStream;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.io.InputStreamReader;
  7. import java.sql.Connection;
  8. import java.sql.DriverManager;
  9. import java.sql.PreparedStatement;
  10. import java.util.HashMap;
  11. import java.util.Map;
  12. import net.arnx.jsonic.JSON;
  13. public class PostUpdateJSON {
  14.     public static void main(String[] args) throws Exception {
  15.         String url = "jdbc:firebirdsql:192.168.1.101/3050:/usr/local/firebird/post.fdb?encoding=UTF8";
  16.         String user = "sysdba";
  17.         String password = "masterkey";
  18.         String sql = "INSERT INTO post_json(address) VALUES (?)";
  19.         try (Connection con = DriverManager.getConnection(url, user, password);
  20.                 PreparedStatement pstmt = con.prepareStatement(sql)) {
  21.             load(con, pstmt);
  22.         }
  23.     }
  24.     private static void load(Connection con, PreparedStatement pstmt) throws Exception {
  25.         try (InputStream in = new FileInputStream("data.csv");
  26.                 InputStreamReader isr = new InputStreamReader(in, "utf8");
  27.                 BufferedReader reader = new BufferedReader(isr)) {
  28.             // 変換してくれるオブジェクト
  29.             JSON json = new JSON();
  30.             String line;
  31.             while ((line = reader.readLine()) != null) {
  32.                 String[] ary = line.split(",");
  33.                 // 登録用のMapオブジェクトを生成
  34.                 Map<String, String> map = new HashMap<String, String>();
  35.                 map.put("全国地方公共団体コード", ary[0]);
  36.                 map.put("郵便番号", ary[1]);
  37.                 map.put("都道府県名カナ", ary[2]);
  38.                 map.put("市区町村名カナ", ary[3]);
  39.                 map.put("町域名カナ", ary[4]);
  40.                 map.put("都道府県名", ary[5]);
  41.                 map.put("市区町村名", ary[6]);
  42.                 map.put("町域名", ary[7]);
  43.                 // Map => json文字列に変換
  44.                 String jsonString = json.format(map);
  45.                 // 登録実行
  46.                 pstmt.setString(1, jsonString);
  47.                 pstmt.execute();
  48.                 pstmt.clearParameters();
  49.             }
  50.         } catch (IOException e) {
  51.             e.printStackTrace();
  52.         }
  53.     }
  54. }




ちゃんとJSONな文字列が登録されました。

349_01.png





検索



登録したデータがデコードできるかのテストのため、
住所に「銀座」を含むデータを抽出してみます。


  1. package com.fc2.blog68.symfoware.fb.json;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.ResultSet;
  5. import java.sql.Statement;
  6. import java.util.Map;
  7. import net.arnx.jsonic.JSON;
  8. import net.arnx.jsonic.TypeReference;
  9. public class PostSearchJSON {
  10.     
  11.     public static void main(String[] args) throws Exception {
  12.         
  13.         String url = "jdbc:firebirdsql:192.168.1.101/3050:/usr/local/firebird/post.fdb?encoding=UTF8";
  14.         String user = "sysdba";
  15.         String password = "masterkey";
  16.         
  17.         String sql = "SELECT address FROM post_json";
  18.         
  19.         try (Connection con = DriverManager.getConnection(url, user, password);
  20.                 Statement stmt = con.createStatement()){
  21.             
  22.             // Mapに変換するための定義
  23.             TypeReference<Map<String, String>> type = new TypeReference<Map<String, String>>(){};
  24.             
  25.             try (ResultSet rs = stmt.executeQuery(sql)) {
  26.                 
  27.                 while(rs.next()) {
  28.                     
  29.                     // json文字列からMapオブジェクトに変換
  30.                     String jsonString = rs.getString("address");
  31.                     Map<String, String> map = JSON.decode(jsonString, type);
  32.                     
  33.                     if (map.get("町域名").indexOf("銀座") == -1) {
  34.                         continue;
  35.                     }
  36.                     StringBuilder sb = new StringBuilder();
  37.                     sb.append(map.get("都道府県名"));
  38.                     sb.append(map.get("市区町村名"));
  39.                     sb.append(map.get("町域名"));
  40.                     System.out.println(sb.toString());
  41.                     
  42.                 }
  43.                 
  44.             }
  45.         }
  46.     }
  47. }




実行してみると想定通りの結果が得られました。




速度評価



ここと同様、BLOBとVARCHARの速度差を調べてみました。
Firebird 2.5.2にJava1.7 + JaybirdでXMLの登録と取得を行う

BLOBでの定義

CREATE TABLE post_json(
address BLOB SUB_TYPE TEXT
)



VARCHARでの定義

CREATE TABLE post_json(
address VARCHAR(8000)
)




まず、登録時の速度です。
(単位はms)

パターン1回目2回目3回目4回目5回目平均
BLOB(TEXT)500,478552,198606,607488,884514,157532,465
VARCHAR(8000)573,525568,446565,591578,187611,847579,519


XMLの時と同様、BLOBのほうが登録コストは低いようです。


続いて検索。

パターン1回目2回目3回目4回目5回目平均
BLOB(TEXT)39,67239,54939,07039,14837,68639,025
VARCHAR(8000)4,4404,2994,3694,3994,4024,382


同様に、VARCHARのほうがフェッチ速度が上です。





一括コミット



登録がやけに遅いなと思ったら、一括コミットにするのを忘れてました。
connection.setAutoCommit(false)
を指定して、insertクエリを全件実行した後コミットするよう変更します。

パターン1回目2回目3回目4回目5回目平均
BLOB(TEXT)53,70762,68962,21161,70061,13960,289
VARCHAR(8000)31,49653,59251,98652,27054,83848,836


かなり速度が改善されました。




サンプルプログラムはここに置いておきます。
https://bitbucket.org/symfo/firebird-map-sample/src/

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2014/03/02(日) 21:10:02|
  2. Firebird
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
前のページ 次のページ