JBoss Toolsを使ってみよう その2

さて、ここからアプリを作っていきます。
File > New > から

という生成ウイザードが使用できます。名前だけ見るとそれぞれがWebアプリ上で必要になる各レイヤに相当するように見えますが、実はどれも「画面+必要になるクラス etc.」というセットを生成してくれるもので、それぞれは「作る画面のタイプ」が違うだけなんです。具体的には

  • Seam Action->ボタン一個の画面とボタンを押したときのアクション
  • Seam Form->入力フォーム画面とフォーム入力処理アクション(バリデーション込み)
  • Seam Entity->ひとつのエンティティに対応したCRUD処理を行う一連の画面とその処理アクション
  • Seam Conversation->Conversationスコープの処理を行う足し算画面とその処理アクション
  • Seam Generate Entities->複数エンティティ or DBのテーブルに対応したCRUD処理を行う一連の画面とその処理アクション

です。
使ってみればわかりますが、開発アシストというよりは個々の画面サンプルの色合いが強いです。といってもSeamアーキテクチャ、クラス粒度を学ぶのにちょうどよい感じ?慣れればさくっと修正すればいいしね。

今回作るアプリはユーザ情報登録フォーム、っていうことで、単一のエンティティPersonを扱います。
まずは「Seam Entity」からいきます。

クラス名Personを入力してFinishを押すと、

  • エンティティであるPersonクラス
  • Person一覧のハンドリングを行う画面のpersonList.xhtmlと対応するアクションのPersonListクラス
  • Person一件のCreate、Update、Deleteを行う画面のperson.xhtmlと対応するアクションのPersonHomeクラス

を一気に生成します。
え?Personクラスのプロパティは?というと、デフォルトで勝手にID、Vesion、Nameの3つで作っちゃってくれています。エンティティなんでもちろんDBも一緒にCreate tableしちゃいます。Nameは最大20文字のバリデーションというおまけつきです。

http://localhost:8080/FirstSeam/personList.seam

で早速動かせます。

ちゃんと動きますねえ。スパッと動くものを用意して、そこからカスタマイズしてねっていうのはRails以降を強く意識しているところでしょう。

home.xhtmlからこの画面に遷移できるようにリンクを追加してみます。
右ペインの「JBoss Tools Palett」から「JBoss Seam」のlinkタグを選択します。コードアシストが強力なのでさくさく進められますね。

<s:link id="personList" value="一覧" view="/personList.xhtml"/>

※日本語対応について:
JBoss Toolsで生成されるxhtmlにはxml宣言がないのでエンコーディングはデフォルトでUTF-8です。WindowsEclipseを使う人は基本的にMS932(Windows-31J)と思われるので、普通に日本語を使うと文字化けしてしまいます。
対策としては、Eclipseの設定をUTF-8にする(Eclipseの Preferences > General > Workspace のText file encodingを変更)か、明示的にxml宣言を入れてエンコーディングを指定するか、です。

<?xml version="1.0" encoding="Windows-31J" ?>

さて、生成されたクラスをチェックしてみましょうか。

  • Person
package org.domain.FirstSeam.entity;

import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.Version;
import org.hibernate.validator.Length;

@Entity
public class Person implements Serializable {
	
	//seam-gen attributes (you should probably edit these)
	private Long id;
	private Integer version;
	private String name;
	
    //add additional entity attributes
	
	//seam-gen attribute getters/setters with annotations (you probably should edit)
		
	@Id @GeneratedValue
	public Long getId() {
	     return id;
	}

	public void setId(Long id) {
	     this.id = id;
	}
	
	@Version
	public Integer getVersion() {
	     return version;
	}

	private void setVersion(Integer version) {
	     this.version = version;
	}   	
	
	@Length(max=20)
	public String getName() {
	     return name;
	}

	public void setName(String name) {
	     this.name = name;
	}   	
}


単なるPOJOのJavaBean。デフォルトでid,version,nameの3つのプロパティがあります。
POJOといってもアノテーションがついてる時点でただのPOJOではない*1
まず@Entityが、このクラスがJPAで管理されるエンティティだということを表しています。@Idはこのプロパティが主キーであることを示し、@GeneratedValueはJPA側で自動採番することを示しています。@Versionはエンティティのバージョン管理(楽観ロック制御ですね)に使用されるプロパティであることを示しています。ここまではJPAの標準アノテーションですが、nameについている@Length(max=20)はHibernateのバリデーション用アノテーションです。

  • PersonList
package org.domain.FirstSeam.session;

import org.jboss.seam.annotations.Name;
import org.jboss.seam.framework.EntityQuery;

@Name("personList")
public class PersonList extends EntityQuery
{
    @Override
    public String getEjbql() 
    { 
        return "select person from Person person";
    }
}


org.jboss.seam.framework.EntityQueryを継承しており、独自のものはSQLだけです。EntityQueryは汎用的なベースクラスのため、追っかけていくと若干複雑なコードではありますが、JPAのスマートな使用例としても勉強になりそうです。
もちろんEntityQueryを継承せずにJPA(EntityManager)を直接使用してもよいし、必要最小限だけ実装するならそのほうがすごく短く作れます。そこらへんはSeamのexamplesが参考になるかな。

  • PersonHome
package org.domain.FirstSeam.session;

import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Begin;
import org.jboss.seam.annotations.web.RequestParameter;
import org.jboss.seam.framework.EntityHome;

import org.domain.FirstSeam.entity.Person;

@Name("personHome")
public class PersonHome extends EntityHome<Person>
{

    @RequestParameter 
    Long personId;
    
    @Override
    public Object getId() 
    { 
        if (personId==null)
        {
            return super.getId();
        }
        else
        {
            return personId;
        }
    }
    
    @Override @Begin
    public void create() {
        super.create();
    }
 	
}

こっちもorg.jboss.seam.framework.EntityHomeを継承。@RequestParameterがリクエストパラメータとエンティティの検索キーを結び付けてくれるというわけ。

*1:じゃあPOJOって言うなよ、という気持ちも分かる。アノテーションに依存した時点でPOJO(≒特定のクラスを継承しない)のメリットってテスト時の柔軟性ぐらいではないだろうか?ならばPOJOじゃなくてもテストサポートが充実してれば問題ないねえ