Oracle JDBCドライバでLONG列にアクセスする際の注意点

LONG列なんかはもう非推奨で普通はBLOB使うからほとんど問題にはならない内容なのですが。

Oracleデータディクショナリテーブルの一つALL_TAB_COLUMNS(USER_TAB_COLUMNS)DATA_DEFAULT列はなんとLONG型で、これにアクセスするときにこの問題にはまって2時間ほど損失したのでメモしておく。

Oracle JDBCドライバでLONG列を含むテーブルにアクセスする場合は、ResultSetからデータを取得するときLONG列を一番最初に取得しなければならない」

な ん だ そ りゃ

具体的には以下のような現象。

final String SQL_COLUMNINFO =
  "SELECT " +
    "col.column_name COLUMN_NAME, " +
    "col.data_default DATA_DEFAULT, " + //こいつがLONG列
    "co.comments COMMENTS " +
  "FROM " +
    "user_tab_columns col " +
  "WHERE " +
    "col.table_name = 'HOGE' " +
  "ORDER BY " +
    "col.table_name, " +
    "col.column_id ";
Statement stmt = null;
ResultSet rs = null;
try {
  stmt = conn.createStatement();
  rs = stmt.executeQuery(SQL_COLUMNINFO);
  while(rs.next()) {
    String columnName = rs.getString("COLUMN_NAME");
    String comments = rs.getString("COMMENTS");
    String dataDefault = rs.getString("DATA_DEFAULT"); //ここで例外発生
    System.out.println(ColumnName + ':' + comments + ':' + dataDefault);
  }
} finally {
  try { if(rs!=null) rs.close(); } catch (SQLException e) {}
  try { if(ps!=null) ps.close(); } catch (SQLException e) {}
}

上記のような記述だと、getString("DATA_DEFAULT") とした時点で「ORA-17027 ストリームはすでにクローズ済です」が発生する

ここは、以下のようにすると問題が回避できた

final String SQL_COLUMNINFO =
  "SELECT " +
    "col.column_name COLUMN_NAME, " +
    "col.data_default DATA_DEFAULT, " + //こいつがLONG列
    "co.comments COMMENTS " +
  "FROM " +
    "user_tab_columns col " +
  "WHERE " +
    "col.table_name = 'HOGE' " +
  "ORDER BY " +
    "col.table_name, " +
    "col.column_id ";
Statement stmt = null;
ResultSet rs = null;
try {
  stmt = conn.createStatement();
  rs = stmt.executeQuery(SQL_COLUMNINFO);
  while(rs.next()) {
    String dataDefault = rs.getString("DATA_DEFAULT"); //最初に読む
    String columnName = rs.getString("COLUMN_NAME");
    String comments = rs.getString("COMMENTS");
    System.out.println(ColumnName + ':' + comments + ':' + dataDefault);
  }
} finally {
  try { if(rs!=null) rs.close(); } catch (SQLException e) {}
  try { if(ps!=null) ps.close(); } catch (SQLException e) {}
}

Oracle Database JDBC開発者ガイドおよびリファレンスにはたしかに「LONG列に後でアクセスしようとしても、データは使用できず、ドライバは「ストリームがクローズされています。」エラーを戻します。」と書いてあるけど。

気づかないよこんなの。

このOracle JDBCドライバのLONG列に対する制限って、Oracle+Javaな界隈では常識レベルの有名な話だったりするのかなあ?ググっても全然出てこなかったよ…

2010/11/13 Javaのコードに一部誤りがあったので修正しました。また、文章の一部を修正しました