SAStruts + S2JDBC でサンプルアプリを作ってみました
以前、WEB+DB PRESS vol. 41 にて特集2『つらいJavaからたのしいJavaへ Seasar2 サクサク開発 実践カリキュラム』を執筆させて頂きました。
(http://gihyo.jp/magazine/wdpress/archive/2007/vol41)
この記事では、プレゼンテーション層にTeeda、データアクセス層にDBFluteというフレームワーク使ってサンプルアプリケーションを作成しました。
今回は、前回と同じ外部仕様、同じデータベースによる
サンプルアプリケーションのSAStruts + S2JDBC版を作ってみました。
ただし、現時点では「正常系が動作した」というレベルの完成度です。
前回のサンプルアプリケーションと比較して、以下のような未実装な機能が
多く存在するので注意が必要です。
- 一部の日付書式
- バリデーション
- 二度押し防止
- 戻るボタン対策
- 共通レイアウト
- 共通エラーページ
ソースコードを見てもらう前に外部仕様を見てみましょう。
『検索条件入力』 ⇒ 『検索結果一覧 』
⇒ 『更新』 ⇒ 『更新確認』 ⇒ 『更新完了』
という典型的な流れです。
次に、データベース(ERD)を見てみましょう。
従業員(EMP)テーブルに部署(DEPT)テーブルが関連している
典型的なデータ構造です。
アプリケーション用に作成したクラスやJSPファイルは以下のとおりです。
■Actionクラス
- EmpAction.java
■Entityクラス
■JSPファイル
では、ソースコードを見てみましょう。
長くなるので解説は省略しますが、
可読性が高いので、読めばわりと理解できるのではないでしょうか。
(分かりにくい箇所があれば、コメント欄で答えます。)
検索条件入力のURLは次のとおりです。seasardemoアプリケーションのEmpActionクラスのsearchメソッドが呼ばれます。
http://localhost:8080/seasardemo/emp/search
EmpAction.java
package sample.action; import java.util.List; import org.seasar.extension.jdbc.JdbcManager; import org.seasar.framework.beans.util.BeanMap; import org.seasar.framework.beans.util.Beans; import org.seasar.struts.annotation.DateType; import org.seasar.struts.annotation.Execute; import org.seasar.struts.annotation.IntegerType; import sample.entity.Dept; import sample.entity.Emp; public class EmpAction { public JdbcManager jdbcManager; /** 検索条件: 従業員名の前方一致 */ public String condition_name_STARTS; /** 検索条件:入社日の範囲検索(開始) */ @DateType public String condition_hireDate_GE; /** 検索条件:入社日の範囲検索(終了) */ @DateType public String condition_hireDate_LE; /** 識別子(従業員)です。 */ @IntegerType public String id; /** 名前(従業員)です。 */ public String name; /** 入社日(従業員)です。 */ @DateType public String hireDate; /** 部署の識別子です。 */ @IntegerType public String deptId; /** バージョン(従業員)です。 */ @IntegerType public String versionNo; /** 部署名です。 */ public String deptName; /** 従業員のリストです。 */ public List<Emp> empItems; /** 部署のリストです。 */ public List<Dept> deptItems; @Execute(validator = false) public String search() { return "search.jsp"; } @Execute(validator = false) public String list() { empItems = jdbcManager.from(Emp.class) .leftOuterJoin("dept") .where(Beans.createAndCopy(BeanMap.class, this) .prefix("condition_") .excludesWhitespace() .execute()) .orderBy("hireDate") .getResultList(); return "list.jsp"; } @Execute(validator = false, urlPattern = "update/{id}") public String update() { deptItems = jdbcManager.from(Dept.class).orderBy("id").getResultList(); Emp emp = jdbcManager.from(Emp.class).id(id).getSingleResult(); Beans.copy(emp, this).dateConverter("yyyy/MM/dd", "hireDate").execute(); return "updateInput.jsp"; } @Execute(validator = false) public String showConfirm() { if ("".equals(deptId)) { deptName = ""; } else { Dept dept = jdbcManager.from(Dept.class).id(deptId).getSingleResult(); deptName = dept.name; } return "updateConfirm.jsp"; } @Execute(validator = false) public String executeUpdate() { Emp emp = Beans.createAndCopy(Emp.class, this).execute(); jdbcManager.update(emp).execute(); return "updateComplete.jsp"; } }
Emp.java
package sample.entity; import java.util.Date; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToOne; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Version; /** * 従業員です。 * * @author dewa * */ @Entity public class Emp { /** 識別子です。 */ @Id @GeneratedValue public Integer id; /** 名前です。 */ public String name; /** 入社日です。 */ @Temporal(TemporalType.DATE) public Date hireDate; /** 部署の識別子です。 */ public Integer deptId; /** 部署です。 */ @ManyToOne public Dept dept; /** バージョンです。 */ @Version public Integer versionNo; }
Dept.java
package sample.entity; import java.util.List; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Version; /** * 部署です。 * * @author dewa */ @Entity public class Dept { /** 識別子です。 */ @Id @GeneratedValue public Integer id; /** 名前です。 */ public String name; /** バージョンです。 */ @Version public Integer versionNo; }
search.jsp
<html> <head> <title>社員検索</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <s:form action="/emp"> <h1>社員検索</h1> 社員名: <input name="condition_name_STARTS" type="text" title="社員名"/>(前方一致)<br> 入社日: <input name="condition_hireDate_GE" type="text" title="入社日(開始)" />〜 <input name="condition_hireDate_LE" type="text" title="入社日(終了)" /><br> <input type="submit" name="list" value="検索" /> </s:form> </body></html>
list.jsp
<html> <head> <title>社員検索結果一覧</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <h1>社員検索結果一覧</h1> 社員名: ${f:h(condition_name_STARTS)}<br> 入社日: ${f:h(condition_hireDate_GE)} 〜 ${f:h(condition_hireDate_LE)}<br> <table border="1" > <thead> <tr> <th>社員名</th><th>入社日</th><th>部署名</th><th></th> </tr> </thead> <tbody> <c:forEach var="emp" varStatus="s" items="${empItems}"> <tr style="background-color: ${s.index % 2 == 0 ? 'white' : 'lightblue'};"> <td>${emp.name}</td> <td><fmt:formatDate value="${emp.hireDate}" type="DATE" dateStyle="FULL"/></td> <td>${emp.dept.name}</td> <td><a href="update/${f:u(emp.id)}" target="_blank"> 変更 </a></td> </tr> </c:forEach> </tbody> </table> </body></html>
updateInput.jsp
<html> <head> <title>社員変更</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <style TYPE="text/css"> </style> </head> <body> <s:form action="/emp"> <html:hidden property="id"/> <html:hidden property="versionNo"/> <h1>社員変更</h1> <html:errors/> 社員名*: <html:text property="name"/><br /> 入社日 : <html:text property="hireDate"/><br /> 部 署 : <html:select property="deptId"> <html:option value=""></html:option> <c:forEach var="dept" items="${deptItems}"> <html:option value="${f:h(dept.id)}">${f:h(dept.name)}</html:option> </c:forEach> </html:select> <br /> <input type="submit" name="showConfirm" value="確認" /> </s:form> </body></html>
updateConfirm.jsp
<html> <head> <title>社員変更確認</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <s:form action="/emp"> <h1>社員変更確認</h1> 社員名: ${f:h(name)} <br> <html:hidden property="name"/> 入社日: ${f:h(hireDate)} <br> <html:hidden property="hireDate"/> 部 署: ${f:h(deptName)} <br> <html:hidden property="deptId"/> <html:hidden property="id"/> <html:hidden property="versionNo"/> <input type="submit" name="executeUpdate" value="変更" /> </s:form> </body></html>
updateComplete.jsp
<html> <head> <title>社員変更完了</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <h1>社員変更完了</h1> 社員変更処理が完了しました。 </body></html>
現時点では、非機能要件的な機能が実装されていないにも関わらず、
ファイル数やコード量がとても少ないですねぇ。