Archive for the 'Hibernate' Category

WicketとSpringとJPAを使ったAjaxなCRUDサンプルのソースとか

を、公開します。

ソースとかっていうか、EclipseのプロジェクトをそのままZipしただけなんで、
展開すればそのままEclipseにImportできます。
ただし、WTP使ってるんで、WTPプラグインは入れてるEclipseがいいでしょう。
APサーバは、僕はJettyを使いましたが、別に他のAPサーバでも大丈夫(なはず)です。
Jettyを使う方はWTPのJettyプラグインを入れ、それ以外のAPサーバを使う方は各自ご自由に設定ください。

また、現状では、PostgreSQL8.xを使うようになってます。
おそらく他のRDBでも大丈夫ですが、その場合は適宜JDBCドライバライブラリを入れてください。
misc/sql/createTable.sqlを流してもらうと、今回使うテーブルを作成します。

ソースの解説とかは、JavaDoc見れば分かるレベルだと思うので特にするつもりもないです。
不明点とかあれば、コメントくださいませ。

あー、あと配布物の中の僕自身のオリジナルのコード等はPublic Domainってことにします。
つまり、著作権は主張しませんのでお好きいじって使ってください。

ダウンロードはこちらから(9.0Mぐらい)。

#ってか、下で公開してたサンプルに、「ひがやすお」というエントリがあったんだけど、
#ご本人なのだろうか・・・。

WicketとSpringとJPAを使ったAjaxなCRUDサンプル

うーん、前回から思った以上に時間が開いてしまいました。

とりあえず普通にCRUDするだけのサンプルにしようと思ったのですが、
それだけだと面白みに欠けるので、WicketにビルトインされてるAjaxを使ったものも
一緒に作ってみました。

プレゼン層:Wicket 1.2.3
DIコンテナ:Spring 2.0.1
JPA:Hibernate 3.2.1
DB:Postgres 8.1.4
APサーバ:Jetty 6.0.1
OS:Ubuntu Linux 6.10
という構成です。

サンプルは自宅サーバにて公開してます。
http://server.mesolabs.com/WicketSpring/

このサンプルのソースとEclipseプロジェクトも近日中に公開いたします。

それでは今回は時間ないのでこのへんで。

WicketとSpringの連携法

さて、前回のエントリで予告した通り、今回はWicketとSpringとHibernateの連携について書いていきます。

ただし、SpringとHibernate(というかJPA)の連携に関してはWicket独自の部分っていうのはないので、このエントリではWicketとSpringの連携法について説明します。SpringとHibernate(JPA)の連携に関しては他のサイトの記事を参照してください。
また、DBを使わないWicketアプリケーションの作成法についても、初歩的なこと(ここで解説している程度のこと)を理解していることを前提としています。

まず、WicketとSpringの連携における問題点についてざっと説明をし、その解決策を御紹介します。

WicketとSpringの連携における問題点

WicketとSpringの連携においてはいくつかの問題があります。それはWicketのもつ以下の二つの特性が原因としてあげられます。

  1. Wicketは管理されていないフレームワーク(unmanaged framework)である
  2. Wicketのコンポーネントとモデルはシリアライズされることがある

それぞれについて簡単に説明します。

1.Wicketは管理されていないフレームワークである

Wicketは、コンポーネントのライフサイクルを管理しておりません。つまり、ページやコンポーネントはコード中のどこでも単純にnewするだけで作ることができます。なので、コンポーネントの作成時に依存性を注入することが難しいのです。解決法としては依存性を注入するsingleton factoryを用意し、コンポーネントを作成するときにはそれを使って依存性を注入するという方法があります。が、この方法だと、コンポーネントを作成するときには必ずデフォルトコンストラクタによって作られ、その後必要な変数をセットするという形になるため、記述がめんどくさくなってしまいます。

2.Wicketのコンポーネントとモデルはシリアライズされることがある

Wicketは、コンポーネントツリーをセッション中に保存します。クラスタ構成環境では、セッション情報は他のクラスタに複製される必要があります。これはあるクラスタノードでオブジェクトをシリアライズし、他のクラスタノードでデシリアライズされることで実現されます。この際、依存性までシリアライズされるとは期待できないことが、依存性の注入における問題になってしまいます。

解決策

これらの問題を回避し、WicketとSpringを連携させる方法は以下の3種類あります。

  1. Application Object Approach
  2. Proxy-based Approach
  3. Annotation-based Approach

それぞれについて簡単に説明します。

1.Application Object Approach

WicketアプリケーションはApplicationクラスのサブクラスであるグローバルapplicationオブジェクトをただ一つ持っています。このグローバルapplicationオブジェクトは、ただ一度だけ作られ、(ユーザ固有情報を保持していない限り)シリアライズされることはありません。この特性によって、applicationオブジェクトは残りのアプリケーションに対するサービスロケータとして働かせることができます。applicationオブジェクトを作る際には、自分好みのfactoryクラスを用意することができます。wicket-contrib-springプロジェクトではそうしたfactoryクラス(SpringWebApplicationFactory)を提供しております。このクラスは、applicationオブジェクトのインスタンスを作成するかわりに、Springのアプリケーションコンテキストから持ってきます。Wicketはthreadlocal変数にapplicationオブジェクトのインスタンスを保持し、コンポーネント中からそのインスタンスを得るためのメソッドを提供するので、Wicketコンポーネント中の依存性を受け取ることが簡単にできるのです。
実際のコンフィグファイルとコードは以下のようになります。
web.xml:

...
<servlet>
    <servlet-name>wicket</servlet-name>
    <servlet-class>wicket.protocol.http.WicketServlet</servlet-class>

    <init-param>
        <param-name>applicationFactoryClassName</param-name>
        <param-value>wicket.spring.SpringWebApplicationFactory</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
...
<!-- 以下はSpringWebApplicationFactoryが利用するSpringアプリケーションコンテキストの設定例-->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
...

applicationContext.xml:

...
<!-- setup wicket application -->
<bean id="wicketApplication" class="project.MyApplication">
    <property name="contactDao" ref="contactDao"/>
</bean>
...

code:

class MyApplication extends WebApplication {
   private ContactDao dao;
   public void setContactDao(ContactDao dao) { this.dao=dao; }
   public ContactDao getContactDao() { return dao; }
}

class BasePage extends WebPage {
    ContactDao getContactDao() {
        return ((MyApplication)getApplication()).getContactDao();
}

class EditContact extends BasePage {
    public EditContact(long id) {
        Form form=new Form("form",...) {
           public void onSubmit() {
               Contact contact=getContact();
               ...
               getContactDao().save(contact); // < ================
               ...
            }
        }
    }
}

良い点:

  1. シンプルである
  2. コンポーネントに依存性を保持しないため、シリアライズ問題を避けることができる

悪い点:

  1. アプリケーションが多くの依存性を保持するようになると、applicationクラスはクラスタ構成しするほうが良い
  2. 偶然にも依存性の参照を保持してしまうことが起こりやすいので、それゆえシリアライズ問題が発生するかもしれない

2.Proxy-based Approach

シリアライズ/デシリアライズを安全に行える、依存性のためのダイナミックプロキシを作成することもできます。プロキシは必要に応じて依存性を捜し出すための必要な情報を含める必要があり、またセッションのサイズに影響を与えすぎない程度に小さい必要があります。このプロキシは、以後LazyInitProxyと表現します。この手法は、シリアライズ問題を解決しますが、依存性注入問題は解決しません。のちほど依存性注入問題をどうするかを示します。wicket-contrib-springプロジェクトは、必要なプロキシを作成するためのツールを提供しています。
LazyInitProxyはその他のプロキシと同様とてもシンプルです。invocation handlerは以下のようになります。

// this is only an example implementation
class LazyInitProxy implements InvocationHandler {

    // this is the cache for the dependency we are going to lookup on first access
    // notice this is declared as transient so that it will not be serialized with the proxy
    private transient target;

    public Object invocationHandler(Object proxy, Method method, Object[] args) {
        if (target==null) {
            target=lookupTarget();
        }
        return method.invoke(target, args);
    }
}

このプロキシが持つ必要があるただ一つの情報は、lookupTarget()メソッドです。このために、wicket-contrib-springプロジェクトはIProxyTargetLocatorを提供します。このインタフェースはとてもシンプルです。

public interface IProxyTargetLocator extends Serializable
{
	Object locateProxyTarget();
}

LazyInitProxyFactoryは、プロキシターゲットロケーターとプロキシターゲットクラスからLazyInitProxyを作ることができるfactoryクラスです。もしクラスがインタフェースなら、factoryはダイナミックJDKプロキシを作成し、そうでなければ、cglibプロキシを作成します。
例:
applicationContext.xml:

<!-- setup wicket application -->
<bean id="wicketApplication" class="project.MyApplication"/>

code:

class MyApplication extends SpringWebApplication {
}

class EditContact extends WebPage {
   private ContactDao dao=LazyInitProxyFactory.createProxy(ContractDao.class,
      new IProxyTargetLocator() {
         public Object locateProxyTarget() {
            return ((MyApplication)Application.get()).getSpringContext().getBean("contactDao");
         }
      }
   }
}

ここで、applicationクラスはwicket-contrib-springプロジェクトによって提供されているSpringWebApplicationクラスを継承しております。このWebApplicationクラスのサブクラスは、Springコンテキストを取得するための簡単な方法を提供するために、SpringのApplicationContextAwareインタフェースを実装しなくてはなりません。
問題は、その冗長な性質です。各々の依存性について、プロキシとオブジェクトロケーターをを作成しなければなりません。簡単にするために、wicket-contrib-springプロジェクトはInjectorクラスを提供しています。このクラスはIFieldValueFactoryインタフェースの実装を使うことで、自動的にアプリケーションオブジェクトに依存性を注入することができます。Injector.inject(Object object, IFieldValueFactory fieldValueLocator)メソッドは、引数のオブジェクトの全てのフィールドの値を、IFieldValueFactory.getFieldValue(Field field, Object fieldOwner)メソッドから得られる値にアサインします。このクラスを使うことで、フィールドと関連するいくつかのメタデータを使うプロキシを作ることができるIFieldValueFactoryを実装することができます。wicket-contrib-spring-jdk5プロジェクトが提供するAnnotProxyFieldValueFactoryクラスではSpringBeans JDK5 アノテーションを使って実装しています。
例:

class EditContact extends WebPage {
   @SpringBean
   private ContactDao dao;

   @SpringBean(name="userDao")
   private UserDao userDao;

   protected EditContact(long userId) {
       ...
   }

   public static EditContact create(long userId) {
       EditContact c=new EditContact(userId);
       Injector.inject(c, new AnnotProxyFieldValueFactory(Application.get()));
   }
...

上例では、ページを作成しその依存性を注入するfactoryメソッドを作っています。これは実際のIoCなしで、IoCの利点をほとんど持っています。

3.Annotation-based Approach

アノテーションで表現された依存性は、生成時に自動的に注入することができます。そのためには、SpringComponentInjectorをインストールする必要があります。
例:

class MyAppliaction extends WebApplication {
    public void init() {
        addComponentInstantiationListener(new SpringComponentInjector(this));
    }
}

class EditContact extends WebPage {
   @SpringBean
   private ContactDao dao;

   @SpringBean(name="userDao")
   private UserDao userDao;

   public EditContact(long userId) {
       ...
   }
}

この例では、ページ(もしくはWicketコンポーネントから生成されたものなら何でも)は生成時にその依存性を注入されます。[getApplication().notifyComponentInstantiationListeners(this);の呼び出しがあれば、スーパークラスのコンストラクタはComponent(final String id, final IModel model)コンストラクタに至るまでチェーンされます。]
この手法を用いるとき、例えば、private ContactDao dao=null;のように、依存性をnullやその他の値に初期化しないことを覚えておくことが重要です。注入は、サブクラスがフィールドを初期化する前に行われるため、dao=nullは作られたプロキシをnullで上書きしてしまいます。
wicket-contrib-spring-annotプロジェクトは、SpringComponentInjectorクラスを提供します。上記のようにアプリケーションの中でinjectorをインストールするだけで注入することができます。SpringComponentInjectorはまた、Wicket portlet アプリケーション中でも自動注入をサポートします。

結論

さて、もういかにも直訳っぽい文章が多くてお気付きだとは思いますが、上記の文章はこちらの翻訳です。

僕が最初にこれらの3つの解決策を見たときに、一番簡単そうに見えたのは3番目のアノテーションを使う手法です。なので、僕は3番めのアノテーションを使う手法で今後開発を行っていこうと思います。

次回は、実際にWicket-Spring-Hibernateを利用した例(たぶんDB内容をCRUDするようなもの)を紹介したいと思います。

それではまた。

Webアプリ作成前に考えたこと

僕が今回、Webアプリを作ろうと思ったときに、考えたことをまとめてみます。

  1. 何を作るのか。
  2. どのように公開するのか。
  3. 動作OSは何にするのか。
  4. 作成言語は何にするのか。
  5. 使用フレームワーク・手法・ツールは何にするのか。

まず1番目。何を作るのか。

これが決まらないと、どうしようもないですよね。まあ、漠然となんでもいいから作りたいってこともあるかもしれませんが。
今回僕は、ある特定の分野に特化したSNS+Blogのようなシステムを作ろうと考えております。

次に、公開方法をどうするのか。

とりあえず、自宅サーバで運用していこうと思います。もし万一、自宅サーバじゃおっつかなくなったら、レンタルサーバとかに移行するかもしれませんが。

そして、動作OS。

自宅サーバでいく限り、Linuxが基本になります。ずっとCentOS使ってたけど、Solaris 10(とBelenix)のインストールにチャレンジして失敗して以来、またCentOSに戻してずるずる使ってました。

でも、最近人気のUbuntuが気になって、6.10が出たので入れてみたらこれが想像以上にいい!ってことで、Ubuntu Linuxでいこうと思っています。

それから、言語。

いろいろ悩みましたが、まずLinuxってことで.NETは却下(Mono使えばいいかもしれませんが)。Webアプリ作成に使える言語でなれているのはJavaかRubyであり、Ruby On Railsも使いたいけど今回は仕事のための技術習得もかねてJavaでいくことに決めました。

最後に使用フレームワーク等。

RubyならRailsで決定だったんですが、Javaの場合いろいろ多すぎて相当悩みました。とりあえず、Ajaxはふんだんに採り入れたい、最新の技術を使っていきたいってことで、以下のような候補をあげました。

とりあえず、Ajax層・プレゼン層・ビジネスロジック層・パーシステンス層・フルスタックなものに分けて書いてみましたが、これの組合せで色々考えました。

まず、プレゼン層。

今後の標準を考えればJSFなんだろうけど、あまりJSF好きじゃないんでその他を考えてたところ、Ajaxとの親和性からEcho 2が、作成面のやりやすさからWicketが候補としてあがりました。Echo 2はほんとに素晴らしいフレームワークで、こちらのDemoみてもわかるとおりAjaxに特化したフレームワークです。最初はEcho 2で行っちゃおうかと思ったんだけど、そのころWicketがApache Software Foundation入りするって話題が入って、Wicketも大好きなフレームワークだったから調べ直したら、こちらもAjaxとの親和性が相当高くなってきてることを再確認しました。両方とも試しにプログラム組んでみたりしたところ、Wicketの方がEcho 2よりもプログラマに残されている領域が広い(逆にいうとEcho 2はWicketよりフレームワーク主導な部分が多い)と感じ、Wicketで行くことに決めました。そして、AjaxはWicketに組込みのものを使っていこうと思います。

次にビジネスロジック層。

プレゼン層でJSFに決まっていれば、EJB 3.0かSeasar 2.4の一騎打ちだったのですが、WicketになったのでWicketとの親和性を考えてSpring 2.0に決めました。また、WicketにはDataBinderという素晴らしいフレームワークがあり、これを使えばHibernateとの連携がとても簡単にできるとのことだったのですが、DataBinderの開発が実質一人で行われていること、Maven 2主導でいろんなライブラリとか勝手に使われたりするとこが気に食わなくて、使うのは遠慮しときました。

んで、パーシステンス層。

Wicket+Springとくれば、ここはHibernateに迷いなく決めました。ほんとはdb4oとかのオブジェクトデータベースとかも使って見たかったんだけどね。

最後に、フルスタックのフレームワーク。

QwicketはWicket+Spring+Hibernateのフレームワーク。結果として僕が選んだのと同じ組合せなので、これを使おうかと思ったけど、更新滞ってていまだにWicket、Spring、Hibernateともに古いバージョンなので、これも却下しました。また、ROMAはEcho 2+Spring+JPOXのフレームワークであり、Echo 2でいくことになってたらこれもアリだったのかもしれませんが、Wicketを選んだのでこれも却下で。ChuraはSeasar 2.4使うならってとこだったんだけどこれも却下で。

というわけで、結果としてWicket1.2.3+Spring 2.0+Hibernate 3.2で自力で組み合わせて作っていくことにしました。

それを前提に、使用するサーバ・DB等を考えました。

EJB 3.0ならJBossGeronimoGlassfishかといったところだったのですが、Springでいくので今回はJetty 6.0.1でいくことにしました。Jettyを選んだ理由としては、Cometを実現しやすいことがあります。まあ、GlassfishでもNIO使ってるらしいけど。JettyのほうがWebサーバ機能もあり、軽量だってことで。

んで、DBはPostgresでいきます。MySQLでもいいんだけど、なんとなくエンタープライズ版だしたりするあたりの商魂が嫌だったんで。

最後にIDEはEclipse(+Callisto)でいきます。EJB 3.0ならNetBeansこのDemoみたいに楽にできるみたいだけど、EclipseのWTP用のJettyプラグインも発見したので、まあEclipseでいいかな、と。Wicket用のプラグインであるWicket Benchとかもあるけど、これもライブラリファイルとか勝手に入れてくれたりするので気にくわなかった。

というわけでまとめると、

OS:Ubuntu Linux 6.10 日本語版
言語:Java
フレームワーク:Wicket 1.2.3、Spring 2.0、Hibernate 3.2
アプリケーションサーバ:Jetty 6.0.1
データベース:PostgreSQL 8.1.4

で、SNS+Blogのようなシステムを作っていきます。

次回は、WicketとSpringとHibernateを結びつける方法について書こうと思います。

ではまた。

次のページ »