”[Oracle] JDBCでバインドできる文字列のサイズの制限
”で触れたように、Oracleでは、PreparedStatementクラスのsetString()メソッドを使用してバインドできる文字列のサイズに制限があり、その制限を回避するにはsetString()ではなく、setCharacterStream()を使用して値をバインドする必要があります。
ただ、実際にsetCharacterStream()を使うとsetString()の制限を超えるような文字列でも正常にバインドできることは確認できるのですが、特定のケースにおいて別の問題が発生することもわかりました。
1つのPreparedStatement(またはCallableStatement)にsetString()の制限を超えるバインド変数が複数含まれているときに、その複数のバインド変数に対してsetString()の制限を超える文字列をバインドすると、バインドした文字列が別の列にバインドされてしまう、つまりすり替えられてしまうという問題が発生するのです。
具体的な例をあげると、
CREATE TABLE TABLE1 ( STR1 VARCHAR2(2000), STR2 VARCHAR2(2000), STR3 VARCHAR2(2000) );
と定義されたテーブルに対し、
PreparedStatement pstmt = con.prepareStatement( "INSERT INTO TABLE1 (STR1,STR2,STR3) VALUES (?,?,?)" );
というPreparedStatementを生成し、この各バインド変数に対して
int p=1; String value1 = new String("あいうえおかき..."); String value2 = new String("アイウエオカキ..."); String value3 = new String("ABCDEFG..."); pstmt.setCharacterStream( p++, new StringReader( value1 ), value1.length() ); pstmt.setCharacterStream( p++, new StringReader( value2 ), value2.length() ); pstmt.setCharacterStream( p++, new StringReader( value3 ), value3.length() );
とバインドして
pstmt.execute();
として実行すると、
STR1 STR2 STR3 -------------------- -------------------- -------------------- あいうえおかき... アイウエオカキ... ABCDEFG...
と登録されるはずですが、バインドする文字列が日本語文字で666文字を超えている場合、
STR1 STR2 STR3 -------------------- -------------------- -------------------- ABCDEFG... あいうえおかき... アイウエオカキ...
というように複数の列間でバインドした文字列がすり替えられてしまうのです。
しかも、666文字を超える日本語文字をバインドしたバインド変数の組み合わせによってすり替わり方が異なります。
どうもバグっぽいんですが、これを避けるにはsetString()の制限を超える文字列のバインドは1つのステートメントにつき1つだけにしておくのがよいようです。
そのうちパッチか何かで解消されるのでしょうか...
(ちなみにPSR9.2.0.6.0では解消されていませんでした。)
【関連エントリ】
[Oracle] JDBCでバインドできる文字列のサイズの制限 2004/12/09
[Oracle] バインドした文字列がすり替えられる??? 2004/12/28