しいしせねっと
[PostgreSQL関連][JDBC全般] [Developer.jp]
[日本PostgreSQLユーザ会]

PostgreSQL JDBCドライバのいろいろ対応

Last update 2006.10.24

Tomcat でServlet の開発をしていると、データベースにアクセスする必要などがでてきます。
PostgreSQLを使って開発をしていたのですが、いくつか動作のよくなさそうな点が出てきたので、直してみました。
ここには、PostgreSQL7.0.3用の日本語/多言語対応JDBCドライバ、PostgreSQL7.3.4/7.4.2用 Sun ONE Studio対応JDBCドライバの2種類があります。

最新のJDBCドライバは、オフィシャルなもので問題ないので作っていません。

もくじ?

順番ばらばら・・・

最新版に関する情報

最新版はもうこちらでは作っていません。オフィシャルなものを使ってください。

PostgreSQL 8.0からの型変換

PostgreSQL 7系までは、integer型に文字列を入れても数字的なものであれば自動的に型変換してくれました。8系になってからは、この型変換をしてくれないのでJDBCのPreparedStatement などではまってしまうかもしれません。ふつうにSQL文を書くだけなら、型変換してくれます。

create table foo (a int, b int);

PreparedStatement st = con.prepareStatement("insert into foo (a,b) values('123','456')");
st.execute();

PreparedStatement st = con.prepareStatement("insert into foo (a,b) values(?, ?)");
st.setInt(1,123);
st.setString(2,"456"); // この変換がうまくできない
st.execute();

aとbがどちらもintegerだったとしましょう。上のプログラムはなんとなく動き、下のプログラムはなんとなく動いてくれません。上のような直にデータをSQL中に埋めてしまうプログラムはSQLインジェクションなどで脆弱です。下のようにするには、SQLのtext やJavaのString などの文字列からintegerへのキャストを許可してやる必要があったりなかったり。

create cast (text as integer) with function int4(text);

のようなものが必要なんでしょうか? もともとあるのでできない? または

con.prepareStatement("insert into foo (a, b) values(cast (? as int), cast (? as int))");

的に明示的なcastを指定します。これはできるようです。

とりあえず以前と同じように使う方法は謎です。

参考

7系の必要だった古いもの

7.4.5対応のものを作りました。エラーメッセージが一つ増えていたのでその訳も追加。コードの変更は従来通り。

修正箇所はPostgreSQL 7.4のものと同じですが、元のJDBCドライバに修正が含まれているようですので、PostgreSQL 7.4.2、7.4.3、7.4.5ではこちらをお使いください。変更点はSRAのサイトなどを参照してみてください。→PostgereSQL 7.4.2から7.4.3の変更点

[SoftLib]にあります。

PostgreSQL7.4.3 JDBCドライバ

PostgreSQL7.4のときと修正点は同じです。PostgreSQL7.4のJDBCドライバではPostgreSQL7.4.2で不具合が起きる場合があるようですのでこちらをお使いください。

# tar zxvf postgresql-7.4.3.tar.gz
# cd postgresql-7.4.3
# patch -p1<../postgresql-7.4.3-jdbc.patch

動作確認

J2SDK 環境 PostgreSQL JDBCドライバ 動作
1.4.2_04 NetBeans IDE ja 3.6 7.4.2 EUC jdbc.postgresql.org 7.4 214
PostgreSQL src 7.4.2 ?
しいしせねっと7.4.2
1.4.2_04 Java Studio 6 Enterprise
2004Q1
7.4.2 EUC jdbc.postgresql.org 7.4 214 ×
PostgreSQL src 7.4.2 ×
しいしせねっと7.4.2

テスト項目 IDEの実行時タブで、テーブルなどが表示できること
EJBが使えること(未実施)
NetBeans IDEでは、この差分なしで使えるようですが、JDBCドライバの動作は修正しておいた方がいいかもしれません。

動作確認報告ください。

確認中項目

未確認項目

不具合報告

設定

Javaのjre/lib/ext ディレクトリ(JDK用("c:\j2sdk1.4.x"とJRE用("c:\Program Files\Java\..."の2つがありますが、Sun ONE Studioが使うのはJDK用)またはSunONE Studioのディレクトリ("c:\Program Files\s1studio\"のどこか?)です。そこへpostgresql.jarをコピーするだけ。

Sun ONE Studio 4・5、NetBeans IDEでの登録方法

  1. Sun ONE Studio の「エクスプローラ(Explorer)」の「実行時(Runtime)」を開く。
  2. データベース(Databases)のDriversから、PostgreSQL(7.0 and later)を選択。
  3. データベース URLとUser Name、Passwordを入力。
  4. 「詳細設定(Advanced)」の「スキーマを選択(Select schema)」でpublicを選択する。
  5. OK

うまくいかない場合は別のPostgreSQL用JDBCドライバが残っていないことを確認してください。それでもうまくいかないときは、JDBCドライバを直してみます。

主要クラス

普段使う場合にどれを指定すればいいのか、ちょっとメモ

java.sql.Driver型のクラス(JDK1.1/J2SE用)

JNDIから利用できるjavax.sql.DataSource型のクラス(J2EE用、J2SE1.4からも利用可)

DataSourceが使えると、コネクションpoolなどができます。Tomcat 5のconfig.xmlでは自前のコネクションpoolを実装しているようなので、こちらは設定できないようです。たぶん。Java System Application Serverなどで使えます。

プロパティ

固有かどうかわかりませんが、JNDIに設定するDataSourceを使うためのプロパティ

プロパティ名 内容
serverName サーバ名 db.example.com
databaseName データベース名 testdb
user ユーザ名 okome
password パスワード password
ssl なくていい? SSLで接続したいときに設定します  

java.sql.DriverManager.getConnection("jdbc:postgresql://db.example.com/testdb?ssl=true","okome","password") と同じようなことができます。DataSource系でクライアント認証ができるかどうかは未検証。

SSLを使う設定でサーバが動いている場合のみ、?sslのオプションを使うことができます。 8系ドライバではssl=true にしましょう。

SSLの詳細は[PostgreSQL SSL]に書いてみました。

設定のヒント

PostgreSQLは、日本語対応でmakeしましょう。->[PostgreSQL倉庫?]で日本語対応のインストール方法をメモしてみました
DBの初期化は EUC-JP または UNICODEでしましょう。EUC-KRなど、Javaで対応している文字コード(1バイト英字と共存できるもの)なら他のものでもたぶん可です。 Linuxコンソールなどから使う場合は、UnicodeにはせずにEUC-JPにしましょう。
psql の \l コマンドまたは、コマンドでpsql -lとすることで、文字コードを確認できます。

JDBCドライバのディレクトリは、postgresqlのsrc/interfaces/jdbc にあります。
そのファイルを日本語対応のものと入れ換えて、make jdbc2 でできた postgresql.jar ファイルを jre/lib/ext に置いたり、CLASSPATHを通したりします。

Class.forName("org.postgresql.Driver");
java.sql.DriverManager.getConnection("jdbc:postgresql:~",account,password);

と、PostgreSQL のJDBCドライバの設定そのままで使えます。

J2EE用とJ2SE用ドライバの違い

J2EE環境でmake する場合は、J2EEに対応したJDBCドライバが作られます。
違いはConnectionPool 関連だと思いますが、興味のある方は調べてみるといいのではないでせぅか。

# configure --with-java
# make
Java 2 SDKとJakarta-Ant が必要です。

注意
PostgreSQL 7.1以降のJDBCドライバは、標準で日本語にも対応していますので、日本語対応のためにこちらのJDBCドライバは必要ありません。 もし、問題があるようなら、再度作成します。Stringとbyteの変換で、たとえASCIIコードを想定したものであっても、文字コードの指定のないコードが1つでもあると、正しく動作しない場合があります。

不足機能

JDBC3系の実装が、全般で不足しているようです。
DatabaseMetadata 系のJDBCドライバがどの機能をサポートしているか? という根本的な部分がきちんと実装されていないようです。

要修正箇所

DatabaseMetaData.getTables() scheme名の大文字小文字の違いについて、Sun ONE Studio(NetBeans?)では同一視している。(統一されていない)PostgreSQLでは区別している。対応済。

テーブル名等について、PostgreSQL本体は区別している。psqlコマンドでは小文字に統一されている? JDBCドライバでは区別される。区別すべきではない?

boolean / bit 型のString化について、Sun ONE Studioなどはtrue/false を期待値としているが、PostgreSQLはt/fで返す。対応済。

文字とバイト列の変換、まだ変ですね。StringクラスのgetBytes() は、引数なしで使ってはいけません。将来的にOSのデフォルトエンコードがUTF-16などのときに誤変換してしまいます。org.postgresql.core.Encodingクラスが用意されているので、Encoding.encode()に全て置き換えましょう。

EUC-JP系DBについては、MS系Unicodeと標準Unicode、両方に変換できるようにしてみましょうか。7.3系JDBCドライバではMS系Unicode、7.2系JDBCドライバでは標準Unicodeに変換されます。

Java 2 SDK 1.4 と PostgreSQL 7.2.x JDBCドライバ

Java 2 SDK 1.4では、一部のクラスが中途半端に存在するため、PostgreSQL7.2.xのJDBCドライバをmakeすることができません。
Java 2 SDK 1.3 等では作成することができますので、一時的にjava 2 SDK 1.3をインストールして PostgreSQLのJDBCドライバをmake してみてください。
PostgreSQL7.3で、この問題は修正されています。

変更点

PostgreSQL 7.4版 7.4.2版 7.4.3版

7.3.4版で実験した結果を7.4のJDBCドライバに反映。変更点を最小にしてみた。schema等の大文字小文字を同一とみなす点とResultSet.getString()でboolean型(bit型)のデータをString型で取得するときの戻り値の修正。

PostgreSQL 7.3.3版

こちらはSun ONE Studio 4または5に対応する程度の機能を実装しようとしているものです。
日本語対応は、一応普通に利用する範囲ではできているようですが、完全ではないのかも。7.0.3までの変更は反映していません。PostgreSQL6系ドライバは、変更していません。

PostgreSQL 7.0.3 対応 JDK1.1 対応

PostgreSQL 7.0.3 が出たので、JDBCドライバも7.0.3ベースにアップ、JDK1.1ベースのドライバもEUC-JP/UNICODEエンコード対応しました。(JDK1.1での動作は未確認です)

Timestamp型対応

PostgreSQL v7.0からは、Timestamp型の精度が上がっています。
JDBCドライバのTimestamp型は、PostgreSQL 6.5.xに対応したものでしたので、7.0.xにも対応させました。
7.0.3でも修正されていないようなので、同じ修正を加えました。

新しい問題点(MS互換Unicodeと標準Unicode)

文字コードなのですが、Shift_JIS(のUnicode対応表)について2種類あることが、PostgreSQLでも影響する可能性があります。Java、その他でWindows-31J(MS互換Unicode)とShift_JISやEUC-JP(標準Unicode)では"~"などの文字位置がUnicode上で異なります。
シフトJIS全般のテキストをデータベースに入れる場合など、Unicode上で~などの文字がどちらのコードの文字になっているかを意識する必要があります。文字コードの墓場参照

PostgreSQL 7.3.x系のJDBCドライバを使う場合は、PostgreSQL上のEUC-JPの文字がWindows-31Jと互換のあるMS互換Unicode上の位置にマップされます。7.2.x以前にはShift_JIS互換文字(標準Unicode)でした。Windows上の各種入出力もWindows-31J側にあわせられているので、Javaからのファイル等への入出力にはShift_JISを使わずにWindows-31J(MS932)を使うことをおすすめします。EUC-JP、Shift_JISのファイル等に出力する場合には注意が必要です。ソフトウェア全体でどちらかに統一しましょう。WindowsならWindows-31Jでよいのですが、Linuxの場合はEUC-JPとWindows-31JのUnicode上の位置が一部異なるので難しいところです。

日本語/その他マルチバイト対応(文字コード自動判別)

PostgreSQL 7.0のJDBCドライバは、もともと日本語を使うことができますが、デフォルトエンコードでうまく動かない環境でも、PostgreSQLがマルチバイト対応になっていれば正常に使えるようになります。
他のJDBCドライバを日本語化されているところのものを使えばいいのかもしれませんが、はじめはそういうのがあるのもしらず、適当に直してしまいました。
他のドライバが、JDBCドライバ本体に大幅に手を加えているのに対し、Streamのマルチバイト対応を最初に行ったため、なんとなくすっきりした作りになっているのではないかと思っています。その他の対処クラスは、Connection や ResultSetなどです。
文字コードの判別は、DBの文字コードを見るのが前提だと思うのですが、7.0のドライバや、他の日本語化されたドライバは、JDBCの動作しているマシンの文字コードをみていたりするようです。

文字コード指定

DBがマルチバイト非対応で作られていたりした場合やDBと格納されているデータの文字コードが違ってしまっている場合などに、文字コードを強制的に指定することができます。
この方法はおすすめしませんので、詳しい方法は書きません。
EUC-JPやUNICODE、その他必要な文字コード対応ではないASCIIのみ対応のPostgreSQLを使っている場合は、再度環境を構築し直すことをおすすめします。

なぜJDBCドライバで文字コード変換をするのか?

last update 2003.01.20

Javaの内部コードはUNICODEだからです。
String にUNICODE以外の文字を入れるのは、そもそもの間違いであり、データベースからの文字は、まずUNICODEに変換してアプリケーションに渡す必要があります。逆の場合もおなじことです。
一度は全て修正したのですが、JDBCドライバの中(に限らず)、いくつか文字コードを指定しないでString型とbyte型を変換している部分がまだあるかもしれません。これは、OSのデフォルト文字コードにたよっているということなので、WindowsとLinuxなど、デフォルト文字コードが違うところで使うことを想定すると、たとえASCIIコードのみの変換でも、あってはいけないことです。もし、OSのデフォルト文字コードがUNICODE(UTF-16)などのOSがあった場合にどうなってしまうのか考えてみてください。JDBCドライバは、ネットワーク上の相手のデータベースとJavaの間で文字コード変換をするのであって、OSの文字コードは一切関係ないのです。

TomcatなどのWebアプリケーションと組み合わせる場合、WebとJavaとで文字コードがうまく変換できているかも注意が必要です。

UNICODEデータベースを使う場合の注意(2001/12/26追加)

PostgreSQLのUNICODE データベースに対してJDBCドライバを使うと、データが文字化けしてしまいます、という問いが時々あるかもしれません。
これは、JDBCドライバは間違っていません。データ自体がUNICODE以外の文字列で格納されているのです。
ということで、他の部分を疑ってみてください。
よろしくお願いしまする。

UNICODE DBに対して psql コマンドを使うときなども、telnetなどがUTF-8の文字コードになっていないと使えないです。ODBCドライバも対応していないかもしれません。emacs 経由などでUNICODE DBにアクセスする方法はあるようです。

PostgreSQL JDBCドライバの構造

ドライバの構造は、単純です。
クライアントサーバ間のデータ転送は基本的にバイナリでやりとりしており、データ取得時はドライバ内でもバイナリのまま保存されています。
ResultSetのgetString() やgetInt() を実行した時点で、元の文字コードからJavaで使用しているUNICODEに変換されます。
書き込むときも、setString() などのタイミングで変換されます。
それだけではよくある簡単な対応でできるので、可能な限りPG_Streamもマルチバイト文字対応に書き換えました。この結果、列名なども正しく処理できているはずです。
PostgreSQL JDBCドライバ内の文字は、プロトコルの性格上、1バイト文字として扱われています。これを拡張するにはUTF-8のUNICODEを使うしかありません。
そのように工夫されています。

参考資料

参考にさせていただいたり、サーチエンジンで検索していて見付けたPostgreSQL JDBCドライバの日本語化に関するページです。
承諾なしでリンクさせていただいています。

psqlODBCも各所にあるのですね。PostgreSQLユーザ会にリンクがないのでメモ

Get Thunderbird

[しいしせねっと] [日本PostgreSQLユーザ会]