「めざましテレビ」

今日の「早耳トレンドNo1」は臼田あさ美ちゃーん,お題は「スタミナ満点!人気のニンニク料理」.
なるほど,まこたんが書いているように,今日のあさ美ちゃんは全身が映ってませんね.いつもの小走りも見られず... 無念だ (Zaizener 再び!).
でもまぁ,顔アップもあったしまぁまぁよかったのでは.


ナレーションはやっぱりアヤパンの方がノリがいいなぁ.

Hibernate 入門記 セッションその7 削除とフラッシュ

緊急連載「Axis 入門記」に切り替えようかとも思ったのですが,やっぱりこっちを淡々と進めようということで,今回は「9.5. Deleting persistent objects」です.
永続オブジェクトをデータベースから削除するには,Session

    • void delete(Object object)

を使用します.
この削除はあくまでもデータベースからの削除なので,delete(Object) の引数に渡したオブジェクトはその後も使うことが出来ます.ただし,それはもはや永続化されたオブジェクトではなくなります.
多数の永続オブジェクトをまとめて削除したい場合もあります.そんな場合には問い合わせ文字列を指定して削除することも出来ます.それが Session

    • int delete(String query)
    • int delete(String query, Object value, Type type)
    • int delete(String query, Object[] values, Type[] types)

です.
問い合わせ文字列にはパラメータを含めることが出来ます.戻り値は削除した永続オブジェクトの数です.
永続オブジェクトを削除する際,関連の外部キーに関しては Hibernate が面倒を見てくれるようです.外部キー制約違反になることはないみたい.でも,NOT NULL 制約違反には気をつけろ.らしい.
削除については以上.ラクチンっぽい.


ということでお試しコーナー.
と思ったのですがちょっと中身なさすぎなので,「9.6. Flush」へ進みます.
セッションは,保持している永続オブジェクトの状態と RDB との同期を取るために,SQL を実行する場合があります.それがフラッシュです.
いつフラッシュが行われるかというと,次の場合らしいです.

  • Session#find()Session#iterate() が呼び出された場合.
  • Transaction#commit() が呼び出された場合.
  • Session#flush() が呼び出された場合.

ぐはぁっ,トランザクションをコミットすれば勝手にフラッシュされたのか... これまでのお試しコーナーではコミットとフラッシュを連チャンで呼び出してますがな.心より恥じる (Zaizener 再び!).
んでその SQL ですが,次の順序で発行されるとのこと.

  1. Session#save() が呼び出されたのと同じ順序で INSERT.
  2. エンティティの UPDATE.
  3. コレクション (関連) の DELETE.
  4. コレクションの要素の DELETE,UPDATE,INSERT.
  5. コレクション (関連) のINSERT.
  6. Session#delete() が呼び出されたのと同じ順序で DELETE.

このように SQL が発行されることは保証されているとのことですが,それがいつ行われるかは Session#flush() を明示的に呼び出した場合を除いて保証されないとのことです.
ですが,Session#find() などが古い状態のオブジェクトを返すことはないのだとか.
うーみゅ,Session#find() するとフラッシュされると書いてあったわけですが... つまりあれですかね,Session#find() などが対象とするエンティティについては間違いなくその時点までにフラッシュするけど,関係のないエンティティについてはフラッシュされているとは限らないということかなぁ? いいや,後でお試ししましょう.

2004/08/18 03:15 追記
ぐはぁっ,おためし忘れてました...
心より恥じる.

このようなセッションの振る舞いは変更できるようです.それには,Session

    • void setFlushMode(FlushMode flushMode)

です.
FlushMode には,次の3種類が用意されています.

NEVER
セッションは明示的に Session#flush() しないかぎりフラッシュされません.
リード・オンリーなトランザクションで効果的とのことです.
COMMIT
Session#commit() が呼び出されたときにもフラッシュされます.
AUTO
問い合わせが古い状態を返すことのないように,問い合わせの前にもフラッシュされます.
これがデフォルトのモードです.

なるほど.


ということで,今度こそお試しコーナーです.
テーブル定義,永続クラス,マッピングファイルは前回と同じ.手抜きじゃありませんよぉ〜.
なので,今回新たに用意するのは実行用のクラスのみ.

package study;
import java.util.Iterator;
import net.sf.hibernate.FlushMode;
import net.sf.hibernate.LockMode;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.Transaction;
import net.sf.hibernate.cfg.Configuration;

public class Main {
    public static void main(String[] args) {
        try {
            Configuration config = new Configuration();
            SessionFactory factory = config.configure().buildSessionFactory();

            System.out.println("starting session (NEVER)");
            Session session = factory.openSession();
            session.setFlushMode(FlushMode.NEVER);
            Transaction tx = session.beginTransaction();

            Model model = new Model("Yuri Ebihara", "CanCam");
            session.save(model);
            model.name = "Asami Usuda";

            System.out.println("*** NEVER (before commit) ***");
            Iterator it = session.find("from study.Model").iterator();
            while (it.hasNext()) {
                System.out.println(it.next());
            }

            tx.commit();

            System.out.println("*** NEVER (after commit) ***");
            it = session.find("from study.Model").iterator();
            while (it.hasNext()) {
                System.out.println(it.next());
            }

            session.flush();

            System.out.println("*** NEVER (after flush) ***");
            it = session.find("from study.Model").iterator();
            while (it.hasNext()) {
                System.out.println(it.next());
            }

            tx.commit();

            System.out.println("\nstarting session (COMMIT)");
            session = factory.openSession();
            session.setFlushMode(FlushMode.COMMIT);
            tx = session.beginTransaction();

            model.name = "Sayo Aizawa";
            model.magazine = "ViVi";
            session.update(model);

            System.out.println("*** COMMIT (before commit) ***");
            it = session.find("from study.Model").iterator();
            while (it.hasNext()) {
                System.out.println(it.next());
            }

            tx.commit();

            System.out.println("*** COMMIT (after commit) ***");
            it = session.find("from study.Model").iterator();
            while (it.hasNext()) {
                System.out.println(it.next());
            }

            System.out.println("\nstarting session (AUTO)");
            session = factory.openSession();
            session.setFlushMode(FlushMode.AUTO);
            tx = session.beginTransaction();

            session.lock(model, LockMode.READ);
            session.delete(model);

            System.out.println("*** AUTO (before commit) ***");
            it = session.find("from study.Model").iterator();
            while (it.hasNext()) {
                System.out.println(it.next());
            }

            tx.commit();
        }
        catch (Throwable e) {
            e.printStackTrace();
        }
    }
}

今回はセッションを 3 回作成しています.それぞれの FlushModeNEVERCOMMITAUTO に設定しています.
こいつを実行!!!!

starting session (NEVER)
onSave() : Yuri Ebihara
Hibernate: insert into Model (name, magazine, id) values (?, ?, null)
Hibernate: call identity()
*** NEVER (before commit) ***
Hibernate: select model0_.id as id, model0_.name as name, model0_.magazine as magazine 
           from Model model0_
Asami Usuda (CanCam)
*** NEVER (after commit) ***
Hibernate: select model0_.id as id, model0_.name as name, model0_.magazine as magazine 
           from Model model0_
Asami Usuda (CanCam)
Hibernate: update Model set name=?, magazine=? where id=?
*** NEVER (after flush) ***
Hibernate: select model0_.id as id, model0_.name as name, model0_.magazine as magazine 
           from Model model0_
Asami Usuda (CanCam)

starting session (COMMIT)
onUpdate() : Sayo Aizawa
*** COMMIT (before commit) ***
Hibernate: select model0_.id as id, model0_.name as name, model0_.magazine as magazine 
           from Model model0_
Sayo Aizawa (ViVi)
Hibernate: update Model set name=?, magazine=? where id=?
*** COMMIT (after commit) ***
Hibernate: select model0_.id as id, model0_.name as name, model0_.magazine as magazine 
           from Model model0_
Sayo Aizawa (ViVi)

starting session (AUTO)
Hibernate: select id from Model where id =?
onDelete() : Sayo Aizawa
*** AUTO (before commit) ***
Hibernate: delete from Model where id=?
Hibernate: select model0_.id as id, model0_.name as name, model0_.magazine as magazine 
           from Model model0_

むむぅ? ちょっと予定外の結果だ...
まずは NEVER のセッションですが,友里ちゃんで作った永続オブジェクトをあさ美ちゃんに更新しているのですが,実際に UPDATE されているのはコミットとフラッシュの間です.これは意図したとおり.
しかし,コミット前もコミット後も,問い合わせ結果の表示は友里ちゃんではなくてあさ美ちゃんです.なぜ??
そうかぁ,問い合わせ後に onLoad() コールバックの表示がありませんね.つまり,メモリ上にある永続オブジェクトがそのまま使われるので更新後の内容なんですね.やられた...
次の COMMIT のセッションですが,こちらではモデルを紗世ちゃんに更新しています.その UPDATE はちゃんとコミットの前に実行されています.
最後の AUTO のセッションでは,前回お試しを忘れていた Session#lock() を使っています.LockMode#READ を指定しているため,その時点で ID を確認する SELECT が実行されています.このセッションでは,その永続オブジェクトを削除しています.その DELETE は,コミットもフラッシュもしていませんが,問い合わせの直前に実行されています.そのため,問い合わせ結果には何も表示されていません.


ということで,問い合わせ結果の表示は必ずしも DB の状態と一致しないことに注意という,かなり基本的なところでびびってしまいましたが,永続オブジェクトの削除とフラッシュモードについてはちゃんと理解できたようです.ついでに前回お試しを忘れていた Session#lock() も試すことが出来ました.よしよし.
...
実は一つ,白状しなければならないことがあります.
今回,「ぐはぁっ」していませんが,実は裏でこっそし吐血しまくりました.無念だ (Zaizener 再び!)
というのも,最初トランザクションのコミットには,従来通り

        session.connection().commit();

とやっていたのです.これでも結果的には Transaction#commit() が呼ばれるだろうと勝手に思いこんでいたのです.しかし... 呼ばれないようです.(ToT)
そのため,フラッシュモードが COMMIT なのに,コミット後も SQL が発行されなくて悩みました.心より恥じる. (Zaizener 再び!)


ということで,次回は遂に「9.7. Ending a Session」です.ようやく,よぉーやっく,セッションの後始末を学習できます.残念!!!!
…イヤべつに残念でもなんでもないのですが

「H2」

香里奈が出演するドラマ,もうすぐ始まりますよー.
01:43 からですよー.
みんな準備はいいかなぁー?


02:22 追記
みんな見ましたかぁー?
先週はさっぱり分からなかったストーリー,今週のを見て少し分かってきましたが,やはり第一話を見逃したのが痛い... 無念だ (Zaizener 再び!).
でもいいんです.香里奈さえ見れれば.心より恥じる (Zaizener 再び!).
その香里奈ですが,今週の顔アップのシーン,めちゃくちゃきれいでしたよ.このシーンだけ保存版ですね♪
っていうか,第一話が見たいので DVD 出してくれないかなぁ.出るわけないか...
残念!!!!

S2Axis 開発記 準備その1 WSDD と AdminService

なんでも○○記ってすればいいもんじゃないと思いつつ,っていうか「その1」っていつまで準備で引っ張るつもりなんだろうと自分で突っ込みつつ,とりあえず書いてみるテスト.


まず S2Axis とはなんぞや? それは,サーバサイドでは S2 のコンポーネントを Web サービスとして利用できるようにするものである.クライアントサイドでは,Web サービスを S2 のコンポーネントとして利用できるようにするものである.たぶん.
ということで,いずれはクライアントサイド・サーバサイドとも実現したいと思っていますが,まずはサーバサイドが欲しいというのが言い出しっぺのはぶさん達の要望でした.どっちもいらなくなったみたいだけど.(^^;


それでそのサーバサイドですが,Java のクラスをWebサービスとして公開するには,WSDD (Web Service Deploymen Descripter) というものを記述しなければならないようです.しかしそんな面倒なことはしたくない!
そこで S2Axis がよきに計らいましょう,ということらしいです.
そこのところについて,まこたんが日記でヒントを書いてくれているのですが...

S2AxisってAOPでリモート呼び出しも組み込まなきゃね。
AdminServiceっていうWebサービスを直接呼び出したら組み込みとか自由に出来るんだ〜へ〜〜そしたらWSDD書かなくて良いと
http://ws.apache.org/~toshi/jp-site/axis/java/user-guide.html

うーん,相変わらず謎めき.(^^;
URL で示されているドキュメントによると,自前のクラスを Web サービスとして公開する際には,AdminClient というコマンドラインから実行できるクラスを使うことになっています.その引数で WSDD を指定します.
この AdminClient というのは,Web サービスのクライアントとして実装されています.つまり,Axis の管理機能自体もまた Web サービスなんですね.
そこで,その Web サービスを直接呼び出せば WSDD いらないじゃん! ってことなのかな?
でもねー,AdminClient を見ると,WSDD をそのまま AdminService に渡してるんだよねぇ (message サービスとかいうらしい),だから WSDD を生成して AdminClient 呼び出すのとたいして変わらないんだよねぇ,ってことはさておき,問題はその AdminService のクライアントってどこで誰が動かすつもりなの? ってこと.


自分が最初にこの S2Axis の話を聞いたときにイメージしたのは,Axis と S2 (コンテナ&コンポーネント) を組み込んだ Web アプリを Tomcat なんかにデプロイすると,S2ContainerServlet の初期化の際に S2Axis によってコンポーネントが Axis に登録されるというもの.
でも,まこたんは Web アプリの外からコンポーネントを Axis に登録しようと考えていた? それはなぜ? どういうこと?
まこたんの構想について解説希望.おいらに理解できるように.(^^;