一、简介
虽然在Web应用程序的多个层内加入相应的数据校验功能都十分必要,但是以前实现这项任务是相当费时的,这使得许多开发者干脆略过它—这明显会带来
一系列的问题。但是,随着在最新版本的Java平台中引入了注解概念,校验问题容易得多了。在本文中,我将向你展示在你的Web应用程序中使用
Hibernate注解中的校验器(Validator)组件来构建和维护校验逻辑是多么容易的事情。
二、预备知识
阅读本文前,你应该对下列内容有一个基本了解:Java 5.0(具体地说是其中的“注解”概念);JSP
2.0(因为我在一个TLD中创建标签文件和定义函数,它们都是JSP
2.0新引入的特征);还有Hibernate和Spring框架。另外请注意,即使没有使用Hibernate实现持久性存储,你也可以在自己的应用程
序中使用Hibernate Validator。
与以前的版本相比,Java SE
5增加了许多改进,也许再也没有比注解更为重要的了。借助于注解,你可以最终为你的Java类构造出一种标准的、一流的元数据框架。多年以来,
Hibernate用户一直是手工地编写*.hbm.xml文件(或使用XDoclet自动完成这一任务)。如果你手工地创建XML文件的话,相应于每一
种需要的持久性属性,你都必须更新两个文件(类定义和XML映射文档)。尽管使用HibernateDoclet可以简化这一工作(具体示例请参考列表
1),但是要求你确保你的HibernateDoclet版本支持你想使用的Hibernate版本。另一方面,这种doclet信息在运行时刻还不可
用,因为它被编码成Javadoc风格的注释。列表2中的Hibernate注解对这些选择进行了改进—提供一个标准的简明的映射类,而且在运行时刻可
用。
列表1.使用HibernateDoclet创建的Hibernate映射代码
/** * @hibernate.property column="NAME" length="60" not-null="true" */ public String getName() { return this.name; } /** * @hibernate.many-to-one column="AGENT_ID" not-null="true" cascade="none" * outer-join="false" lazy="true" */ public Agent getAgent() { return agent; } /** * @hibernate.set lazy="true" inverse="true" cascade="all" table="DEPARTMENT" * @hibernate.collection-one-to-many class="com.triview.model.Department" * @hibernate.collection-key column="DEPARTMENT_ID" not-null="true" */ public List<Department> getDepartment() { return department; }
|
列表2.使用Hibernate注解创建的Hibernate映射代码
@NotNull @Column(name = "name") @Length(min = 1, max = NAME_LENGTH) //NAME_LENGTH是一个在另外地方声明的常数 public String getName() { return name; } @NotNull @ManyToOne(cascade = {CascadeType.MERGE }, fetch = FetchType.LAZY) @JoinColumn(name = "agent_id") public Agent getAgent() { return agent; } @OneToMany(mappedBy = "customer", fetch = FetchType.LAZY) public List<Department> getDepartment() { return department; }
|
如果你使用HibernateDoclet,那么,只有到生成XML文件或在运行时刻你才能捕获错误。借助于注解,你能够检测编译时刻中的许多错
误;或者,如果你使用的是一种优秀的IDE的话,在编辑期间也能够检测出许多错误。当从头创建一个应用程序时,你可以利用hbm2ddl工具来由
hbm.xml文件为你的数据库生成DDL。特别注意:name属性的最大长度不超过60个字符,也就是说,DDL应该添加一个“not
null”型约束—从HibernateDoclet入口添加到DDL中。当你使用注解时,你可以用一种与此类似的方式自动地生成DDL。
尽管上面列举的两种代码映射方案都可以使用;但是,相比之下,注解具有更为清晰的优点。借助于注解,你可以使用约束来指定长度或其它值。你将拥有更
快的构建周期而不需要生成XML文件。最大优点是,你能够在运行时刻存取有用的信息,例如一个“not
null”注解或长度。除了在列表2展示的注解之外,你还可以指定校验约束。其中,一些非常有用的约束列举如下:
在适当的情况下,这些注解将会导致可以使用DDL生成检查约束(显然,@Future并不是一种合适的情形)。你还能够据自己的需要创建定制的约束注解。
三、校验与应用程序层
编写校验代码可能是一个十分冗长而且相当耗时的过程。所以,很多情况下,开发的主要负责人都会放弃在一特定层实现校验以便节约时间,但是这常常引起
争议—是否这样做所导致的缺点会抵销所节约的时间?如果花费在整个应用程序层创建和维护校验方面的时间能够极大地减少,那么,我们更应该推荐在更多的层中
实现校验功能。假定你有一个允许用户通过用户名、口令和信用卡号来创建帐户的应用程序,那么,你很可能喜欢在如下一些应用程序组件上加入校验逻辑:
◆视图:通过JavaScript进行校验有望避免客户端与服务器的来回“折腾”,并且能够提供一种更好的用户体验。但是,由于用户有可能会禁用JavaScript,因此尽管这一级别的校验实在不错,但是却并不可靠。但无论如何,对要求的域进行简单的校验是必需的。
◆控制器:校验必须按照服务器端逻辑进行处理。在这一层的代码可以以一种适当的方式来处理校验以适用于特定场所。例如,当添加一个新用户时,在继续操作之前,控制器可以首先检查是否该指定的用户名已经存在。
◆服务:相对复杂的业务逻辑校验最经常情况下应该放于这个服务层来实现。例如,一旦你有一个看上去似乎有效的信用卡对象,那么你就应该使用你的校验处理服务来验证该信用卡信息。
◆DAO:
在数据到达这一层时,它确实应该是有效的。即使如此,再执行一次快速的检查以确保要求的域非空而且该值满足指定的范围或遵循指定的格式化(例如,一个电子
邮件地址域应该包含一个有效的电子邮件地址)都是非常有益的。在这一层捕获一个问题要比导致系统抛出SQLException异常(实际上能够避免)要好
得多。
◆DBMS:对这一部分的校验常常被忽视。即使你今天构建的应用程序仅是数据库的客户端,其它客户端也有可能在未来某一天添加进来。如果应
用程序存在错误(并且大多数情况下是如此),那么,无效的数据就会到达数据库中。在这种情况下,如果你幸运的话,你有可能会发现这部分无效的数据并要求你
确定是否以及怎样把它清理掉。
◆模型:在这一层比较适合于加入对不要求存取外部服务或持久性数据信息的校验。例如,你的业务逻辑可以指示用户至少提供一种形式的联系人信息,或者是一个电话号码或者是一个电子邮件地址,那么,此时你可以使用模型层校验来确保用户实现满足这一要求。
一种典型的校验方案是:首先使用Commons Validator实现简单校验,然后在控制器中编写其它的校验。Commons
Validator提供了自动生成JavaScript来处理视图中校验功能的优点。但是,Commons
Validator也确实存在它自己的不足:它仅能处理简单的校验并且它把校验定义存储到一个XML文件内。Commons
Validator的设计目的是为了与Struts联用,因此没有提供一种容易的方法来实现跨整个应用程序层重用校验声明。
当规划你的校验策略时,仅仅简单地选择在发生错误时才预以处理是远远不够的。一个良好的设计还应该能够防止错误-通过生成一种友好的用户接口。采取
积极的校验方法能够极大地增强用户使用应用程序的感觉。遗憾的是,Commons
Validator没有提供这种支持。假定你想使你的HTML设置文本域的maxlength属性以匹配校验或是在文本域后面放置一个百分号(%)用以提
示要收集一个百分比值—典型地,这种信息往往被硬编码到HTML文件中。如果你决定改变你的name属性以支持75个字符长度而不是60个字符,那么,你
需要在多少地方加入你的这一改变呢?在许多应用程序中,你将需要:
◆更新DDL以提高数据库中数据表格的列长度(通过HibernateDoclet,hbm.xml或Hibernate注解)。
◆更新Commons Validator XML文件,从而把最大值提高为75。
◆更新所有的与这个域有关的HTML表单来修改maxlength属性。
一种更好的方法是使用Hibernate
Validator。可以通过注解把校验定义添加到模型层,同时包括对处理校验的支持。如果你选择全部利用Hibernate,那么,校验器会有助于在
DAO和DBMS层提供校验。在接下来的代码示例中,你将通过使用反射和JSP
2.0标签文件把这种情况更深入一层—利用注解动态地生成视图层代码。这样以来,就不必要再在视图中进行业务逻辑硬编码。
在列表3中,dateOfBirth被注解为NotNull并且是已经过去的日期。Hibernate的DDL生成将把一个“not
null”约束添加到这一列,还将加入一个要求该日期必须是已经过去的日期的检查约束。此外,电子邮件地址也必须是“not
null”并且必须匹配电子邮件格式。这将生成一个“not null”约束但是却不生成一个匹配该格式的检查约束。
列表3.经由Hibernate注解实现的联系人信息映射
/** *一个简化的存储联系人信息的对象 */ @MappedSuperclass @Embeddable public class Contact implements Serializable { public static final int MAX_FIRST_NAME = 30; public static final int MAX_MIDDLE_NAME = 1; public static final int MAX_LAST_NAME = 30; private String fname; private String mi; private String lname; private Date dateOfBirth; private String emailAddress; private Address address; public Contact() { this.address = new Address(); } @Valid @Embedded public Address getAddress() { return address; } public void setAddress(Address a) { if (a == null) { address = new Address(); } else { address = a; } } @NotNull @Length(min = 1, max = MAX_FIRST_NAME) @Column(name = "fname") public String getFirstname() { return fname; } public void setFirstname(String fname) { this.fname = fname; } @Length(min = 1, max = MAX_MIDDLE_NAME) @Column(name = "mi") public String getMi() { return mi; } public void setMi(String mi) { this.mi = mi; } @NotNull @Length(min = 1, max = MAX_LAST_NAME) @Column(name = "lname") public String getLastname() { return lname; } public void setLastname(String lname) { this.lname = lname; } @NotNull @Past @Column(name = "dob") public Date getDateOfBirth() { return dateOfBirth; } public void setDateOfBirth(Date dateOfBirth) { this.dateOfBirth = dateOfBirth; } @NotNull @Email @Column(name = "email") public String getEmailAddress() { return emailAddress; } public void setEmailAddress(String emailAddress) { this.emailAddress = emailAddress; }
|
四、示例应用程序
本文提供了一个完整的可工作的示例应用程序(代码相当长)。其中涉及到多方面的内容,例如,多个标签文件(列表9中仅涉及到一个),还包括一个完整
的可工作的应用程序框架;还提供了一个Ant构建文件,Spring和Hibernate
XML绑定代码和一个log4j配置文件。尽管这些都不是本文讨论的重点,但是你会发现本文所提供的示例程序源代码相当有用。
如果需要的话,你也可以使用Validation注解来实现Hibernate
DAO。为此,你仅需要在hibernate.cfg.xml文件中指定基于Hibernate事件的校验即可。如果你编码相当严格,那么你可以在服务或
控制器层捕获InvalidStateException异常并遍历InvalidValue数组。
五、在控制器中加入校验功能
为了执行校验,你需要创建Hibernate的ClassValidator的一个实例。要实例化这个类需要较高的代价,所以,最好仅对你确定需要
校验的类实施这一操作。一种方法是,针对每一个模型对象创建一个工具类或存储一个ClassValidator实例和singleton,如列表4所示:
列表4.处理校验的工具类
/** *基于Hibernate Annotations Validator框架进行校验 */ public class AnnotationValidator { private static Log log = LogFactory.getLog(AnnotationValidator.class); //执行下面几行并对校验器实例进行缓冲是一种良好的编码习惯 public static final ClassValidator<Customer> CUSTOMER_VALIDATOR = new ClassValidator<Customer>(Customer.class); public static final ClassValidator<CreditCard> CREDIT_CARD_VALIDATOR = new ClassValidator<CreditCard>(CreditCard.class); private static ClassValidator<? extends BaseObject> getValidator(Class<? extends BaseObject> clazz) { if (Customer.class.equals(clazz)) { return CUSTOMER_VALIDATOR; } else if (CreditCard.class.equals(clazz)) { return CREDIT_CARD_VALIDATOR; } else { throw new IllegalArgumentException("Unsupported class was passed."); } } public static InvalidValue[] getInvalidValues(BaseObject modelObject) { String nullProperty = null; return getInvalidValues(modelObject, nullProperty); } public static InvalidValue[] getInvalidValues(BaseObject modelObject, String property) { Class<? extends BaseObject>clazz = modelObject.getClass(); ClassValidator validator = getValidator(clazz); InvalidValue[] validationMessages; if (property == null) { validationMessages = validator.getInvalidValues(modelObject); } else { //仅取得指定属性的无效值。 //例如,“city”应用于getCity()方法 validationMessages = validator.getInvalidValues(modelObject, property); } return validationMessages; } }
|
在列表4中,你创建了两个ClassValidators:一个用于Customer校验,另一个用于CreditCard校验。要进行校验的类可
以调用getInvalidValues(BaseObjectmodelObject)—它的返回值为InvalidValue[];这是一个包含模型
对象实例错误信息的数组。作为选择,你可以使用一个特定的属性名来调用这个方法;此时,它仅返回相应于该域的错误信息。
当使用Spring MVC和Hibernate Validator时,创建一个信用卡校验器已经非常简单了,详见列表5。
列表5.由Spring MVC控制器所使用的CreditCardValidator
/** *在Spring MVC实现信用卡校验 */ public class CreditCardValidator implements Validator { private CreditCardService creditCardService; public void setCreditCardService(CreditCardService service) { this.creditCardService = service; } public boolean supports(Class clazz) { return CreditCard.class.isAssignableFrom(clazz); } public void validate(Object object, Errors errors) { CreditCard creditCard = (CreditCard) object; InvalidValue[] invalids = AnnotationValidator.getInvalidValues(creditCard); //仅在上面没有出现简单错误时才进行“昂贵”的校验 if (invalids == null || invalids.length == 0) { boolean validCard = creditCardService.validateCreditCard(creditCard); if (!validCard) { errors.reject("error.creditcard.invalid"); } } else { for (InvalidValue invalidValue : invalids) { errors.rejectValue(invalidValue.getPropertyPath(), null, invalidValue.getMessage()); } } } }
|
这里的validate()方法仅需要把creditCard实例传递给校验器,从而使之返回InvalidValue数组。如果你发现若干这种简
单的错误,那么,你可以把Hibernate的InvalidValue数组翻译成Spring的Errors对象。如果用户已经创建了这个信用卡并且没
有任何简单的错误,那么,你可以把一个更为详细的校验委托给服务层去实现。这个层可以使用你的提供商处信息来验证该信用卡。
到目前为止,你应该看到把模型层注解应用于控制器层、DAO层和DBMS层的校验是相当简单的事情。现在,可以把在HibernateDoclet
和Commons
Validator中所发现的校验逻辑加入到你的模型中。尽管这是一种极受欢迎的改进,但是,视图层一直以来是最需要实现更好的校验的部分。
六、把校验添加到视图层
在接下来的例子中,我将使用Spring MVC和JSP 2.0标签文件。JSP
2.0允许定制函数在一个TLD文件内登记并在一个标签文件中进行调用。标签文件类似于taglib库,但它是以JSP代码而不是以Java代码写成的。
通过这种方式,使用Java语言编写的代码可以被封装于函数中,而使用JSP编写的代码可以被放于标签文件内。在我们的例子中,处理注解要求使用反射功
能,这将通过若干函数来实现。绑定到Spring或生成XHTML的代码将成为标签文件的一部分。
列表6中的TLD摘录定义了一个我们要使用的text.tag文件和一个称为“required”的函数:
列表6.创建表单TLD
列表7是工具类中的部分代码,其中包含了为标签文件所使用的所有函数。在前面,我已经提到把最好以Java代码方式编写的代码放于若干函数中;然
后,由TLD对它们进行映射以便进一步为标签文件所使用;所有这些函数都位于这个工具类内。因此,你需要三部分:TLD文件定义一切;在工具中定义函数;
使用这些函数的标签文件(第四部分是使用该标签文件的JSP页面)。
在列表7中,你定义了为TLD所引用的required函数和另一个指示一个给定属性是否是一个日期的方法。其实,这个工具类中还有大量内容,但篇
幅所限,在此不赘述。但是请注意,findGetterMethod()及其它方法都要执行基本的反射—除了实现从表达式语言(EL)方法表示
(customer.contact)转换成Java表示(customer.getContact())以外。
列表7.工具类中的主要代码片断
public static Boolean required(Object object, String propertyPath) { Method getMethod = findGetterMethod(object, propertyPath); if (getMethod == null) { return null; } else { return getMethod.isAnnotationPresent(NotNull.class); } } public static Boolean isDate(Object object, String propertyPath) { return java.util.Date.class.equals(getReturnType(object, propertyPath)); } public static Class getReturnType(Object object, String propertyPath) { Method getMethod = findGetterMethod(object, propertyPath); if (getMethod == null) { return null; } else { return getMethod.getReturnType(); } }
|
在此,我们看到在运行时刻使用校验注解是非常容易的。你只需简单地获取到一个对象的getter方法的引用并检查是否该方法有一个与之相关联的给定注解即可。
列表8中的JSP示例被大大简化;这样你会专注于相关的部分。其中建立了一个带有一个选择框和两个输入域的表单—所有这些域都是通过在
form.tld文件中声明的标签文件生成的。这些标签文件的设计目标是为了使用缺省值—这是考虑到在简单编码的JSP中可以根据要求指定更多的信息。这
里的关键属性是propertyPath,它使用EL标志把域映射到相应的模型层属性,就象使用Spring MVC的绑定标签实现的效果一样。
列表8.包含有一个表单的简单JSP页面
<%@ taglib tagdir="/WEB-INF/tags/form" prefix="form" %> <form method="post" action="<c:url value="/signup/customer.edit"/>"> <form:select propertyPath="creditCard.type" collection="${creditCardTypeCollection}" required="true" labelKey="prompt.creditcard.type"/> <form:text propertyPath="creditCard.number" labelKey="prompt.creditcard.number"> <img src="<c:url value="/images/icons/help.png"/>" alt="Help" Effect.SlideDown('creditCardHelp')"/> </form:text> <form:text propertyPath="creditCard.expirationDate"/> </form>
|
列表9中仅列出text.tag文件的关键部分(完整内容请参考本文下载源码文件):
列表9.标签文件text.tag摘要
< %@attribute name="propertyPath" required="true" %> <%@ attribute name="size" required="false" type="java.lang.Integer" %> <%@ attribute name="maxlength" required="false" type="java.lang.Integer" %> <%@ attribute name="required" required="false" type="java.lang.Boolean" %> <%@ taglib uri=" http://www.springframework.org/tags" prefix="spring" %> <%@ taglib uri="formtags" prefix="form" %> <c:set var="objectPath" value="${form:getObjectPath(propertyPath)}"/> <spring:bind path="${objectPath}"> <c:set var="object" value="${status.value}"/> <c:if test="${object == null}"> <%-- Bind ignores the command object prefix, so simple properties of the command object return null above. --%> <c:set var="object" value="${commandObject}"/> <%-- We depend on the controller adding this to request. --%> </c:if> </spring:bind> <%-- If user did not specify whether this field is required, query the object for this info. --%> <c:if test="${required == null}"> <c:set var="required" value="${form:required(object,propertyPath)}"/> </c:if> <c:choose> <c:when test="${required == null || required == false}"> <c:set var="labelClass" value="optional"/> </c:when> <c:otherwise> <c:set var="labelClass" value="required"/> </c:otherwise> </c:choose> <c:if test="${maxlength == null}"> <c:set var="maxlength" value="${form:maxLength(object,propertyPath)}"/> </c:if> <c:set var="isDate" value="${form:isDate(object,propertyPath)}"/> <c:set var="cssClass" value="input_text"/> <c:if test="${isDate}"> <c:set var="cssClass" value="input_date"/> </c:if> <div class="field"> <spring:bind path="${propertyPath}"> <label for="${status.expression}" class="${labelClass}"><fmt:message key="prompt.${propertyPath}"/></label> <input type="text" name="${status.expression}" value="${status.value}" id="${status.expression}"<c:if test="${size != null}"> size="${size}"</c:if> <c:if test="${maxlength != null}"> maxlength="${maxlength}"</c:if> class="${cssClass}"/> <c:if test="${isDate}"> <img id="${status.expression}_button" src="<c:url value="/images/icons/calendar.png"/>" alt="calendar" style="cursor: pointer;"/> <script type="text/javascript"> Calendar.setup( { inputField : "${status.expression}", //输入域的ID ifFormat : "%m/%d/%Y", //日期格式 button : "${status.expression}_button" //按钮的ID } ); </script> </c:if> <span class="icons"><jsp:doBody/></span> <c:if test="${status.errorMessage != null && status.errorMessage != ''}"> <p class="fieldError"><img id="${status.expression}_error" src="<c:url value="/images/icons/error.png"/>" alt="error"/>${status.errorMessage}</p> </c:if> </spring:bind> </div>
|
你一下就会看出,propertyPath是唯一要求的属性;其它的属性(如size,maxlength)可以选择性地指定。这个
objectPath变量被置为在propertyPath中引用的属性的父对象。因此,如果propertyPath是
customer.contact.fax.number,那么,objectPath将被置为customer.contact.fax。现在,你需要
使用Spring的绑定标签来绑定包含你的属性的对象。这将把你的对象变量置为一个对包含你的属性的实例的引用。接下来,你需要检查以确定这个标签的用户
是否已经指定他们要求使用你的属性。允许表单开发者重载从注解返回的值是十分重要的;这样以来,他们可以使控制器设置要求的域的缺省值(用户可能不想提供
该域的值)。如果表单开发者没有指定一个要求的值,那么你可以调用TLD形式的required函数;这个函数再去调用在TLD文件中映射的相应方法。此
方法仅执行@NotNull注解检查;如果它发现属性有这个注解,那么它会把labelClass变量按要求赋值。此外,你可以用一种类似方式来决定适当
的maxlength以及是否该域为一个日期。
接下来,你要使用Spring绑定到propertyPath,这与你前面所使用的仅包含属性的对象形成对照。这允许你在生成label和
input HTML标记时使用status.expression和status.value。这个input标签还可以根据一个给定的size,
maxlength和class而生成。如果你早些时候决定你的属性为一个Date;那么现在,你可以加入一个JavaScript日历。请注意,把标签
的属性、输入ID和图像ID链接到一起是非常简单的。这个JavaScript日历仅要求图像ID匹配input域(而且添加上一个
“_button”)。
最后,你使用一个span标签来包装<jsp:doBody/>,从而允许表单开发者添加其它图标,例如把一个帮助图标添加到页面上
(列表8展示了一个帮助图标被添加到信用卡号域)。上面最后一部分代码检查是否Spring已经打印一个针对这个属性的错误并预以显示(同时还显示一个错
误图标)。
借助于一点点CSS编码,你就可以轻松修饰你的表单域—例如,让它们以红色显示,或者在它们的文本之后添加一个星号,或者加上一个背景图像。在列表
10中,对于Firefox及其它标准兼容的浏览器的情况,你把相应的表单域的标签设置为黑色,后面跟着一个红色的星号;而对于Internet
Explorer的情况,则是把一个标志背景图像添加到Internet Explorer的左边:
列表10.修饰相应表单域的CSS代码
label.required { color: black; background-image: url( /images/icons/flag_red.png ); background-position: left; background-repeat: no-repeat; } label.required:after { content: '*'; } label.optional { color: black; }
|
其中,日期输入域的右边自动地加上一个JavaScript日历图标;另外,还设置所有文本域的适当的maxlength属性以免用户输入过长的文
本。注意,你还能够扩展text标签以设置input域的class,用于输入其它类型的数据。你还能够修改text标签以使用HTML来代替XHTML
(如果你喜欢这样做的话)。这样以来,你能够不费劲地实现优秀的HTML表单所具有的优点,并且你可以实现一个基于组件的Web框架中的许多优点而不必去
专门学习一种基于组件的框架。
尽管标签文件能够生成有助于防止错误的HTML,但是,你在视图层却没有使用任何代码来完成实际的错误检查。而借助于class属性,你可以添加一些简单的JavaScript来实现这一点,见列表11。
列表11.简单的JavaScript校验器
<script type="text/javascript"> function checkRequired(form) { var requiredLabels = document.getElementsByClassName("required", form); for (i = 0; i < requiredLabels.length; i++) { var labelText = requiredLabels[i].firstChild.nodeValue; //取得标签的文本 var labelFor = requiredLabels[i].getAttribute("for"); //取得“for”属性 var inputTag = document.getElementById(labelFor); //得到输入标签 if (inputTag.value == null || inputTag.value == "") { alert("Please make sure all required fields have been entered."); return false;//取消提交 } } return true; } </script>
|
通过在表单声明中添加onsubmit="return
checkRequired(this);"语句调用这一段JavaScript代码。这段脚本简单地提取满足要求类型的所有元素。由于你是在label
标签中使用这个类,所以后面的代码通过它的for属性查找与之相联系的input域。如果这些input域中的任何一个为空,那么,将生成一个简单的警告
消息并且放弃表单提交。另外,你还可以容易地扩展这个脚本来扫描若干种类型(class)并实现相应的校验。
最后,我建议:要实现一组全面的基于JavaScript的校验功能,最好使用一个开源框架而不是一切由你自己来实现。请看下面列表8中的代码:
Effect.SlideDown('creditCardHelp')"
|
这个函数是优秀的Script.aculo.us库(这个库实现了许多高级效果)中的一部分。如果你正在使用这个Script.aculo.us库,那么,你还会用到Prototype库(Script.aculo.us库正是基于它构建而成)。
七、总结
本文中,我们分析了如何把你的模型层中的注解用于创建你的视图、控制器、DAO及DBMS层中的校验。注意,你必须手工地创建服务层校验(例如信用
卡校验)。其它的模型层校验,例如当属性A和B处于特定状态时要求强制实现属性C;这样的情况仍然需要手工去实现。然而,借助于Hibernate注解中
的Validator组件,你可以很容易地声明和实现大部分的校验任务。
本文出自 “青峰” 博客,转载请与作者联系!
本文出自 51CTO.COM技术博客