隠された例外(その3)
2008/08/14 xx:xx:xx oracle.jdbc.driver.OracleStatement [doExecuteWithTimeout] SQL: INSERT INTO ADDBOOK ORDER BY FULLNAME (FULLNAME, ADDRESS, EMAIL, MARRIED, GENDER, UPDDATE) VALUES (?, ?, ?, ?, ?, ?)
2008/08/14 xx:xx:xx oracle.jdbc.driver.T4CTTIoer [processError] Throwing SQLException: ORA-00926: VALUESキーワードがありません。
コメント頂いた直後はCachedRowSetWriterのバグと決めつけていたのだが、ふと思い出してXMLにシリアライズしたWebRowSetのプロパティ部を見てみた。
: 以下略 : SELECT * FROM ADDBOOK ORDER BY FULLNAME 1008 MyDataSource :ADDBOOK ORDER BY FULLNAME :
CachedRowSetWriterは同期時(acceptChanges)に内部でSQLコマンドを合成するが、その時のテーブル名にWebRowSet#getTableName()の戻り値を使っている。この値がそのまま使われていると考えて間違いなさそうだ。
それにしてもどうしてテーブル名に"ORDER BY"が付加されてしまっているのだろう。自前のシリアライズザクラス(RowSetXmlWriter実装)を確認してみたが、こんなテーブル名はセットしていないぞ。
逆アセンブルしていないのでここからは推測になるが、WebRowSetの実装(WebRowSetImpl extends CachedRowSetImpl)ではSQLコマンドが設定されると(#setCommand)、ご丁寧にもSQLからテーブル名を類推して、セットしているのではないだろうか。恐らくその類推の程度は最悪で、単に"SELECT * FROM"の後をテーブル名と決めうちしていると思われる。これは酷い。
この問題の回避方法だが、
- setCommandで設定するSQLコマンドをCachedRowSetImplが正しく解釈できる記述だけに限定する
具体的には、SetCommandメソッドでセットする値にはテーブル名の後に何も書かないことになるが、これはちょっと現実的ではない。
- WebRowSetImplを継承したクラスで、setTableNameメソッドをオーパライドして、正しいテーブル名だけがセットされるようにする
こんな感じで先頭のテーブル名と思われるワード以外は全て切り捨てる(トークンの区切りは空白であることを前提にしている、ベタだがご容赦)
@Override public void setTableName(String tableName) throws SQLException { String[] tokens = tableName.split("[\\s]"); if ( tokens != null && tokens.length > 1 ) { super.setTableName(tokens[0]); } else { super.setTableName(tableName); } }
うーん、格好悪いなぁ。