ひこぽんのーと

覚書と雑記です。

JavaEE 7をやってみよう。 JSF 画面遷移 その2

その1のつづき。

登録画面用のManaged Beanを作る。
その前に、Managed Beanで使う艦種情報検索用の機能をJPAの回で作ったEJBに追加する。

WarshipServiceLocal.java

EJBモジュールのインターフェイス
ここにgetAllTypeList()とgetWarship(Warship key)を追加する。

getAllTypeList()は艦種リストを取得するメソッド
主に艦種選択リストのデータ取得に使用する。

getWarship(Warship key)は艦艇IDから艦艇情報を取得するメソッド
これはまだ使う予定は無いが、更新を作るときに使うのでついでに作る。

package ejbModule;

import java.util.List;

import javax.ejb.Local;

import model.Warship;
import model.WarshipType;

@Local
public interface WarshipServiceLocal {
    public List<Warship> getAllList();
    public List<WarshipType> getAllTypeList();
    public Warship getWarship(Warship key);
}

WarshipService.java

getAllTypeList()とgetWarship(Warship key)の実装を追加する。
getAllTypeList()に関しては、getAllListメソッドと同じ作りなので楽勝。

条件を付ける方にしても、プライマリキーで検索する場合では、
すでにEntityManaegerに用意されているfindメソッドを使えば良いのでこれまた楽勝。
findメソッドの第1パラメータが取得するクラス、第2パラメータがキー値。
この場合、取得クラスはWarshipクラスで、キー値はStringのID。

もっとも、対象テーブルが複合キーだったりすると、
このやり方は通用しないのだが(なんせ、第2パラメータに渡せるのはキー値そのものだから)、
そのことについてはまた、別の機会に。

package ejbModule;

import java.util.List;

import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import model.Warship;
import model.WarshipType;

/**
 * Session Bean implementation class WarshipService
 */
@Stateless
@LocalBean
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class WarshipService implements WarshipServiceLocal {
    @PersistenceContext
    private EntityManager entityManager;
    
    /**
     * Default constructor. 
     */
    public WarshipService() {
    }

    @Override
    public List<Warship> getAllList() {
        return entityManager.createNamedQuery("Warship.findAll", Warship.class).getResultList();
    }

    @Override
    public List<WarshipType> getAllTypeList() {
        return entityManager.createNamedQuery("WarshipType.findAll", WarshipType.class).getResultList();
    }

    @Override
    public Warship getWarship(Warship key) {
        return entityManager.find(Warship.class, key.getId());
    }
}

EJBを直したら、次はManaged Bean。

WarshipDefault.java

艦種、艦名保持用のフィールド、
艦種リスト取得、
艦艇取得を共通化するためのabstractクラス。

編集画面でも同じような構成の画面になるので、こんなクラスを作成してみた。
新規登録画面でのManaged Beanはこれを継承したものとなる。

package app.manage;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.model.ListDataModel;

import model.Warship;
import model.WarshipType;
import ejbModule.WarshipServiceLocal;

public abstract class WarshipDefault {
    @EJB
    protected WarshipServiceLocal ejb;

    /** 入力フィールド値の格納用 **/
    private Warship warship;
    
    @PostConstruct
    private void init() {
        warship = new Warship();
        warship.setWarshipType(new WarshipType());
    }
    
    public ListDataModel<WarshipType> findAllWarshipType() {
        return new ListDataModel<WarshipType>(ejb.getAllTypeList());
    }
    
    public void find() {
        Warship key = new Warship();
        key.setId(getWarshipId());
        warship = ejb.getWarship(key);
    }
    
    public Warship getWarship() {
        return warship;
    }
    
    public void setWarship(Warship warship) {
        this.warship = warship;
    }
    
    public abstract Integer getWarshipId();
    
    public abstract void update();
}

WarshipRegister.java

今のところ登録処理は作ってないので、
実質、空実装に等しいが、登録メソッドも作った。

package app.manage;

import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

@RequestScoped
@Named(value="WarshipReg")
public class WarshipRegister extends WarshipDefault {

    @Override
    public Integer getWarshipId() {
        // 新規登録ではfindを使わないため、空実装
        return null;
    }

    @Override
    public void update() {
        // TODO ejbに登録メソッドを作成後に実装
    }

}

ここまでで、ひとまず、Java側はおしまい。
新規登録画面のinsert.xhtml艦種リストの呼び出しと登録メソッドの呼び出しを追加する。

insert.xhtml

入力項目の格納先には、Managed Beanに用意したフィールドをあてがう*1
フィールドはWarshipクラスなので、Warshipクラスのメンバをそれぞれ指定することになる。

艦種選択リストのOptionタグに当たる部分については、selectItemsタグを使ってListDataModelから生成できる。
selectItemsタグはselectOneMenuタグの中に記述する。
使い方はdataTableタグと似ている。
valueにはリスト値を、varにはその要素を表す変数名を指定できる点は、全く同じ。
他に、itemLabelには表示する値、itemValueには選択時に送信される値を設定する。
itemLabelEscpedは文字通り表示値をエスケープするか否かで、trueにすれば、
HTMLコードもそのまんま文字として出力できる。

あとは、登録時の呼び出しとして、
登録ボタンにactionListenerを追加した。
actionListenerには、登録メソッドたる、updateメソッドの呼び出しを記述した。

actionListenerはManaged Beanのメソッドの呼び出しが行える。
ただし、呼び出すメソッドにはパラメータを持たせることが出来ない。
戻り値はvoidかStringであることが条件。

JSFでは、actionLisener → actionの順で処理が行われるので、
actionListenerを使えば、メソッド呼び出し後に画面遷移を行うことができる。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
        xmlns:ui="http://java.sun.com/jsf/facelets" 
        xmlns:h="http://java.sun.com/jsf/html"
        xmlns:f="http://java.sun.com/jsf/core">
    <h2><h:outputLabel value="#{staticText['warship.reg.lblSreenTitle']}" /></h2>
    <hr/>
    <h:messages id="errorText" layout="table" errorClass="error-message"/>
    <h:outputLabel value="#{staticText['warship.reg.lblWarshipType']}" />
    <h:selectOneMenu id="warshipType" value="#{WarshipReg.warship.warshipType.typeId}" styleClass="warship-type-select" >
        <f:selectItems value="#{WarshipReg.findAllWarshipType()}" var="warshipType"
                itemLabel="#{warshipType.name}" itemValue="#{warshipType.typeId}" itemLabelEscaped="true" />
    </h:selectOneMenu>
    <h:outputLabel value="#{staticText['warship.reg.lblWarshipName']}" />
    <h:inputText id="warshipName" value="#{WarshipReg.warship.name}" styleClass="warship-name-input" required="true" autocomplete="off" />
    <br/>
    <h:commandButton value="#{staticText['warship.reg.btnRegister']}" action="catalog" actionListener="#{WarshipReg.update()}" />
    <h:commandButton value="#{staticText['warship.reg.btnRegisterAndRegister']}" />
</ui:composition>

といったところで、テスト実行。
画面を動かしてメニューから新規登録を選ぶと入力画面へ遷移でき、
入力画面では艦種選択リストが表示できている。
また、艦名を入力した後に登録ボタンを押すと一覧画面に遷移できる。

f:id:nagamitsu1976:20151030145214p:plain
f:id:nagamitsu1976:20151106180159p:plain

こんな感じで選択リストの表示と、登録ボタンの処理はできた。
次回はJPAの続きとして新規登録(Insert)の実装を行う。

*1:ほんとは、入力項目と1対1とフィールドを設けたほうがわかりやすいとは思うけど、色々思ってこうした