Javaのバッチ処理開発において、SQLのログ出力を手軽に行える「log4jdbc」は非常に便利です。
しかし、OracleデータベースのCLOBカラムに対して、長大な文字列を登録しようとした際に少し厄介なエラーに遭遇したので、その解決策をメモしておきます。
発生した問題:setStringの文字数制限
PreparedStatementを使用して、OracleのCLOBカラムに3万文字を超える長い文字列を setString で登録しようとしたところ、以下のSQLExceptionが発生しました。
java.sql.SQLException: setStringでは、32766文字未満の文字列のみ処理できますOracleのJDBCドライバの仕様上、setString メソッドで扱える文字数には制限(約32kバイト)があるようです。
陥りやすい罠:AbstractMethodError
「setStringがダメなら、setClobやCharacterStreamを使えばいい」と考えるのが一般的です。 そこで、以下のようにコードを修正しました。
// 修正案1:setClobを使用(JDK 1.6以降)
Reader rd = new StringReader(LONG_STRING_DATA);
pstmt.setClob(1, rd);
// または
// 修正案2:setCharacterStreamを使用(長さ指定なし)
pstmt.setCharacterStream(1, rd);しかし、これを log4jdbc を経由している環境で実行すると、今度は以下のエラーが発生してしまいます。
Exception in thread "main" java.lang.AbstractMethodError: oracle.jdbc.driver.T4CPreparedStatement.setClob(ILjava/io/Reader;)V
at net.sf.log4jdbc.PreparedStatementSpy.setClob(PreparedStatementSpy.java:908)原因:log4jdbcが新しいメソッドに対応していない?
java.lang.AbstractMethodError は、呼び出そうとしたメソッドの実装が存在しない場合に発生します。 log4jdbcは、本来のJDBCドライバ(Oracle Driver)をラップ(包み込む)して動作しますが、使用しているlog4jdbcのバージョンが古く、JDK 1.6で追加された「長さ指定のない setClob や setCharacterStream」に対応していない(ラップしきれていない) ことが原因と考えられます。
解決策:長さを指定する旧メソッドを使う
解決策はシンプルです。「長さ(length)」を指定するタイプの、古いバージョンのメソッドを使用することです。
以下のコードであれば、log4jdbc経由でも問題なくCLOBへの登録が成功します。
// 【成功】第3引数に文字数(longまたはint)を指定する
Reader rd = new StringReader(LONG_STRING_DATA);
pstmt.setCharacterStream(1, rd, LONG_STRING_DATA.length());まとめ
- Oracleの
setStringは32766文字制限がある。 log4jdbcを使っている場合、JDK 1.6以降の「長さ指定なしメソッド」を使うとAbstractMethodErrorになる場合がある。- その場合は、明示的に長さを指定する
setCharacterStreamを使用する。
古いライブラリと新しいJDKのメソッドが混在する環境ではよくある落とし穴ですが、同様の現象に悩んでいる方の助けになれば幸いです。