JPA + EJB + JSF による Web アプリケーション(その 3)

さて、DAO の実装が済んだところで Web アプリケーション側の構築に入ります。普通に ServletJSP でも作れますが、今回は JSF を使ってみましょう。

JSF(JavaServer Faces) とは

JSFJava EE 純正規格であるコンポーネントフレームワークです。Struts との決定的な違いは「Java EE 純正の API である」ということでしょう。*1

Struts では View にカスタムタグが使えましたが、あくまでも JSP です。それに対し、JSF 2.0 からは Facelets という、独自のタグが用意された XHTML で View を作成します。また、Struts では ActionForm を継承した Form Bean のクラスと Action クラスを継承したクラスを用意する必要がありましたが、JSF では管理(Managed) Bean を使います。これは他のクラスを継承しない POJO(Plain Old Java Object)として作成できます。*2さらに JSF 2.0 になって、管理 Bean はアノテーションで指定するだけで、設定ファイル(faces-config.xml)を作成・編集する必要がなくなりました。Struts では struts-config.xml の作成・編集が不可避だったので、これは大きな違いです。

管理 Bean を作成する

ではさっそく管理 Bean を作成してみましょう。EclipseJSF プロジェクトを作成してください。

package jp.mydns.akanekodou;

import javax.faces.bean.ManagedBean;
import javax.annotation.PostConstruct;

import javax.ejb.EJB;

import java.util.List;

import jp.mydns.akanekodou.dao.CustomerDAO;
import jp.mydns.akanekodou.entity.Customer;

@ManagedBean
public class CustomerList {
    @EJB
    private CustomerDAO dao;
    private List<Customer> items;

    @PostConstruct
    private void init() {
        items = dao.all();
    }

    public List<Customer> getItems() {
        return items;
    }
}

ManagedBean というアノテーションによって、これが管理 Bean であることを示します。管理 Bean には、Bean のスコープを示すアノテーションを付けることもできます。たとえば session で保持しなければならない情報(ログイン情報など)が含まれる場合は SessionScoped アノテーションを付けます。((SessionScoped にする場合は管理 Bean は Serializable を実装している必要があります。))省略すると RequestScoped になります。

EJB アノテーションで DAO のインスタンスを取得します。で、dao のメソッドを呼び出すのですが、インスタンスはこの管理 Bean がインスタンス化されてから依存性注入で取得するので、インスタンス化が終わった後でないとメソッドを呼び出せません。そこで PostConstruct を使ってインスタンス化が終わった直後に 1 回だけ init メソッドを実行させます。これで itemsCustomer のリストが格納されます。

package jp.mydns.akanekodou;

import javax.faces.bean.ManagedBean;
import javax.faces.component.html.HtmlInputText;

import javax.ejb.EJB;

import jp.mydns.akanekodou.dao.CustomerDAO;
import jp.mydns.akanekodou.entity.Customer;

@ManagedBean
public class CustomerSearch {
    @EJB
    private CustomerDAO dao;
    private HtmlInputText id;
    private Customer item;

    public HtmlInputText getId() {
        return id;
    }

    public void setId(HtmlInputText id) {
        this.id = id;
    }

    public Customer getItem() {
        return item;
    }

    public String search() {
        int id = Integer.parseInt((String)this.id.getValue());
        item = dao.find(id);
        return "";
    }
}

HtmlInputText は後で作成する Facelets の要素と対応するフィールドです。これは getter と setter の両方を用意してください。

String 型を返り値にもつ search メソッドはアクションと言われるもので、フォームの値を送信した時などに実行するメソッドです。返り値には遷移先の XHTML を拡張子なして指定します。空文字を return (もしくは return null) した場合は遷移せずに自分自身にとどまります。ここでは入力された値を ID とみなして、ID 検索で Customer のデータを取得してきます。送信される値は String 型なので一回 String にキャスト(getValue の返り値の型は Object)し、それを整数に変換しています。

package jp.mydns.akanekodou;

import javax.faces.bean.ManagedBean;
import javax.faces.component.html.HtmlInputText;

import javax.ejb.EJB;

import java.util.List;

import jp.mydns.akanekodou.dao.ProductDAO;
import jp.mydns.akanekodou.entity.Product;

@ManagedBean
public class ProductSearch {
    @EJB
    private ProductDAO dao;
    private HtmlInputText keyword;
    private List<Product> items;

    public HtmlInputText getKeyword() {
        return keyword;
    }

    public void setKeyword(HtmlInputText keyword) {
        this.keyword = keyword;
    }

    public List<Product> getItems() {
        return items;
    }

    public String search() {
        items = dao.like((String)keyword.getValue());
        return "";
    }
}

こちらも同じ要領で。

*1:ただし実装はベンダーによって異なります。

*2:一部例外がありますが。