FrontPage

2008/03/12からのアクセス回数 8703

Spring-MVCのValidationでドメインモデルのString型以外の属性を検証したい

Formコントローラでエラーをチェックする場合、

しかし、ドメインモデルのString型以外の属性に不適当な文字列を指定するとValidationの前の 段階でエラーとなり、Validation機能が有効に働きません。

以下は、MemberクラスにInteger型のageを追加し、入力として数値としてふさわしくない"b" を入力したときのエラー出力画面です。

validation_error1.jpg

GenMVCプラグインで実行したいと考えているプログラミングは、

という一連の流れにおいて、ドメインモデルの属性の型にStringしか使えないというのは 致命的な問題です。

解決方法

悩んだあげく以下のような方法で対応することにしました。

ドメインモデルに対応したコマンドオブジェクトの生成

idを除くテーブル要素となる属性(テーブルのカラムに対応するもの)の型をすべてString型とし、 それ以外をそのままの型と持ち、それらのアクセスメソッドを生成します。

Cart問題のLineItemを定義しますと、

public class LineItem {
	private Integer	id;
	private Integer	quantity = new Integer(1);
	private Integer	productId;
	private Product	product;
	private Integer	orderId;
	-- アクセスメソッドは省略
}

から以下のようなコマンドオブジェクトが生成されます。

package example.cart.command;

public class LineItemCommand {
	private Integer	id;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	private String	orderId;
	public String	getOrderId () {
		return orderId;
	}
	public void setOrderId (String orderId) {
		this.orderId = orderId;
	}
	-- productId, quantity は、省略
	private example.cart.domain.Product	product;
	public example.cart.domain.Product	getProduct () {
		return product;
	}
	public void setProduct (example.cart.domain.Product product) {
		this.product = product;
	}
}

ドメインモデルとコマンドオブジェクト間の変換関数

先ほどのLineItemの編集を行うEditLineItemControllerを見てみると

	protected void bind(Object target, Object source)  throws Exception {
		CustomDataBinder binder = new CustomDataBinder(target, source);
		this.prepareBinder(binder);
		binder.bind();				
	}
	
	protected Object commandToDomain(Object source) throws Exception {
		Object object = new LineItem();
		bind(object, source);
		return (object);
	}

	protected Object domainToCommand(Object source) throws Exception {
		Object object = new LineItemCommand();
		bind(object, source);
		return (object);
	}

CustomDataBinderの処理

このように簡単にコマンドオブジェクトとドメインモデルの属性間の値変換ができるのは、 ServletRequestDataBinderとCustomDataBinderのおかげです。

CustomDataBinderでは、

以下にCustomDataBinderのソースを示します。

package jp.co.pwv.utils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jp.co.pwv.utils.DbHelper;

import org.springframework.beans.MutablePropertyValues;
import org.springframework.web.bind.ServletRequestDataBinder;

public class CustomDataBinder extends ServletRequestDataBinder {
	DbHelper	helper;
	Map			valueMap = new HashMap();
	
	private void setUpValueMap(Object source) {
		List propList = helper.getPropList();
		for (int i = 0; i < propList.size(); i++) {
			PropertyDescriptor objProp = (PropertyDescriptor)propList.get(i);
			String	propName = objProp.getName().toUpperCase();
			Class	propType = objProp.getPropertyType();
			Method	getMethod = objProp.getReadMethod();
			Object	propValue = null;
			try {
				propValue = getMethod.invoke(source, null);
			}
			catch (Exception e) {
				propValue = null;
			}
			if (propName.equals("CLASS"))	// オブジェクト自身を除く
				continue;	
			if (propValue != null)
				if (Integer.class.equals(propType) ||
						Double.class.equals(propType) ||
						Boolean.class.equals(propType) ||
						String.class.equals(propType) ||
						Date.class.equals(propType) ||
						java.util.Date.class.equals(propType) ||
						Timestamp.class.equals(propType)) {
					valueMap.put(objProp.getName(), propValue.toString());
				}
				else {
					valueMap.put(objProp.getName(), propValue);					
				}
		}				
	}
	
	public CustomDataBinder(Object target, Object source) {
		super(target);
		helper = new DbHelper(source.getClass());
		setUpValueMap(source);
	}
	
	public void bind() throws Exception {
		MutablePropertyValues mpvs = new MutablePropertyValues();
		mpvs.addPropertyValues(valueMap);
		super.doBind(mpvs);
	}
}

動作の確認

validation.xmlのmemberCommandのフォームのageのチェックをrequired,integerに変更します。

		<form name="memberCommand">
			<field property="address" depends="required">
				<arg0 key="member.address" />
			</field>
			<field property="age" depends="required,integer">
				<arg0 key="member.age" />
			</field>
			<field property="name" depends="required">
				<arg0 key="member.name" />
			</field>
		</form>

mavenでパッケージ、jettyを起動します。

$ mvn package
$ mvn jetty:run

正常に動作し、"Member age must be an integer"のメッセージが出力しました。

validation_ok.jpg

コメント

この記事は、

選択肢 投票
おもしろかった 0  
そうでもない 0  
わかりずらい 0  

皆様のご意見、ご希望をお待ちしております。


(Input image string)

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
SmartDoc