Seasar2とSpringでいまいちサーバー管理のJPAの使い方が分らない

まずRESOURCE_LOCALとかnon-jta-datasourceとかがはいってるwarやejb-jarはGlassfishがデプロイ時にはじくので環境がかなり狭められてしまう。

そしてSpringやSeasar2のサポートするJPAは独自のJPA管理ということで躊躇してしまう。アプリ側のコンテナ依存はしかたないにしても、JPAやデータソースは鯖側で持っていてほしい情報だ。そこをアプリ側で持つのは我慢できない人は多いと思う。

Glassfishの管理ツールでjndi一覧を見る限りEntityManagerはjndiで取得することはできなさそう。

やはりJPAを使うならEJB3が一番だということか。GlassfishToplinkで日本語が通らない問題さえ解決すればべつにこだわる必要はないのだが。

もっともJPAを扱う場合NetBeansEJB3の開発効率は割といいので本当は日本語問題が解決されるのが一番なのだが、ここは正直期待できない部分なので回避方法を探すしかない。


シングルバイト圏とくらべてこういった部分だけでもかなりハンデだなー。


そこでJSFでは@EJBがきくことから、他のリソース系もインジェクトされるかどうか確かめてみた。

Visual Web JSFを起動して確かめる。Visual Web JSFJSFのRIを使いつつもさらに薄いラッピングをしている一種のフレームワークだ。NetBeansGUIエディタのようにボタンを貼り付けてそのイベントを書くだけでWebアプリを作ることが可能だ。

そこのイベントにコードを書いてみる。

    @PersistenceContext
    EntityManager em;

    @Resource
    UserTransaction ut;

    //ボタンクリックイベント
    public String button1_action() {
        
        try {
            ut.begin();

            Customer c = new Customer();
            c.set得意先名("なまえ");
            c.set電話番号("TEL");
            em.persist(c);

            ut.commit();
        } catch (Exception ex) {
            Logger.getLogger(Page1.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

まったく問題なく動いた…。

シンプルにアプリを書く場合悪くはないな。トランザクション管理はコンテナがやってくれるのが一番だが小さいアプリを書いたりそこまでやる必要としない場合はこれでいいかもしれない。ejb-jarが必要としないのでシンプルに扱える。

通常Visual Web JSFのロジックはセッションスコープのマネージドビーンにおいておくのが普通だ。そうなるとインジェクトしたEntityManagerの動きがどうなるかが怪しく感じるかもしれない。

そこでGlassfishのソースを調べてみた。NetBeansコンパイルの仕方が分らないのでデバッグ用のログも吐き出すことができず、エディタで開いての机上デバッグのみとなる。それでも、実際に動かしててどの用になるのか調べる必要が出てくる。

そこでリフレクションだけでひたすら追ってみた。フィールド変数までしか終えないが、getDelegate()とかを使うとトレースできないからだ。

たとえば一部を取り出したりするとこんな感じになる。

            try {

                Method method = em.getClass().getDeclaredMethod("_getDelegate");
                method.setAccessible(true);
                method.invoke(em);
                
                Field field = em.getClass().getDeclaredField("txManager");
                field.setAccessible(true);
                Object obj =  field.get(em);
                System.out.println("txManager:"+obj);
                
                Method tran = obj.getClass().getMethod("getTransaction");
                System.out.println("transaction:"+tran.invoke(obj));

            } catch (Exception ex) {
                Logger.getLogger(SessionBean1.class.getName()).log(Level.SEVERE, null, ex);
            }

Object型だけでひたすらリフレクションで追うという、あほみたいに時間がかかることをしたのは俺だけでいい。

getDelegate()やその他のメソッドは毎回生成されて初期化や後始末等されていて追えないので、_getDelegateを直接いじったりトレースしたりする必要が出てくる。


とりあえず軽く触って分ったのは、インジェクトされるEntityManagerはラッパであるということ。そしてEntityManagerをアクセスするたびに本来のEntityManagerが生成されるということ。

あとUserTransactionのcommitまたはrollbackでこれら取得した本来のEntityManagerがcloseされるようだ。

この場合EJB3でのユーザー管理のトランザクションと同じ動きなのでたぶん問題はない…はず。


ということはこのインジェクトをSpringやSeasar2コンポーネントに対しても行うことができれば手軽にアプリケーションサーバー管理のJPAを扱うことができるはず。そうすれば日本語問題も解決しなくてもなんとかなるかな。