论坛首页 Java企业应用论坛

使用Hibernate元数据构建表单校验功能

浏览 4527 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-07-18   最后修改:2012-07-18

      表单数据的检验是应用程序必不可少的组成部分,通常来讲,表单数据的校验分为前台校验和后台校验两部分,前台校验使用JS脚本在提交数据前进行校验,后台校验在数据提交后进行校验。

      出于安全性的考虑,后台校验是必不可少的,当因为某种原因前台校验失效的情况下,由于有后台校验的存在,仍能保证提交数据的合法性和有效性。

      后台校验有许多种方法,有基于配置文件的校验,有基于注释的校验等等,也有许多成熟的校验框架可以使用。本文向大家介绍一种基于Hibernate元数据的校验方法,笔者曾在多个项目中使用,效果非常好,在这里与大家分享。

      应用本文提到的校验方法前应该满足以下几点:

  • 该程序持久层使用Hibernate。
  • 页面上的表单与数据库表有对应关系,且表单数据字段名与数据库字段名一致。
  • 最好使用了Spring。
      另外,使用基于Hibernate元数据的校验只能保证校验到字段是否为空、数据类型是否正确、字段长度是否符合三种规则。由于本文提到的方法是采用拦截器的方式在业务层前置进行校验,所以与其它的校验工具之间并不冲突,可以形成有效互补。
public class ValidationInterceptor extends HandlerInterceptorAdapter {

	@Resource(name = "&sessionFactory")
	private LocalSessionFactoryBean sessionFactory;

	@Override
	@SuppressWarnings("unchecked")
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		final String uri = request.getRequestURI();
		//通过请求的URI找到对应的校验实体类,这个需要在外部进行配置
		final String entityName = getValidation(uri);
		//没有配置说明这个请求不需要校验
		if (entityName == null)
			return true;
		//通过实体名返回Hibernate的PersistentClass
		final PersistentClass clazz = sessionFactory.getConfiguration().getClassMapping(entityName);
		//对应的表的元数据
		final Table table = clazz.getTable();
		final Iterator<Column> iterator = table.getColumnIterator();
		final Map<String, String> errors = new HashMap<String, String>();
		while (iterator.hasNext()) {
			//对应的列的元数据
			final Column column = iterator.next();
			//列名
			final String name = column.getName();
			//这是数据库列映射到JAVA中的属性
			final Property property = clazz.getProperty(name);
			//这是前台表单提交上来的值
			final String value = request.getParameter(name);
			//如果值为空,跳过该字段的校验
			if (value == null)
				continue;
			//校验非空
			if (!column.isNullable() && "".equals(value.trim())) {
				errors.put(name, "字段"+name+"的值不能为空!");
			} 
			//校验字符串长度
			else if (property.getType() instanceof StringType) {
				if (value.length() > column.getLength()) {
					errors.put(name, "字段"+name+"的值超长了!");
				}
			} 
			//校验日期类型
			else if (property.getType() instanceof DateType) {
				final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
				try {
					format.parse(value);
				} catch (Exception e){
					errors.put(name, "字段"+name+"必须是日期类型!");
				}
			} 
			//校验数字类型,包括浮点数
			else if (property.getType() instanceof BigDecimalType) {
				int precision = column.getPrecision();
				int scale = column.getScale();
				int front = precision - scale;
				String patten = "";
				for (int i = 0; i < front; i++) {
					patten += "#";
				}
				if (scale > 0) {
					patten += ".";
					for (int i = 0; i < scale; i++) {
						patten += "#";
					}
				}
				final NumberFormat format = new DecimalFormat(patten);
				try {
					format.parse(value);
				} catch (Exception e) {
					errors.put(name, "字段"+name+"必须是数字类型("+patten"+")!");
				}
			}
		}
		//如果没有错误,直接放行
		if (errors.isEmpty())
			return true;
		//有错了,生成描述错误信息的JSON串,返回前台
		response.setContentType("text/json;charset=UTF-8");
		response.getWriter().write(JSONObject.fromObject(errors).toString());
		response.getWriter().close();
		return false;
	}
}
       每个URL需要映射一下校验数据使用的实体类,可以使用一个Propertes文件进行配置,上代码中的getValidation方法需要从这个配置文件中取信息。
/test/save_log.do=com.coolfancy.blog.entity.Log
/test/save_reply.do=com.coolfancy.blog.entity.Reply
 
      使用这个方法后,基于数据库元数据的校验就不需要再在程序中写了,使用这个拦截器就全搞定了,如果有其它复杂的校验,可以在Action中使用原来的校验方法继续校验。

      更多精彩原创文章请关注笔者的原创博客:http://www.coolfancy.com
   发表时间:2012-07-19  
看样子是使用了Spring MVC,感觉你就是重复造轮子了,Spring MVC完美的支持了Hibernate Validation, 楼主可以去了解下。
0 请登录后投票
   发表时间:2012-07-23  
yxb1990 写道
看样子是使用了Spring MVC,感觉你就是重复造轮子了,Spring MVC完美的支持了Hibernate Validation, 楼主可以去了解下。

可能是我孤陋寡闻了,请问Spring MVC+Hibernate Validation能作到在Action中不用写注释就直接借用Hibernate实体的校验规则完成同名字段的校验吗?
0 请登录后投票
   发表时间:2012-07-23  
fancy888 写道
yxb1990 写道
看样子是使用了Spring MVC,感觉你就是重复造轮子了,Spring MVC完美的支持了Hibernate Validation, 楼主可以去了解下。

可能是我孤陋寡闻了,请问Spring MVC+Hibernate Validation能作到在Action中不用写注释就直接借用Hibernate实体的校验规则完成同名字段的校验吗?

完全可以。请仔细看一下JSR303的具体规范。如果页面是JSF2的话,JS验证也可以直接完成而不用写。
0 请登录后投票
   发表时间:2012-07-23  
魔力猫咪 写道
fancy888 写道
yxb1990 写道
看样子是使用了Spring MVC,感觉你就是重复造轮子了,Spring MVC完美的支持了Hibernate Validation, 楼主可以去了解下。

可能是我孤陋寡闻了,请问Spring MVC+Hibernate Validation能作到在Action中不用写注释就直接借用Hibernate实体的校验规则完成同名字段的校验吗?

完全可以。请仔细看一下JSR303的具体规范。如果页面是JSF2的话,JS验证也可以直接完成而不用写。

我对JSR303规范也有个大概的了解,刚才又百度了一下,这个规范貌似只是用来校验Bean的,和Hibernate没有任何关系。
我们在Spring MVC Action中接收请求参数使用的是自己封装的BEAN,类似于Struts中的ActionForm,这样作是为了更好的与持久层隔离。
如果在Action中接收参数直接使用Hibernate实体BEAN的话当然可以实现直接集成实体BEAN的校验,这一点我知道。
请问有没有具体文章可以参考一下,如何能实现在Action中校验任意类型的Bean套用Hibernate实体映射的校验规则(按字段名同名配置原则)?
0 请登录后投票
   发表时间:2012-07-25  
fancy888 写道
yxb1990 写道
看样子是使用了Spring MVC,感觉你就是重复造轮子了,Spring MVC完美的支持了Hibernate Validation, 楼主可以去了解下。

可能是我孤陋寡闻了,请问Spring MVC+Hibernate Validation能作到在Action中不用写注释就直接借用Hibernate实体的校验规则完成同名字段的校验吗?

不好意思,没看完就下结论了,今天又仔细看了下,你应该是使用了Hibernate的配置文件或者是注解来实现验证的吧?但感觉还是有点多此一举了,就算你不用拦截器去验证数据,hibernate 也还是会利用这些配置约束对数据进行验证的,可能就是错误信息没这么详细了,另外不是很明白"能作到在Action中不用写注释就直接校验"是什么意思,按照我的理解,我们不是在Action层对bean 加@Valid注解?
0 请登录后投票
   发表时间:2012-07-26  
yxb1990 写道
fancy888 写道
yxb1990 写道
看样子是使用了Spring MVC,感觉你就是重复造轮子了,Spring MVC完美的支持了Hibernate Validation, 楼主可以去了解下。

可能是我孤陋寡闻了,请问Spring MVC+Hibernate Validation能作到在Action中不用写注释就直接借用Hibernate实体的校验规则完成同名字段的校验吗?

不好意思,没看完就下结论了,今天又仔细看了下,你应该是使用了Hibernate的配置文件或者是注解来实现验证的吧?但感觉还是有点多此一举了,就算你不用拦截器去验证数据,hibernate 也还是会利用这些配置约束对数据进行验证的,可能就是错误信息没这么详细了,另外不是很明白"能作到在Action中不用写注释就直接校验"是什么意思,按照我的理解,我们不是在Action层对bean 加@Valid注解?

呵呵,我的意思就是在Action里传递的参数是自定义的Java Bean(类似于Struts的ActionForm),因为架构上要求持久化层完全隔离,不能在Action里直接使用实体类作为参数,所以没法使用@Valid来直接调用Hibernate的校验逻辑。加上这一层校验也是为了提升用户体验(想作页面微错误提示效果),必竟拿到500页面对用户来说太不友好。如果自己用Hibernate Validator来写注释的话感觉是重复劳动,因为这部分校验逻辑的元数据Hibernate已经定义过了,所以想到了使用拦截器。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics