tanigonのS2部屋

メイン日記はこちら(tanigonの日記)

2004-11-24

あっちにも書いたけども

メインのはてなダイアリのエントリを受けて...

実務的には遭遇しそうなトラブルではあるがどう考えてもOracleドライバ責任…なんだよなあ。しかし、この「責任」の発想に固執するとたとえば「~」文字が化けちゃうOracleのShift-JISマッピングの問題とかもその通りなわけで、いかに解決するか、のみを考えるのが真に実務的なのかもしれない。

S2JDBCでResultSetのハンドラを自分で実装してやって ClobとかならStringに変換してやればいいのかしらん...?

StringType#getValue()で

return resultSet.getString(columnName);

がCLOB列に対して呼ばれている。ふむ、つまりOracleのJDBCドライバ(Thin)は CLOB列に対してgetStringするとnullを返してるわけだな。このへんの仕様ドライバ依存だしなあ... CLOB列に対してgetString()したときにStringに変換するあたりの処理は入れてくれてもよさそうなもんなんだけど…といってても仕方ないので…どうなんだろう。DTOをsetObject()になるように変えてしまうとか...? むぐぐ

seasar-usersでLOBについて書いてあったような気がするけどこの件とは似ているが CLOBはStringにできますよね、というところがポイントなので少し違うか (MLのほうはBLOBだった気がする)

急場しのぎ

public static final String hoge_COLUMN = "HOGE";
  を
public static final String hogeAsClob_COLUMN = "HOGE";

にする。で、

    private String hoge;
    
    public void setHoge(String a) { ... }
    public void setHogeAsClob(Clob clob) throws SQLException {
        this.setHoge(clob.getSubString(1L, (int)clob.length()));
    }

とする。これで set名前As型(型 a); という形式でDAOにアクセスしてもらえるので、引数型を見たときにClobなので getObject()してコールしてもらえる、というわけです。

急場しのぎというか結構アリな気もしてきた。こういう解決策は全然ありかも?? (インピーダンスミスマッチに近い問題の気がする)

追っかけてないけど insert文自動生成のときにヤバいかもしれない... うちはいまinsert文はSQLファイルになっているので...うーむ?

と思ったらinsert updateで大コケ

OracleのJDBCドライバ

 PreparedStatement#setString(1, "ながーーーい文字列");

とかすると、

java.sql.SQLException: データ・サイズがこの型の最大サイズを超えています。: 145000

とか出るんですよ。うう。OracleSQLでCLOB型の列 hoge に対してinsertするときは

  insert into tablename(hoge) values('文字')

でいけるんですよ。暗黙の型変換。で、短いときは JDBCからでも #setString() でもいけるんです。ところが長くなると…

これは暗黙のうちにVARCHAR2(最大4000)の制約を受けてるのか...

だとしたら、Clob格納はOracleオススメのClobロケータ取得後のAsciiOutputStreamに対してwriteするという方法…か。つまり一度Selectしないとダメか。ぐはぁーーー!!

insert,updateでのさらなるドハマリ

仕方ない、interfaceS2Daoを使うんじゃなくて abstractclassS2Daoをカマせよう... abstractclassではCLOBがらみのinsert,update系だけ実装してあとはS2DaoInterceptorがやってくれる…

ということでやりました。abstractクラスに DataSourceをinjectionしてもらい、.diconに arg とか書き込んで初期化してもらいます。

んでupdate,insertでは

  • BasicSelectHandlerとか使って SELECT CLOB列 FROM ... WHERE ... FOR UPDATEなSQL文を投げる(Oracle様ご推奨...(皮肉))
  • 帰ってくる値はLOBロケータなのでこれに作業を行う

と、ここまでは良かった。ところがですよ、ふつーに

Writer stream = clob.setCharacterStream(1);
stream.write(song.getLooooooongText());
stream.flush();
stream.close();

なんてすると、Oracle様は「サポートされていない機能です」なんて言うんですよ。えっ、でもjava.sql.Clobって他に書き込みする方法ないんじゃない? なにしろマニュアルには

重要: JDBC 2.0 の仕様では、PreparedStatement のメソッド
setBinaryStream() およびsetObject() を使用して、ストリーム値
をBLOB として入力でき、また、PreparedStatement のメソッド
setAsciiStream()、setUnicodeStream()、
setCharacterStream() およびsetObject() を使用して、ストリーム
値をCLOB として入力できると規定されています。これにより、LOB ロ
ケータを通さず、直接LOB データ自体にアクセスできます。
Oracle JDBC ドライバの実装では、この機能はリリース8.1.6 以上のデータ
ベースおよび8.1.6 以上のJDBC OCI ドライバを使用した構成でのみサ
ポートされます。その他の構成では、この機能を使用しないでください。
データが破損する可能性があります。

なんて書いてあるんですよ。Thinドライバだよ、こっちは(泣)

で、Oracle様がどうしろ、といってるかというと、

ということなんだそうです。それでイケるならなんでラッパーしてくれないの...(ぶちぶち)

結局どうなるかというと、

            Writer stream = null;
            if (clob instanceof oracle.sql.CLOB) {
                stream = ((CLOB)clob).getCharacterOutputStream();
            } else {
                stream = clob.setCharacterStream(1);
            }
            stream.write(song.getLyricText());
            stream.flush();
            stream.close();

オーノー。

S2Daoの恩恵にあずかれない、コードは生書き、しかもメチャメチャ油っこい…なにかいい方法はないものだろうか...

それとも根本的に大きな間違いをしているのだろうか? 私はどうもOracleが好き勝手やってるようにしか見えないのだが...

S2NazoWeb

NanoWeb万歳の私としては期待大。これ、そういうことですよね? > takaiさん

厳格さ、自由度、プロセスの性質、とかなんとかは基本的に対象物の性質に依存すると思うので、NanoWebのほうが目的を簡単に達成できるケースもあれば、StrutsだとかJSFとかのほうが目的を達成できるケースもあると思います。個人的には前者の状況に対して重いフレームワークとかXML地獄を適用してしまうケースのほうがイタイ目にあっているので、超軽量なんでもありフレームワーク(ただし負荷には弱いし、ちゃんとしたシステムを作る上では生産物の整合性や一貫性を取るのが大変)に期待してしまうんです。

自分で作れよって自分に言い聞かせてたのですが、nanoWebの移植してたらNazoWebを見つけてしまったのですよ...

higayasuohigayasuo2004/11/24 20:57オラクル側は、BLOBにして、ダミーのプロパティを追加し、byte[]とStringの相互変換をする。

tanigontanigon2004/11/24 20:59なるほど... BLOBに対する byte[]のサポートはありますもんね。byte[]とStringの変換はjavaのレイヤでするわけですね。キャラクタセットとかの扱いも考えないといけないのか...