struts2とseasar2でCRUD処理

今回はstruts2とseasar2CRUD処理を書いていきます。
前提としてこのページの準備編(トランザクションあたりまで)が済んでいるものとします。
Bootstrapも使ってますが、なくても動くことは動きます。(表示が見難いだけです)
Bootstrap設定メモ

CRUDの対象テーブル

SQL
CREATE TABLE customer (
  id SERIAL NOT NULL PRIMARY KEY,
  name TEXT,
  email TEXT,
  sex INTEGER
);

下記のSQLPostgreSQLのものです。
sexカラムは性別を表すenumです。
java.lang.Enum のサブクラスは,デフォルトでは列挙定数の序数 ( Enum#ordinal() の戻り値) で扱われます。
そのためテーブル定義上の型は整数型にしています。
http://s2container.seasar.org/2.4/ja/s2jdbc_entity.html#列挙定義
また、ここではNOT NULL制約はID以外ははずしています。

kamegu.enumerable.Sex (パッケージは任意)
package kamegu.enumerable;

public enum Sex {
	MALE, FEMALE;
	
	public String toString() {
		if (this == MALE) {
			return "男";
		} else {
			return "女";
		}
	}
}
kamegu.entity.Customer
package kamegu.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import kamegu.enumerable.Sex;

@Entity
public class Customer {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	public Integer id;

	public String name;
	public String email;
	public Sex sex;
}

idに@GeneratedValueアノテーションを設定して、データベースの仕組み(PostgreSQLの場合はシーケンス)でID自動設定するようにします。
http://s2container.seasar.org/2.4/ja/s2jdbc_entity.html#識別子定義
nameとemailフィールドはデータベースのTEXT型に対応するStringクラスとします。
sexフィールドはSexクラス(enum)を型として設定します。

画面作成

今回作成する画面は、一覧表示画面と編集・新規登録画面です。
一覧画面に新規作成ボタンを追加し、そこをクリックすると新規登録画面が表示され、入力して登録すると一覧画面に戻ります。
一覧の各行には編集ボタンを追加し、そこをクリックすると編集画面が表示され、入力して登録すると一覧画面に戻ります。

kamegu.action.fwtest.JspCrudAction
package kamegu.action.fwtest;

import java.util.List;

import javax.annotation.Resource;

import kamegu.common.MyActionSupport;
import kamegu.entity.Customer;
import kamegu.enumerable.Sex;

import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import org.seasar.extension.jdbc.JdbcManager;

@Results({
	@Result(name="list", type="redirect", location="./jsp-crud")
})
public class JspCrudAction extends MyActionSupport {

	public List<Customer> customers;
	public Customer customer = new Customer();

	public Sex[] sexs = Sex.values();

	@Resource
	private JdbcManager jdbcManager;

	public String execute() {
		customers = jdbcManager.from(Customer.class).orderBy("name").getResultList();
		return SUCCESS;
	}

	public String input() {
		if (customer.id != null) {
			customer = jdbcManager.from(Customer.class).id(customer.id).getSingleResult();
		}
		return INPUT;
	}

	public String save() {
		if (customer.id != null) {
			jdbcManager.update(customer).execute();
		} else {
			jdbcManager.insert(customer).execute();
		}
		return "list";
	}

	public String delete() {
		jdbcManager.delete(customer).execute();
		return "list";
	}
}

ここでは画面を2つとしています。
一覧画面(executeメソッド)と入力(新規登録・編集)画面(inputメソッド)です。
これはそれぞれ、jsp-crud.jspjsp-crud-input.jspに対応します。
http://struts.apache.org/release/2.3.x/docs/convention-plugin.html#ConventionPlugin-Resultsandresultcodes
登録(saveメソッド)と削除(deleteメソッド)は、処理後一覧画面にリダイレクトさせるように設定しています。
アクションクラスにResultsアノテーションを設定してそこにtype="redirect"とすることで実現できます。
http://struts.apache.org/release/2.3.x/docs/convention-plugin.html#ConventionPlugin-Resultannotation
次に、各フィールドについてです。

  • customersは一覧画面でテーブルの中身を一覧表示するためのフィールドです。
  • customerは入力画面、登録処理で値の受け渡しをするためのフィールドです。
  • sexsは入力画面で性別のセレクトボックスの一覧として使うためのフィールドです。
  • jdbcManagerはDI注入されるフィールドです。publicでアノテーションなしにすることもできますが、jspから参照できないようにprivateにしてアノテーションをつけています。
src/main/webapp/WEB-INF/content/fwtest/jsp-crud.jsp (body内のみ表示)
<body>
  <table class="table table-condensed table-bordered">
    <thead>
      <tr>
        <th>ID</th><th>名前</th><th>E-Mail</th><th>性別</th><th></th>
      </tr>
    </thead>
    <tbody>
    <s:iterator value="customers">
      <tr>
        <td>${id}</td>
        <td><s:property value="name"/></td>
        <td>${email}</td>
        <td><s:property value="sex"/>(${sex})</td>
        <td>
          <a type="button" class="btn btn-small" href="<s:url method="input"><s:param name="customer.id" value="%{id}"/></s:url>">編集</a>
        </td>
      </tr>
    </s:iterator>
    </tbody>
  </table>
  <a type="button" class="btn btn-small" href="<s:url method="input"/>">新規</a>
</body>

値を表示する際に${}で囲っているものとs:propertyタグで囲っているものとあり、基本的にはどちらでもOKなことが多いです。
違いといえば、性別の列を見れば分かりますが、s:propertyタグで囲ったほうは「男」「女」と表示され${}で囲ったほうは「MALE」「FEMALE」と表示されます。このあたりの詳細はいろいろな仕組みが関係してそうなので、ここでは省略します。
ちなみにここで「id」とか「name」で値を取得できているのは、s:iteratorタグでcustomersをループさせる際に各要素がValueStackに追加されているためです。ValueStackの詳細はここでは省略します。
aタグのhref内をs:urlタグで定義しています。同一アクションを呼ぶためaction属性は省略しています。
http://struts.apache.org/release/2.3.x/docs/url.html
これは、次のように書いても問題ありません。

    <a type="button" class="btn btn-small" href="jsp-crud!input?customer.id=${id}">編集</a>

今回の場合はこちらのほうが簡単な記述ですが、s:urlタグにはさまざまな属性を設定可能なので簡単に記述できる場合もあります。

src/main/webapp/WEB-INF/content/fwtest/jsp-crud-input.jsp (body内のみ表示)
<body>
  <s:form method="POST">
    <s:if test="customer.id != null">
      <s:hidden name="customer.id"/>
    </s:if>
    <table>
      <tr>
        <td>名前</td>
        <td><s:textfield name="customer.name" placeholder="名前"/></td>
      </tr>
      <tr>
        <td>E-Mail</td>
        <td><s:textfield name="customer.email" type="email" placeholder="E-Mail"/></td>
      </tr>
      <tr>
        <td>性別</td>
        <td><s:select name="customer.sex" listKey="name()" list="sexs" value="customer.sex.name()"/></td>
      </tr>
    </table>
    <s:submit cssClass="btn btn-primary" value="登録" method="save"/>
    <s:if test="customer.id != null">
      <s:submit cssClass="btn btn-primary" value="削除" method="delete"/>
    </s:if>
  </s:form>
</body>

ここで書くことがあるとすれば性別のs:selectタグについてくらいです。
SexクラスでtoString()をoverrideしていなければ、次のように書けば十分です。

        <td><s:select name="customer.sex" list="sexs"/></td>

ただ、toString()をoverrideした場合にこのままだとoptionタグのvalue属性がtoString()の値になるためちょっと不都合が生じます。
というのも、struts2のデフォルトではformの値をenumフィールドにセットするためにはname()の値で渡す必要があるためです。
ここはTypeConversionの話になりますので、今回は省略します。
http://struts.apache.org/release/2.3.x/docs/type-conversion.html

確認

/myapp/fwtest/jsp-crudにアクセスして一覧表示と、そこからの新規登録および編集ができることを確認できると思います。