アプリケーションアーキテクチャの説明
サンプルアプリケーションは冒頭で説明したとおり、MVCモデルで作られています。
簡単に説明すると、サーブレット(Controller)を1本にし、ビジネスロジック部分のみをModelに実装して、表示はView(jsp)に任せるという効率的なアプリケーションの作り方になると思います。
次に上記の図について説明をしようと思います。まず、左からControllerにあたるものが、com.oisix.sample.base.ControllerServlet.javaになります。ControllerServletはweb.xmlでwelcomeというurlとマッピングされています。中身はシンプルなサーブレットですが、少し分かりづらい部分がありますので、ソースを説明したいと思います。
ControllerServletは、リクエストパラメータの「model」というキー名で指定されたModelを呼び出す作りになっています。もし該当のModelがない場合は、エラー画面に遷移します。
続いて、Modelの説明になります。ModelはModelBeanインターフェースを実装し、下記のソースコードの太字部分のようにControllerServletから
- init(HttpServletRequest request, HttpServletResponse response)…初期化を処理を実装。
- validate()…入力チェックなどのバリデーション処理を実装。
- process()…データベースアクセスなどのビジネスロジックを実装。
のメソッドが順番に呼び出されるようになっています。Modelの処理が完了すると、該当のjspにforwardされます。
String name = request.getParameter("model");
ModelBean modelBean = null;
try {
modelBean = (ModelBean)Class.forName(modelPackage + name + "Bean").newInstance();
} catch (Exception e) {} // log省略
if (modelBean == null) {
forward("error.jsp", request, response);
} else {
modelBean.init(request, response);
modelBean.validate();
modelBean.process();
request.setAttribute("bean", modelBean);
if (modelBean.isSystemError()) {
forward("error.jsp", request, response);
} else {
forward(name + ".jsp", request, response);
}
}
ViewはWebContent/WEB-INF/jsp以下のjspになります。
データベースアクセスについては、Daoはcom.oisix.sample.dao以下のクラスで行い、O/Rマッパーにhibernateを使用しています。Hibernateのエンティティはcom.oisix.sample.model以下に置いてあります。
テスト対象クラスの説明
テスト対象ソースの説明をしたいと思います。すべて掲載すると長くなってしまうので、一部省略してあります。
説明については、ソースにコメントを付けましたので、確認いただければと思います。
public class CustomerSearchBean extends ModelBeanBase {
・
・省略
・
@Override
public void validate() throws IOException, ServletException {}
@Override
public void process() {
// isClick()メソッドは、ブラウザで[検索]ボタンが押されてたことを判断するメソッドです。
// ”search”というキーがリクエストパラメーターに含まれているか確認して[検索]ボタンが
// 押されたことを判断しています。
if (isClick("search")) {
this.search();
}
}
/**
* 顧客を検索します。
*/
public void search() {
getParameter();
// hibernateのセッションを作っている部分です。
Configuration configuration = new Configuration();
・
・省略
・
try {
factory.getCurrentSession().beginTransaction();
// Daoで顧客情報を検索しています。このfind()メソッドの中でsqlを発行しています。
// 結果はresultというフィールド変数に参照させて、jspで表示するときに使います。
this.result = MstCustomerDao.find(factory.getCurrentSession(), this.searchCondition);
factory.getCurrentSession().getTransaction().commit();
} catch (RuntimeException e) {
factory.getCurrentSession().getTransaction().rollback();
} finally {
factory.getCurrentSession().close();
}
}
/**
* リクエストパラメータを取得します。
*/
private void getParameter() {
// このメソッドではブラウザから送信された検索条件をMstCustomerにセットしています。
this.searchCondition.setCustomerId(getParameterNvl("customerId"));
this.searchCondition.setFullname(getParameterNvl("fullname"));
・
・省略
・
}
・
・省略
・
}
上記に掲載しているソースは、顧客検索画面のソースです。基本的な作りとしてvalidate()、process()メソッドで構成されているクラスです。validate()に入力チェックに関する処理を実装して、process()メソッドに画面に表示する項目をデータベースから取得してくるようなビジネスロジックを実装します。詳しい説明については、上記ソースのコメントとして記述しましたので、そちらをご確認ください。
public class CustomerEditBean extends ModelBeanBase {
・
・省略
・
@Override
public void validate() {
// isClick()メソッドは、ブラウザで[登録]または、[更新]ボタンが押されてたことを判定するメソッドです。
// "edit"というキーがリクエストパラメーターに含まれているか確認して[確認]ボタンが
// 押されたことを判定しています。
if (isClick("edit")) {
// リクエストパラメータから登録・更新情報を取得します。
this.getParameter();
// エラーメッセージ用のListを取得します。
List<String> errors = super.getErrors();
// 登録・更新する情報に対して入力チェックを行います。
new FullnameValidator(errors).validate(this.mstCustomer.getFullname());
new FullnameKanaValidator(errors).validate(this.mstCustomer.getFullnameKana());
new MailAddressValidator(errors).validate(this.mstCustomer.getMailAddress());
new ZipCodeValidator(errors).validate(this.mstCustomer.getZipCode1() + "-" + this.mstCustomer.getZipCode2());
new TodofukenValidator(errors).validate(this.mstCustomer.getTodofuken());
new Address1Validator(errors).validate(this.mstCustomer.getAddress1());
new Address2Validator(errors).validate(this.mstCustomer.getAddress2());
new TelValidator(errors).validate(this.mstCustomer.getTel1(), this.mstCustomer.getTel2(), this.mstCustomer.getTel3());
}
if (errors.size() == 0) {
// エラーがなかった場合の処理
// 郵便番号と電話番号は画面ではテキストボックスを分けて入力していますが
// 郵便番号:[xxx]-[xxxx]、電話番号:[xxx]-[xxxx]-[xxxx]
// データベースのカラムでは、1カラムになっているので結合しています。
this.mstCustomer.joinZipCode();
this.mstCustomer.joinTel();
}
// エラーをフィールド変数にセットする。
super.setErrors(errors);
}
@Override
public void process() {
this.mstCustomer.setCustomerId(getParameterNvl("customerId"));
// isClick()メソッドは、ブラウザで[登録]または、[更新]ボタンが押されてたことを判定するメソッドです。
// "edit"というキーがリクエストパラメーターに含まれているか確認して[確認]ボタンが
// 押されたことを判定しています。
if (isClick("edit")) {
this.edit();
}
// こちらのロジックはリクエストパラメータにcustomerIdを含むか判断して
// 含まれる場合は顧客情報検索のsearch()メソッドを呼び出す処理です。
// 更新モードでこの画面クラスが呼ばれた場合は、リクエストパラメータでcustomerIdを送信するようにしています。
else if (!StringUtils.isEmpty(this.mstCustomer.getCustomerId())) {
search();
request.setAttribute("mstCustomer", this.mstCustomer);
}
}
public void search() {
Configuration configuration = new Configuration();
configuration.configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
SessionFactory factory = configuration.buildSessionFactory(serviceRegistry);
try {
// トランザクション開始
factory.getCurrentSession().beginTransaction();
// Daoで顧客情報を検索しています。このfindByPk()メソッドの中でsqlを発行されています。
// 結果はmstCustomerというフィールド変数に参照させて、jspで表示するときに使います。
this.mstCustomer = MstCustomerDao.findByPk(
factory.getCurrentSession(), mstCustomer.getCustomerId()
);
this.mstCustomer.divideZipCode();
this.mstCustomer.divideTel();
// コミット
factory.getCurrentSession().getTransaction().commit();
} catch (RuntimeException e) {
// ロールバック
factory.getCurrentSession().getTransaction().rollback();
} finally {
// セッションをクローズ
factory.getCurrentSession().close();
}
}
public void edit() {
// 入力エラーがあるか判定
// 入力エラーがある場合は処理終了
if (super.isError()) {return;}
Configuration configuration = new Configuration();
configuration.configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
SessionFactory factory = configuration.buildSessionFactory(serviceRegistry);
try {
// トランザクション開始
factory.getCurrentSession().beginTransaction();
// セッションにマージ
MstCustomerDao.merge(factory.getCurrentSession(), mstCustomer);
// コミット
factory.getCurrentSession().getTransaction().commit();
super.setActionMessage(getViewTitle() + "完了しました。");
} catch (RuntimeException e) {
// ロールバック
factory.getCurrentSession().getTransaction().rollback();
super.setActionMessage(getViewTitle() + "に失敗しました。");
} finally {
// セッションをクローズ
factory.getCurrentSession().close();
}
}
・
・省略
・
}
上記に掲載しているソースは、顧客登録・更新画面のソースです。こちらも基本的な作りとしてvalidate()、process()メソッドで構成されているクラスです。validate()に入力チェックに関する処理を実装して、process()メソッドに画面に表示する項目をデータベースから取得してくるようなビジネスロジックを実装します。詳しい説明については、上記ソースのコメントとして記述しましたので、そちらをご確認ください。
簡単ではありますが、上記がサンプルWebアプリケーションのディレクトリ構成になります。
- 「build」…ビルドしたファイルが出力されます。Eclipseの.classpathで使用しているディレクトリです。
- 「src」…hibernateの設定ファイルの他、javaファイルが置いてあります。
- 「Web-Content/images」…画像ファイルが置いてあります。
- 「Web-Content/META-INF」…MANIFESTファイルが置いてあります。
- 「Web-Content/WEB-INF」…web.xmlファイル、jspの中にはjspファイル。libの中には主にhibernateに必要なjarが入っています。


