技術・開発

【Java】log4jdbcで「setStringは32766文字未満…」エラー!CLOB登録時のAbstractMethodError回避策

2012年1月14日

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で追加された「長さ指定のない setClobsetCharacterStream」に対応していない(ラップしきれていない) ことが原因と考えられます。

解決策:長さを指定する旧メソッドを使う

解決策はシンプルです。「長さ(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のメソッドが混在する環境ではよくある落とし穴ですが、同様の現象に悩んでいる方の助けになれば幸いです。

-技術・開発
-, , , , , ,