Vincent

Vicent's blog
随笔 - 74, 文章 - 0, 评论 - 5, 引用 - 0
数据加载中……

2006年9月18日

一、 桥梁(Bridge)模式

     摘要: 一、 桥梁(Bridge)模式 桥梁模式是一个非常有用的模式,也是比较复杂的一个模式。熟悉这个模式对于理解面向对象的设计原则,包括"开-闭"原则(OCP)以及组合/聚合复用原则(CARP)都很有帮助。理解好这两个原则,有助于形成正确的设计思想和培养良好的设计风格。 注:《Java与模式》一书认为Bridge模式不是一个使用频率很高的模式,我不太赞同,我认为Bridge模式中...  阅读全文

posted @ 2006-09-18 13:38 Binary 阅读(3256) | 评论 (2)编辑 收藏

2006年9月1日

Hibernate Validator 简介

在项目的业务属性中,你是不是要经常验证属性的取值范围呢. 想要了解比较优美的解决方案吗?          

看看Hibernate Validator 是怎么做的吧.一见到她,相信你就会说: Oh God, 这就是我需要的.

任何获得Matrix授权的网站,转载请保留以下作者信息和链接:
作者:icess(作者的blog:http://blog.matrix.org.cn/page/icess)
关键字:Hibernate Validator

用Annotations 给类或者类的属性加上约束(constraint),在运行期检查属性值是很优雅的.Hibernate Validator就是这样的一个框架.该框架是十分容易的(就像参考文档中宣称的那样),几乎没有什么学习曲线,Validator 是一个验证框架 不需要和Hibernate的其他部分绑定就可以使用,只要在你的项目中添加Hibernate-annotations.jar库就可以了.那么下面就让我们看看怎么使用吧.

Person.java 类

/*
  * Created on 2006-1-12 Person.java
  * @author 
  */
package  test.annotation.validator;

import  org.hibernate.validator.Length;
import  org.hibernate.validator.Min;
import  org.hibernate.validator.Valid;
 

//@Serializability  //测试自定义约束
public class  Person {

   private  String name;
   private int  age;
   private  Address address;
  
   public  Person() {}
  
   @Valid //注意此处
   public  Address getAddress() {
     return  address;
   }
   public void  setAddress(Address address) {
     this .address = address;
   }
  
   @Min(value =  1 )
   public int  getAge() {
     return  age;
   }
   public void  setAge( int  age) {
     this .age = age;
   }
  
   @Length(min =  4 )
   public  String getName() {
     return  name;
   }
   public void  setName(String name) {
     this .name = name;
   }
}

 

Address.java 类

/*
  * Created on 2006-1-12 Address.java
  * @author 
  */
package  test.annotation.validator;

import  org.hibernate.validator.Length;
import  org.hibernate.validator.Max;
import  org.hibernate.validator.Min;

public class  Address {

   private  String street;
   private int  num;
  
   public  Address() {}
  
   @Min(value =  1 )
   @Max(value =  100 )
   public int  getNum() {
     return  num;
   }
   public void  setNum( int  num) {
     this .num = num;
   }
  
   @Length(min =  3 ,max =  8 )
   public  String getStreet() {
     return  street;
   }
   public void  setStreet(String street) {
     this .street = street;
   }
}

上面是两个用 Validator Annotations 注释的 类. 每个属性都用 约束限制了.  下面看看测试的类吧:

TestValidator.java 类

/*
  * Created on 2006-1-12
  * @author icerain
  */
package  test.annotation.validator;

import  org.hibernate.validator.ClassValidator;
import  org.hibernate.validator.InvalidValue;


public class  TestValidator {
   public void  test() {
     Address add =  new  Address();
     add.setNum( 0 );
     add.setStreet( "1" );
    
     Person p =  new  Person();
     p.setAddress(add);
     p.setAge( 0 );
     p.setName( "ice" );
    
     /******************Test validator ********/

    // 注意该处只验证了Person 为了说明 @Valid 注释的使用
     ClassValidator<Person> classValidator =  new  ClassValidator<Person> (Person. class );
     InvalidValue[] validMessages = classValidator.getInvalidValues(p);
     for  (InvalidValue value : validMessages) {
      
     System.out.println( "InvalidValue 的长度是:"  + validMessages.length
         + " . 验证消息是: "  + value.getMessage() 
         " . PropertyPath 是:"  + value.getPropertyPath()
         + " .\n\t PropertyName 是: "  +value.getPropertyName()
         "Value 是: "  + value.getValue()
         + " Bean 是: " + value.getBean()
         + "\n\t BeanClass 是:"  + value.getBeanClass());
     }
   }
  
   public static void  main(String[] args) {
     new  TestValidator().test();
   }
}

 

程序的输出如下

InvalidValue 的长度是:4 . 验证消息是: 必须大于等于 1 . PropertyPath 是:age .

PropertyName 是: age. Value 是: 0 Bean 是: test.annotation.validator.Person@dd87b2

BeanClass 是:class test.annotation.validator.Person

InvalidValue 的长度是:4 . 验证消息是: 长度必须介于 4 与 2147483647 之间 . PropertyPath 是:name .

PropertyName 是: name. Value 是: ice Bean 是: test.annotation.validator.Person@dd87b2

BeanClass 是:class test.annotation.validator.Person

InvalidValue 的长度是:4 . 验证消息是: 必须大于等于 1 . PropertyPath 是:address.num .

PropertyName 是: num. Value 是: 0 Bean 是: test.annotation.validator.Address@197d257

BeanClass 是:class test.annotation.validator.Address

InvalidValue 的长度是:4 . 验证消息是: 长度必须介于 3 与 8 之间 . PropertyPath 是:address.street .

PropertyName 是: street. Value 是: 1 Bean 是: test.annotation.validator.Address@197d257

BeanClass 是:class test.annotation.validator.Address

可以看出不满足约束的值都被指出了.

同时该句: ClassValidator<Person> classValidator = new ClassValidator<Person> (Person.class);

我们只验证了 Person. 在Person里面的Address的属性 由于有@Valid Annotations 所以 Address的相关属性也被机联验证了 .

如果 把@Valid Annotations 去掉,结果如下:

InvalidValue 的长度是:2 . 验证消息是: 必须大于等于 1 . PropertyPath 是:age .

PropertyName 是: age. Value 是: 0 Bean 是: test.annotation.validator.Person@18fef3d

BeanClass 是:class test.annotation.validator.Person

InvalidValue 的长度是:2 . 验证消息是: 长度必须介于 4 与 2147483647 之间 . PropertyPath 是:name .

PropertyName 是: name. Value 是: ice Bean 是: test.annotation.validator.Person@18fef3d

BeanClass 是:class test.annotation.validator.Person

可以看出 没有验证 Address.

当然了 ,你还可以只验证一个属性 , 没有必要验证整个类.只需要在调用 classValidator.getInvalidValues(p,"age")方法时 加上你要验证的属性就可以了.如我们只想验证age 属性 把代码改为如下所示:

InvalidValue[] validMessages = classValidator.getInvalidValues(p,"age"); / /只验证age 属性

运行结果如下:

InvalidValue 的长度是:1 . 验证消息是: 必须大于等于 1 . PropertyPath 是:age .

PropertyName 是: age. Value 是: 0 Bean 是: test.annotation.validator.Person@1457cb

BeanClass 是:class test.annotation.validator.Person

只是验证了 age 属性.

怎么样 ,很简单吧. 关于 Hibernate Validator 内建的验证Annotations 大家可以看看 API 或者 参考文档(中文版我正在翻译中 请访问我的 Blog 获得最新信息).

如果你要写自己的约束呢 , 你不用担心 ,这也是很容易的. 任何约束有两部分组成: [约束描述符 即注释]the constraint descriptor (the annotation) 和[约束validator 即 实现类] the constraint validator (the implementation class).下面我们扩展Hibernate Test suit 中的一个Test 来讲解一下.

首先: 要声明一个 constraint descriptor .如下:

package  test.annotation.validator;

import  java.lang.annotation.Documented;
import static  java.lang.annotation.ElementType.TYPE;
import static  java.lang.annotation.ElementType.FIELD;
import static  java.lang.annotation.ElementType.METHOD;
import  java.lang.annotation.Retention;
import static  java.lang.annotation.RetentionPolicy.RUNTIME;
import  java.lang.annotation.Target;

import  org.hibernate.validator.ValidatorClass;

/**
  * Dummy sample of a bean-level validation annotation
  *
  @author  Emmanuel Bernard
  */
@ValidatorClass(SerializabilityValidator. class )
@Target({METHOD,FIELD,TYPE})
@Retention(RUNTIME)
@Documented
public  @interface Serializability {
   int  num()  default  11 ;
   String message()  default  "bean must be serialiable" ;
}

@ValidatorClass(SerializabilityValidator. class ) 指出了 constraint validator 类.

@Target({METHOD,FIELD,TYPE})
@Retention(RUNTIME)
@Documented                

这几个我就不用解释了吧.

Serializability 里面声明了一个 message 显示约束的提示信息. num 只是为了说明一个方面 在这里面没有实际用途用 .

然后就是 实现一个 constraint validator 类 该类要实现Validator<ConstraintAnnotation>.这里是SerializabilityValidator.java 如下:

//$Id: SerializabilityValidator.java,v 1.3 2005/11/17 18:12:11 epbernard Exp $
package  test.annotation.validator;

import  java.io.Serializable;

import  org.hibernate.validator.Validator;

/**
  * Sample of a bean-level validator
  *
  @author  Emmanuel Bernard
  */
public class  SerializabilityValidator  implements  Validator<Serializability>, Serializable {
   public boolean  isValid(Object value) {
    //这里只是Validator 里面的 实现验证规则的 方法. value 是要验证的值.
     System.out.println( "IN SerializabilityValidator isValid:" +value.getClass()+ ": "  +value.toString());
     return  value instanceof Serializable;
  }

  public void initialize(Serializability parameters) {
    // 在这里可以 取得
constraint descriptor 里面的属性 如上面我们声明的 num
     System.out.println( "IN SerializabilityValidator: parameters:" + parameters.num() );
   }
}

然后在你的类中应用@Serializability  就可以约束一个类实现Serializable 接口了. 如下:

在我们的Person.java类 添加@Serializability  Annotations ,把Person.java 中的 //@Serializability //测试自定义约束 注释去掉就ok了.

运行结果如下:

InvalidValue 的长度是:3 . 验证消息是: bean must be serialiable . PropertyPath 是:null .

PropertyName 是: null. Value 是: test.annotation.validator.Person@1a73d3c Bean 是: test.annotation.validator.Person@1a73d3c

BeanClass 是:class test.annotation.validator.Person

现在把Person类实现 java.io.Serializable 接口 则没有出现 验证错误消息.

消息的国际化也是很简单的,把 Serializability  中的message 改为以{}扩住的 属性文件的Key就可以了

public  @interface Serializability {
   int  num()  default  11 ;
   String message()  default  "{Serializable}"; //"bean must be serialiable"; //消息的国际化
}

然后编辑资料文件. 注意 该资源文件中要包括 Hibernate Validator 内建的资源. 可以在该org\hibernate\validator\resources 包里面的资源文件基础上修改 ,在打包里面 这样就可以了. 自己打包可能不太方便.你可以把该包里面的文件复制出来.然后放到你自己的项目包下在自己编辑, 该测试中 我是放在 test\resources 包下的.

然后在 资源文件中添加 Serializable = '''''' 这么一行, 样例如下:

#DefaultValidatorMessages.properties (DefaultValidatorMessages_zh.properties 不再列出^_^)

 

#下面是 Hibernate Validator 内建的国际化消息

validator.assertFalse= assertion failed

validator.assertTrue= assertion failed

validator.future= must be a future date

validator.length= length must be between {min} and {max}

validator.max= must be less than or equal to {value}

validator.min= must be greater than or equal to {value}

validator.notNull= may not be null

validator.past= must be a past date

validator.pattern= must match "{regex}"

validator.range= must be between {min} and {max}

validator.size= size must be between {min} and {max}

#下面是自定义的消息

Serializable= Bean not Serializable  //加上自己定义的国际化消息.

在构造 ClassValidator 时要添上 资源文件 如下:(在测试类中)

ClassValidator<Person> classValidator = new ClassValidator<Person> (Person.class,ResourceBundle.getBundle("test.resources.DefaultValidatorMessages"));//加载资源

这样就可以了 .  当然 你还可以 更改 Hibernate Validator 的消息(不是在上面的资源文件中直接修改 validator.length = ... 等等 ) , 还记得 Validator 注释中有个 message 元素吗? 你以前用的都是默认值,现在你可以该为你自己定义的了. 如:validator.length 我把他改为 "该字符串的长度不符合规定范围范围". 在资源文件中添加一行键值属性对(key定义为 "myMsg")如下:

myMsg=该字符串的长度不符合规定范围范围

并且还要在 @Length 注释中提供message的引用的key 如下 @Length(min = 4,message = "{ myMsg }")

再一次运行测试 ,我们就可以看到上面两条自定义绑定的消息了 .如下:

InvalidValue 的长度是:3 . 验证消息是: Bean 不是 可 Serializable . PropertyPath 是:null .
PropertyName 是: null. Value 是: test.annotation.validator.Person@1bd4722 Bean 是: test.annotation.validator.Person@1bd4722
BeanClass 是:class test.annotation.validator.Person


InvalidValue 的长度是:3 . 验证消息是: 该字符串的长度不符合规定范围范围 . PropertyPath 是:name .
PropertyName 是: name. Value 是: ice Bean 是: test.annotation.validator.Person@1bd4722
BeanClass 是:class test.annotation.validator.Person

怎么样,比你想象的简单吧.

OK 上面我们讨论了 Hibernate Validator 的主要用法: 但是 该框架有什么用呢? ^_^

看到这里其实不用我在多说了 大家都知道怎么用,什么时候用. 作为一篇介绍性文章我还是在此给出一个最常用的例子吧,更好的使用方式大家慢慢挖掘吧.

比如 : 你现在在开发一个人力资源(HR)系统 (其实是我们ERP课程的一个作业 ^_^), 里面要处理大量的数据,尤其是在输入各种资料时 如 登记员工信息. 如果你公司的员工的年龄要求是18 -- 60 那么你所输入的年龄就不能超出这个范围. 你可能会说这很容易啊 , 不用Validator就可以解决啊.这保持数据前验证就可以啦 如if ( e.getAge() > 60 || e.getAge() < 18 ) ........ 给出错误信息 然后提示重新输入不就OK啦 用得着 兴师动众的来个第三方框架吗?

是啊 当就验证这一个属性时, 没有必要啊 ! 但是一个真正的HR 系统,会只有一个属性要验证吗? 恐怕要有N多吧

你要是每一个都那样 写一段验证代码 是不是很烦啊 ,况且也不方便代码重用. 现在考虑一些 Validator 是不是更高效啊,拦截到 约束违例的 属性 就可以直接得到 国际化的消息 可以把该消息显示到一个弹出对话框上 提示更正  !

Validator的用处不只这一种 ,你可以想到如何用呢 ! 欢迎发表你的高见!!

OK 到此 我们的 Hibernate Validator 之旅就要先告一段落了 . 希望这是令你心旷神怡的一次寒冬之旅,

把你学到的应用到你的项目中吧,一定会提高你的生产率的. 相信我 ,没错的  ^_^ !

posted @ 2006-09-01 14:05 Binary 阅读(438) | 评论 (0)编辑 收藏

Hibernate Annotations 实战(二)

-- hbm.xml 与 Annotations 性能比较

任何获得Matrix授权的网站,转载请保留以下作者信息和链接:
作者:icess(作者的blog:http://blog.matrix.org.cn/page/icess)
关键字:Hibernate Validator

我在前面一篇文章<Hibernate Annotations 实战-- 从 hbm.xml 到 Annotations>:

中,有很多开发者在谈论中提到,有没有必要从 hbm.xml 往 Annotations 上转移. 那么在这篇文章中我们就来讨论一下 hbm.xml 与 Annotations的优缺点,看看那种情况最适合你.

首先,讨论一下 xml 配置文件的优点, 个人认为主要优点就是当你改变底层配置时 不需要改变和重新编译代码,只需要在xml 中更改就可以了,例如 Hibernate.cfg.xml 当你要更改底层数据库时, 只要更改配置文件就可以了.Hibernate会为你做好别的事情.

那么xml的缺点呢,个人认为有以下几点:

  • 描述符多,不容易记忆,掌握 要深入了解还有看DTD文件

  • 无法做自动校验,需要人工查找

  • 读取和解析xml配置要消耗一定时间,导致应用启动慢,不便于测试和维护

  • 当系统很大时,大量的xml文件难以管理

  • 运行中保存xml配置需要消耗额外的内存

  • 在O/R Mapping的时候需要在java文件和xml配置文件之间交替,增大了工作量

其中第一 二点 借助于先进的IDE 可能不是什么问题. 但是对初学者还是个问题 ^_^.

 

下面我们看看 Annotations的 特性吧! 可以解决xml遇到的问题,有以下优点

  • 描述符减少。以前在xml配置中往往需要描述java属性的类型,关系等等。而元数据本身就是java语言,从而省略了大量的描述符

  • 编译期校验。错误的批注在编译期间就会报错。

  • 元数据批注在java代码中,避免了额外的文件维护工作

  • 元数据被编译成java bytecode,消耗的内存少,读取也很快,利于测试和维护

关于 映射文件是使用 hbm.xml 文件还是使用 Annotations 我们来看看2者的性能吧. 先声明一下,个人认为映射文件一旦配置好就不会在很大程度上改变了.所以使用xml文件并不会带来很大的好处.如果你认为 映射文件在你的项目中也经常变化,比如一列String数据 ,今天你使用 length="16" 明天你认为 该数据的长度应该更长才能满足业务需求 于是改为length="128" 等等类似的问题 . 如果你经常有这方面的变动的话,下面的比较你可以不用看了 , 你应该使用 xml文件 因为Annotations 无法很好的满足你的要求.

现在让我们就来看看2者的性能比较吧.

(说明: 这里只是比较查找 插入 的时间快慢,没有比较除运行时间以外的其他性能,如 内存占用量 等等)

先来看看测试程序和配置.

首先在 Hibernate.cfg.xml 文件中去掉了

<property name="hibernate.hbm2ddl.auto">update</property>

这一行, 因为在前面的实验中以及建立了数据库表了 不再需要更新了.如果你是第一次运行该例子 还是要该行的.

Test.java 如下:

/*
 * Created on 2005
 * @author 
 */
package test.hibernate.annotation;

import org.hibernate.Session;
import org.hibernate.Transaction;

public class Test {
  
  public static void main(String [] args) {
    long start = 0;
    long end = 0;
    start = System.currentTimeMillis();  //程序开始时间
    
    Session s = HibernateUtil.currentSession();
    long mid =  System.currentTimeMillis();  //初始化完毕的时间 (可能此时并没有初始化完毕^_^)
    
    Transaction tx = s.beginTransaction();    
    /********************测试读取的代码************************/
    Person p = null;
    for(int i = 1; i <= 100; i ++) {
    p = (Person) s.get(Person.class, i);
    System.out.println(p.getName());
    }
    System.out.println(p.getName());

    /********************测试读取1次的代码************************/
    Person p = null;
    p = (Person) s.get(Person.class, 1);
    System.out.println(p.getName());
    /*********************测试插入的代码*************************************/
    /*
    for (int i = 0; i < 100; i ++) {
      Person p = new Person();
      p.setAge(i+1);
      p.setName("icerain"+i);
      p.setSex("male"+i);
      s.save(p);
      s.flush();
    }
    */
    tx.commit();
    HibernateUtil.closeSession();
    
    end = System.currentTimeMillis(); //测试结束时间
    System.out.println("String[] - start time: " + start);
    System.out.println("String[] - end time: " + end);
    System.out.println("Init time : " + (mid-start)); // 打印初始化用的时间
    System.out.println("Last time is :" +(end - mid) ); //打印 数据操作的时间
    System.out.println("Total time : " +(end - start)); //打印总时间
 
 }
}

Annotations 包中的Person.java 如下

package test.hibernate.annotation;

import java.util.LinkedList;
import java.util.List;

import javax.persistence.AccessType;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.GeneratorType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;

/**
 * Person generated by hbm2java
 */

@SuppressWarnings("serial")
@Entity(access = AccessType.PROPERTY)
@Table
public class Person implements java.io.Serializable {
  private Integer id;
  private String name;
  private String sex;
  private Integer age;
  private List list = new LinkedList();

  // Constructors
  /** default constructor */
  public Person() {
  }

  /** constructor with id */
  public Person(Integer id) {
    this.id = id;
  }

  // Property accessors
  @Id(generate=GeneratorType.AUTO)
  public Integer getId() {
    return this.id;
  }

  public void setId(Integer id) {
    this.id = id;
  }

  @Basic
  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }

  @Basic
  public String getSex() {
    return this.sex;
  }

  public void setSex(String sex) {
    this.sex = sex;
  }

  @Basic
  public Integer getAge() {
    return this.age;
  }

  public void setAge(Integer age) {
    this.age = age;
  }
  @Transient
  public List getList() {
    return list;
  }
  public void setList(List list) {
    this.list = list;
  }

}

其他的代码几乎没有改变:

下面的每种类型的测试都测试了3次以上, 取中间的测试时间.

测试机器配置:

CPU:  AMD Athlon (xp) 2000+

内存: 784880KB

硬盘: 三星 SP0812N

读取一次  的比较:(单位: 毫秒)

使用Annotations 的测试数据使用Xml文件的测试数据简要说明
Init time : 2444Init time : 2431测试前我认为该项结果xml应该比较大,要读取映射文件啊,实际情况不是这样,不知道为什么?
Last time is :62Last time is :85相差比较大不知道为什么?
Total time : 2506Total time : 2516xml文件总体上慢了一点

   读取100次的比较:

使用Annotations 的测试数据使用Xml文件的测试数据简要说明
Init time : 2437Init time : 2422和前面初始化差不多
Last time is :438Last time is :484有时间差
Total time : 2875Total time : 2906也是xml文件总体上慢了一点

插入100次的比较:

使用Annotations 的测试数据使用Xml文件的测试数据简要说明
Init time : 2453Init time : 2469和前面初始化差不多
Last time is :469Last time is :656有时间差
Total time : 2922Total time : 3125也是xml文件总体上慢了一点

从上面的三次对比中大家可以看到 初始化的部分几乎两者是一样的, 在数据操作上面 使用xml文件 总是比使用Annotations 慢一点.在我们只操纵一个只有几个属性的小持久化类的操作中就有 几十毫秒的差距. 几十毫秒在计算机中算不算很大 大家应该都知道,我就不在多说了.

总结: 经过 xml 文件 和Annotations 的优缺点和 性能上的对比.现在使用那个作为你持久化映射策略.我相信大家都会正确选择的.

测试后记: 经过多次测试 感觉有时候很不稳定 ,有的时候很稳定不知道是测试有问题还是别的问题.大家可以自己测试一下. 有什么新的发现 请大家讨论讨论.

posted @ 2006-09-01 14:04 Binary 阅读(321) | 评论 (0)编辑 收藏

第一个Hibernate with Annotation程式

Hibernate是ORM的解决方案,其底层对数据库的操作依赖于JDBC,所以您必须先取得JDBC驱动程序,在这边所使用的是MySQL,所以您必须至 MySQL® Connector/J 取得MySQL的JDBC驱动程序。

接下来至
Hibernate 官方网站 取得Hibernate 3.2Hibernate Annotations 3.2

您必须安装JDK 5.0才可以使用Hibernate Annotations的功能。

解开Hibernate 3.2的zip档案后,当中的hibernate3.jar是必要的,而在lib目录中还包括了许多jar档案,您可以在 Hibernate 3.0官方的参考手册 上找到这些jar的相关说明,其中必要的是 antlr、dom4j、CGLIB、asm、Commons Collections、Commons Logging、 EHCache,Hibernate底层还需要Java Transaction API,所以您还需要jta.jar。

解开Hibernate Annotations 3.2的zip档案后,您需要hibernate-annotations.jar、ejb3-persistence.jar这两个档案。

到这边为止,总共需要以下的jar档案:


Hibernate可以运行于单机之上,也可以运行于Web应用程序之中,如果是运行于单机,则将所有用到的jar档案(包括JDBC驱动程序)设定至CLASSPATH中,如果是运行于Web应用程序中,则将jar档案置放于WEB-INF/lib中。

如果您还需要额外的Library,再依需求加入,例如JUnit、Proxool等等,接下来可以将etc目录下的 log4j.properties复制至Hibernate项目的Classpath下,并修改一下当中的 log4j.logger.org.hibernate为error,也就是只在在错误发生时显示必要的讯息。

接着设置基本的Hibernate配置文件,可以使用XML或Properties档案,这边先使用XML,档名预设为hibernate.cfg.xml:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
  "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
  "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
 
<hibernate-configuration>
    <session-factory>
        <!-- 显示实际操作数据库时的SQL -->
        <property name="show_sql">true</property>
        <!-- SQL方言,这边设定的是MySQL -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
        <!-- JDBC驱动程序 -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <!-- JDBC URL -->
        <property name="connection.url">jdbc:mysql://localhost/demo</property>
        <!-- 数据库使用者 -->
        <property name="connection.username">root</property>
        <!-- 数据库密码 -->
        <property name="connection.password">123456</property>
 
        <!-- 以下设置对象与数据库表格映像类别 -->
        <mapping class="onlyfun.caterpillar.User"/>
    </session-factory>
</hibernate-configuration>

这边以一个简单的单机程序来示范Hibernate的配置与功能,首先作数据库的准备工作,在MySQL中新增一个demo数据库,并建立user表格:
CREATE TABLE user (
id INT(11) NOT NULL auto_increment PRIMARY KEY,
name VARCHAR(100) NOT NULL default'',
age INT
);
对于这个表格,您有一个User类别与之对应,表格中的每一个字段将对应至User实例上的Field成员。

package onlyfun.caterpillar;
 
import javax.persistence.*;
 
@Entity
@Table(name="user") // 非必要,在表格名称与类别名称不同时使用
public class User {
  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
    private Integer id;
 
  @Column(name="name") // 非必要,在字段名称与属性名称不同时使用
    private String name;
 
  @Column(name="age")
    private Integer age; // 非必要,在字段名称与属性名称不同时使用
   
    // 必须要有一个预设的建构方法
    // 以使得Hibernate可以使用Constructor.newInstance()建立对象
    public User() {
    }
 
    public Integer getId() {
        return id;
    }
 
    public void setId(Integer id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
   
    public Integer getAge() {
        return age;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }
}

其中id是个特殊的属性,Hibernate会使用它来作为主键识别,您可以定义主键产生的方式,这边设定为自动产生主键,可以看到,实体标识,主键生成,以及相关映像,都可以使用Annotation来完成。

接下来撰写一个测试的程序,这个程序直接以Java程序设计人员熟悉的语法方式来操作对象,而实际上也直接完成对数据库的操作,程序将会将一笔数据存入表格之中:
package onlyfun.caterpillar;
 
import org.hibernate.SessionFactory;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
 
public class HibernateAnnotationDemo {
 
    public static void main(String[] args) {
        // 需要AnnotationConfiguration读取Annotation讯息
        Configuration config = new AnnotationConfiguration().configure();
        // 根据 config 建立 SessionFactory
        // SessionFactory 将用于建立 Session
        SessionFactory sessionFactory = config.buildSessionFactory();
 
        // 将持久化的物件
        User user = new User();
        user.setName("caterpillar");
        user.setAge(new Integer(30));    
 
        // 开启Session,相当于开启JDBC的Connection
        Session session = sessionFactory.openSession();
        // Transaction表示一组会话操作
        Transaction tx= session.beginTransaction();
        // 将对象映像至数据库表格中储存
        session.save(user);
        tx.commit();
        session.close();
        sessionFactory.close();
      
        System.out.println("新增资料OK!请先用MySQL观看结果!");
    }
}

注意,使用Annotation时,需要的是AnnotationConfiguration类别。

如您所看到的,程序中只需要直接操作User对象,并进行Session与Transaction的相关操作,Hibernate就会自动完成对数据库的操作,您看不到任何一行JDBC或SQL的陈述,撰写好以上的各个档案之后,各档案的放置位置如下:


接着可以开始运行程序,结果如下:
Hibernate: insert into user (name, age) values (?, ?)
新增资料OK!请先用MySQL观看结果!

执行结果中显示了Hibernate所实际使用的SQL,由于这个程序还没有查询功能,所以要进入MySQL中看看新增的数据,如下:
mysql> select * from user;
+----+-----------------+------+
| id    | name         | age  |
+----+-----------------+------+
|  1    | caterpillar  | 30   |
+----+-----------------+------+
1 row in set (0.03 sec)

posted @ 2006-09-01 14:00 Binary 阅读(278) | 评论 (0)编辑 收藏

Hibernate Annotations 实战

     摘要: 任何获得Matrix授权的网站,转载请保留以下作者信息和链接: 作者:icess(作者的blog:http://blog.matrix.org.cn/page/icess)关键字:Hibernate Validator 下面让我们先看一个通常用 hbm.xml 映射文件的例子. 有3个类 .HibernateUtil.java 也就是 Hibernate文档中推荐的工具类,Pers...  阅读全文

posted @ 2006-09-01 13:59 Binary 阅读(388) | 评论 (0)编辑 收藏

在filter中關閉session

利用Thread-Specific Storage撰寫一個HibernateUtil

HibernateSessionUtil.java
								import java.io.Serializable;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.Transaction;

public class HibernateSessionUtil implements Serializable
{
publicstaticfinal ThreadLocal tLocalsess = new ThreadLocal();

publicstaticfinal ThreadLocal tLocaltx = new ThreadLocal();

/*
* getting the thread-safe session for using
*/
publicstatic Session currentSession(){
Session session = (Session) tLocalsess.get();

//open a new one, if none can be found.
try{
if (session == null){
session = openSession();
tLocalsess.set(session);
}
}catch (HibernateException e){
thrownew InfrastructureException(e);
}
return session;
}

/*
* closing the thread-safe session
*/
publicstatic void closeSession(){

Session session = (Session) tLocalsess.get();
tLocalsess.set(null);
try{
if (session != null && session.isOpen()){
session.close();
}

}catch (HibernateException e){
thrownew InfrastructureException(e);
}
}

/*
* begin the transaction
*/
publicstatic void beginTransaction(){
Transaction tx = (Transaction) tLocaltx.get();
try{
if (tx == null){
tx = currentSession().beginTransaction();
tLocaltx.set(tx);
}
}catch (HibernateException e){
thrownew InfrastructureException(e);
}
}

/*
* close the transaction
*/
publicstatic void commitTransaction(){
Transaction tx = (Transaction) tLocaltx.get();
try{
if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack())
tx.commit();
tLocaltx.set(null);
}catch (HibernateException e){
thrownew InfrastructureException(e);
}
}

/*
* for rollbacking
*/
publicstatic void rollbackTransaction(){
Transaction tx = (Transaction) tLocaltx.get();
try{
tLocaltx.set(null);
if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack()){
tx.rollback();
}
}catch (HibernateException e){
thrownew InfrastructureException(e);
}
}

privatestatic Session openSession() throws HibernateException{
return getSessionFactory().openSession();
}

privatestatic SessionFactory getSessionFactory() throws HibernateException{
return SingletonSessionFactory.getInstance();
}
}

 filter中的程式碼如下

HibernateSessionCloser.java
								public class HibernateSessionCloser implements Filter{

protected FilterConfig filterConfig = null;

public void init(FilterConfig filterConfig)throws ServletException{
this.filterConfig = filterConfig;
}

public void destroy(){
this.filterConfig = null;
}

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
try{
chain.doFilter(request, response);
}
finally{
try{
HibernateSessionUtil.commitTransaction();
}catch (InfrastructureException e){
HibernateSessionUtil.rollbackTransaction();
}finally{
HibernateSessionUtil.closeSession();
}
}

}
}

然後在操作資料庫之前加上

HibernateSessionUtil.beginTransaction();
HibernateSessionUtil.currentSession();//取得Session

posted @ 2006-09-01 13:51 Binary 阅读(445) | 评论 (0)编辑 收藏

acegi-security-sample-contacts-filter例子学习(二)

功能实现分析

这个例子使用了HSQL做数据库,spring的AOP作为基础,使用Acegi做安全控制组件。
联系人管理的web应用在启动时候,会做一系列初始化动作:
1. 读取web.xml文件,

2. 并解析文件里的内容。
a) context-param元素。
i. contextConfigLocation属性。这个属性定义了spring所需要的3个属性文件。它们分别是:applicationContext -acegi-security.xml、applicationContext-common-business.xml、 applicationContext-common-authorization.xml
ii. log4jConfigLocation属性。这个属性定义了log4j配置文件。

b) filter元素。
这里定义了acegi的一个过滤器。Acegi的大部分过滤器都是这样配置的。使用FilterToBeanProxy组件,给它传递一个targetClass属性。这个targetClass必须实现javax.servlet.Filter接口。
这里配置的是FilterChainProxy。这个FilterChainProxy比较好用,可以为它定义一串filter属性。这些filter将会按照定义的顺序被调用。例如,
<bean id="filterChainProxy" class="net.sf.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,basicProcessingFilter,rememberMeProcessingFilter,anonymousProcessingFilter,securityEnforcementFilter
</value>
</property>
</bean>
这个过滤器的mapping是“/*”。
c) listener元素。
i. ContextLoaderListener。这个是Spring使用来加载根applicationcontext。并分别解析 applicationContext-acegi-security.xml、applicationContext-common- business.xml、applicationContext-common-authorization.xml等配置文件,把相关的对象初始化
iii. Log4jConfigListener。这个是spring用来初始化log4j组件的listener。
iv. HttpSessionEventPublisher。这个组件将发布HttpSessionCreatedEvent和HttpSessionDestroyedEvent事件给spring的applicationcontext。
d) servlet元素。
i. contacts。这里采用了spring的MVC框架, 所以这个servlet是spring MVC的一个核心控制器(org.springframework.web.servlet.DispatcherServlet)。这个servlet 启动时候,会从contacts-servlet.xml里面读取信息,并做相关的初始化。
v. remoting。也是spring MVC的一个核心控制器。与contacts不同,这个servlet主要是提供web services服务。这个servlet启动时候, 会从remoting-servlet.xml里面读取信息,并做相关的初始化。
e) taglib元素。这里定义了spring的标f) 签库。
3. 解析applicationContext-acegi-security.xml。
a) 过滤器链。定义了一个FilterChainProxy,b) 并指c) 定了一系列的过滤器链。httpSessionContextIntegrationFilter, authenticationProcessingFilter,basicProcessingFilter,rememberMeProcessingFilter,anonymousProcessingFilter,securityEnforcementFilter
d) 认证管理器。这个管理器由acegi提供。这个管理器需要一个providers参数。这个providers参数包含了提供系统认证的对象。
i. daoAuthenticationProvider。一般用户认证。
ii. anonymousAuthenticationProvider。匿名用户认证。
iv. rememberMeAuthenticationProvider。记住我认证。

e) 密码加密。这里定义了一个acegi的Md5算法加密对象Md5PasswordEncoder。
f) 定义了一个jdbcDao实现类。这个类由acegi提供的net.sf.acegisecurity.providers.dao.jdbc.JdbcDaoImpl。这个对象需要一个dataSource的参数。
g) 定义daoAuthenticationProvider。这个对象由acegi提供。它有3个属性:
authenticationDao。这里指向前面定义的jdbcDao。
userCache。这里指向后面定义的user缓存对象。
passwordEncoder。这里指向前面定义的密码加密对象。
h) 用户缓存管理。
为了缓存user,这里使用spring的ehcache来缓存user。缓存机制:
i. 定义缓存管理器――CacheManager。这个对象是spring的EhCacheManagerFactoryBean对象
ii. 定义user缓存实际执行对象――UserCacheBackend。这个对象是spring的EhCacheFactoryBean。它有两个属性:
1. cacheManager。这里指向前面定义的缓存管理器。
2. cacheName。
iii. 定义user缓存――UserCache。它是acegi提供的EhCacheBasedUserCache对象。它有一个属性:
1. cache。这里指向的是前面定义的userCacheBackend。

i) 定义接收来自DaoAuthenticationProvider的认证事件的listener――LoggerListener。
j)
4. 解析applicationContext-common-business.xml。
a) dataSource.
这里使用了spring的DriverManagerDataSource对象。这个对象是一个JDBC数据源的定义。
b) TransactionManager。这里使用spring的DataSourceTransactionManager对象。
c) 事务拦截器。这里使用spring的事务拦截器TransactionInterceptor。它有2个属性:
transactionManager。这个属性指向前面定义的TransactionManager。
transactionAttributeSource。这个属性里, 指定了ContactManager的各个方法的事务方面的要求。
d) DataSourcePopulator。
使用sample.contact.DataSourcePopulator对象,往HSQL里创建相关的表结构和数据。
实现原理:DataSourcePopulator 实现了接口 InitializingBean。其中afterPropertiesSet方法将在spring初始化DataSourcePopulator后被调用。
e) ContactDao。这里指向一个ContactDaoSpring对象。它继承spring的 JdbcDaoSupport,g) 并实现ContactDao接口。它是真正实现JDBC操作的对象。
h) ContactManager。这里使用的是spring的ProxyFactoryBean。它有2个属性:
i. ProxyInterfaces。代理接口:sample.contact.ContactManager

ii. InterceptorNames。拦截器名称。可以有多个,iv. 这里包括:transactionInterceptor、contactManagerSecurity、contactManagerTarget。其中,v. transactionInterceptor是前面定义的事务拦截器。ContactManagerSecurity则是在 applicationContext-common-authorization.xml里定义的方法调用授权。
i) ContactManagerTarget。这里指向的是sample.contact.ContactManagerBackend对象。 ContactManagerBackend实现了ContactManager接口和InitializingBean接口。它有2个自定义属性: contactDao和basicAclExtendedDao。这里会调用ACL的API去做些创建权限和删除权限的工作。

posted @ 2006-09-01 13:45 Binary 阅读(873) | 评论 (0)编辑 收藏

acegi-security-sample-contacts-filter例子学习(一)

这是一个 Acegi 官方的例子。它以联系人的管理为例子,说明如何使用 Acegi 作权限控制。这个例子包含在 acegi 的包里面。下载地址: http://prdownloads.sourceforge.net/acegisecurity/acegi-security-0.8.3.zip?download

联系人管理说明了下列中心的Acegi安全控制能力:

  • Role-based security (基于角色的安全) ――每个责任人都是某个角色的一员。而角色被用来限制对某些安全对象的访问。
  • Domain object instance security (域对象实例安全) ――合同,这个系统里的主要域对象,拥有一个访问控制列表( ACL ),用来指明谁允许读、管理和删除对象。
  • Method invocation security (方法调用安全)―― 这个 ContactManager 服务层对象 包含一些受保护的和公开的方法。
  • Web request security Web 请求安全) ――这个“ /secure URI 路径被使用 Acegi 安全保护,使得没有 ROLE_USER 角色的用户无法访问。 .
  • Security unaware application objects (保护未知的应用对象) ――受保护的对象与 Acegi 之间没有明显的耦合或契约,所以它们没有察觉到安全是由 Acegi 提供的。 *
  • Security taglib usage (安全标签库使用) ――所有的 JSP 使用 Acegi 安全标签库来封装安全信息。 *
  • Fully declarative security( 完全声明式的安全 ) ――每一个安全方面特性都是在 application context 里面使用标准的 Acegi 安全对象来配置的。 *
  • Database-sourced security data (支持数据库来源的安全数据) ――所有的用户、角色和 ACL 信息都可以从一个兼容 JDBC 的内存数据库获得。
  • Integrated form-based and BASIC authentication (集成基于表单和 BASIC 验证)―― 任何 BASIC 验证头部被检测以及作为验证使用。默认使用基于表单的普通交互式验证。
  • Remember-me services (记住我的服务)―― Acegi 安全的插件式的“ remember-me 策略被演示。在登录表单里有一个相关的选择框与之对应。

联系人管理的业务功能描述:

1.1. 每个用户登录后,可以看到一个联系人列表。例如,

marissa's Contacts

id

Name

Email

1

John Smith

john@somewhere.com

Del

Admin Permission

2

Michael Citizen

michael@xyz.com



3

Joe Bloggs

joe@demo.com

Del


4

Karen Sutherland

karen@sutherland.com

Del

Admin Permission

Add

说明:用户没有权限访问的联系人信息,将不会显示。

2.2. 用户可以增加新的联系人信息。

3.3. 如果有删除权限,用户可以看到在联系人后面有一个“ Del ”链接。用户可以点击这个链接来删除某个联系人信息。

4.4. 如果有管理权限,用户可以看到在联系人后面有一个“ Admin Permission ”链接。用户可以点击这个链接来管理访问这个联系人的权限。例如,

Administer Permissions

sample.contact.Contact@26807f: Id: 1; Name: John Smith; Email: john@somewhere.com

-R--- [2] dianne

Del

-RW-D [22] peter

Del

A---- [1] marissa

Del

Add Permission Manage

说明:每一行记录包含有 3 列。

第一列表示权限,例如,“ -RW-D ”表示可读、可写、可删除。

第二列也表示权限,但它是以类似 unix 权限的数字表达。例如,“ [22] , 表示可读、可写、可删除。

第三列是用户名称。

每一行记录后面都有一个“ Del ”链接。点击这个链接,可以删除掉指定用户对这个联系人信息的权限。

5.5. 用户可以为某个联系人信息添加权限。例如,

Add Permission

Contact:

sample.contact.Contact@1787005: Id: 1; Name: John Smith; Email: john@somewhere.com


Recipient:


Permission:


说明:权限是动态添加的。例如,上图中给用户 scott 增加了读联系人 John 的权限。那么 scott 马上就可以看到联系人 John 的信息了。

posted @ 2006-09-01 13:44 Binary 阅读(618) | 评论 (0)编辑 收藏

WebWork教程-ServletDispatcher

     摘要: ServletDispatcher 原理 ServletDispatcher 是默认的处理 Web Http 请求的调度器,它是一个 JavaServlet ,是 WebWork 框架的控制器。...  阅读全文

posted @ 2006-09-01 13:41 Binary 阅读(634) | 评论 (0)编辑 收藏

WebWork教程-validator

验证框架
WebWork 提供了在 Action 执行之前,对输入数据的验证功能,它使用了其核心 XWork 的验证框架。提供了如下功能:
1、   可配置的验证文件。它的验证文件是一个独立的 XML 配置文件,对验证的添加、修改只需更改配置文件,无需编译任何的 Class
2、   验证文件和被验证的对象完全解藕。验证对象是普通的 JavaBean 就可以了(可以是 FormBean 、域对象等),它们不需实现任何额外的方法或继承额外的类。
3、   多种不同的验证方式。因为它验证功能是可以继承的,所以可以用多种不同的方式指定验证文件,比如:通过父类的 Action 、通过 Action 、通过 Action 的方法、通过 Action 所使用的对象,等等。
4、   强大的表达式验证。它使用了 OGNL 的表达式语言,提供强大的表达式验证功能。
5、   同时支持服务器端和客户端验证。
下面我们来看看如何为用户注册添加验证功能:
1、   注册我们的验证类型
WebWork 为不同的验证要求提供不同的验证类型。一个验证类型,一般是有一个类来提供。这个类必须实现接口: com.opensymphony.xwork.validator.Validator ,但我们在写自己的验证类型时,无需直接实现 Validator 接口,它有抽象类可供直接使用如 ValidatorSupport FieldValidatorSupport 等。
验证类型在使用之前,必须要在 ValidatorFactory com.opensymphony.xwork.validator . ValidatorFactory )中 注册。可以有二种方法实现验证类型的注册。一、写程序代码进行注册,它使用 ValidatorFactory 类的静态方法: registerValidator(String name, String className) 二、使用配置文件 validators.xml 进行注册,要求把文件 validators.xml 放到 ClassPath 的跟目录中( /WEB-INF/classes )。但在实际开发中,一般都使用第二中注册方法。我们的验证类型注册如下:
<validators>
    <validator name="required" class="com.opensymphony.xwork.validator.validators.RequiredFieldValidator"/>
    <validator name="requiredstring" class="com.opensymphony.xwork.validator.validators.RequiredStringValidator"/>
    <validator name="int" class="com.opensymphony.xwork.validator.validators.IntRangeFieldValidator"/>
    <validator name="date" class="com.opensymphony.xwork.validator.validators.DateRangeFieldValidator"/>
    <validator name="expression" class="com.opensymphony.xwork.validator.validators.ExpressionValidator"/>
    <validator name="fieldexpression" class="com.opensymphony.xwork.validator.validators.FieldExpressionValidator"/>
    <validator name="email" class="com.opensymphony.xwork.validator.validators.EmailValidator"/>
    <validator name="url" class="com.opensymphony.xwork.validator.validators.URLValidator"/>
    <validator name="visitor" class="com.opensymphony.xwork.validator.validators.VisitorFieldValidator"/>
    <validator name="conversion" class="com.opensymphony.xwork.validator.validators.ConversionErrorFieldValidator"/>
    <validator name="stringlength" class="com.opensymphony.xwork.validator.validators.StringLengthFieldValidator"/>
</validators>
注册验证类型的配置文件非常简单。它使用标签 <validator > 提供名-值对的形式注册。这样我们的验证文件就可以直接引用它的名字。
2、   开启 Action 的验证功能
  如果 Action 要使用验证框架的验证功能,它必须在配置文件中指定拦截器“ validation ”,它的定义如下:
<interceptor name="validation" class="com.opensymphony.xwork.validator.ValidationInterceptor"/>
我们的验证文件必须以 ActionName-validation.xml 格式命名,它必须被放置到与这个 Action 相同的包中。你也可以为这个 Action 通过别名的方式指定验证文件,它的命名格式为: ActionName-aliasname-validation.xml 。“ ActionName ”是我们 Action 的类名;“ aliasname ”是我们在配置文件( xwork.xml )中定义这个 Action 所用到的名称。这样,同一个 Action 类,在配置文件中的不同定义就可以对应不同的验证文件。验证框架也会根据 Action 的继承结构去查找 Action 的父类验证文件,如果找到它会去执行这个父类的验证。
 
3、   实现我们的验证文件: RegisterActionSupport-validation.xml
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd">
<validators>
    <field name="user.username">
    <field-validator type="requiredstring">
            <message>You must enter a value for username.</message>
        </field-validator>
    </field>
    <field name="user.password">
    <field-validator type="requiredstring">
            <message>You must enter a value for password.</message>
        </field-validator>
        <field-validator type="fieldexpression">
            <param name="expression">user.password == verifyPassword</param>
            <message>Passwords don't match.</message>
        </field-validator>
    </field>
    <field name="user.email">
    <field-validator type="email">
            <message>You must enter a valid email.</message>
        </field-validator>
    </field>
    <field name="user.age">
    <field-validator type="int">
            <param name="min">6</param>
            <param name="max">100</param>
            <message>Age must be between ${min} and ${max}, current value is ${user.age}.</message>
        </field-validator>
    </field>
</validators>
说明:
1 )、 <field > 标签代表一个字段,它的属性“ name ”和页面输入框的“ name ”属性必需完全一致,其实它也就是我们的表达式语言。
2 )、 <field-validator > 标签定义我们的验证规则, type 属性的值就是就是我们前面定义的验证类型。
3 )、验证文件中,字段的数据是通过表达式语言从我们的值堆栈( OgnlValueStack )中取得,一般是 Action Model 对象。例如:我们的字段“ user.age ”,它会通过 Action getUser().getAge() 来取得用户输入的年龄,再来根据验证的类型“ int ”和最大值最小值的参数来判断输入的数据是否能通过验证。
4 )、不管验证是否通过,我们的 Action 都会执行,但如果验证没有通过,它不会调用 Action execute() 方法。
 
4、   显示 Action 的验证错误信息
如果用户输入的数据验证没有通过,我们需重新返回输入页面,并给出错误信息提示。拦截器栈“ validationWorkflowStack ”为我们实现了这个功能。它首先验证用户输入的数据,如果验证没有通过将不执行我们 Action execute() 方法,而是将请求重新返回到输入页面。
我们的 xwork.xml 配置文件如下:
<action name="registerSupport" class="example.register.RegisterActionSupport">
            <result name="success" type="dispatcher">
                <param name="location">/register-result.jsp</param>
            </result>
            <result name="input" type="dispatcher">
                <param name="location">/registerSupport.jsp</param>
            </result>
            <interceptor-ref name="validationWorkflowStack"/>
        </action>
 
通过接口 ValidationAware 我们可以获得类级别或字段级别的验证错误信息,这个错误信息也就是我们验证文件中 <message> 标签里的数据。 ActionSupport 类已实现了此接口,这样在应用中我们的 Action 只要继承 ActionSupport 类就可以了。 RegisterActionSupport .java 代码如下:
package example.register;
 
import com.opensymphony.xwork.ActionSupport;
 
public class RegisterActionSupport extends ActionSupport {
 
    private User user= new User();
    private String verifyPassword;
   
    public User getUser(){
        returnthis.user;
    }
   
    public String execute(){
        // 在这里调用用户注册的业务逻辑,比如:将注册信息存储到数据库
        return SUCCESS;
    }
 
    public String getVerifyPassword(){
        returnthis.verifyPassword;
    }
   
    publicvoid setVerifyPassword(String verPassword){
        this.verifyPassword = verPassword;
    }
}
我们 WebWork UI 标签库直接提供了验证错误信息显示功能。如果字段级别的验证没有通过,它会在输入框上方显示验证文件定义的错误提示信息。我们将用户输入的页面更改如下:
registerSupport.jsp
<%@ taglib uri="webwork" prefix="ww" %>
<html>
<head><title>Register Example</title></head>
<body>
<table border=0 width=97%>
<tr><td align="left">
    <ww:form name="'test'" action="'/example/registerSupport.action'" method="'POST'">
            <ww:textfield label="'Username'" name="'user.username'" required="true"/>
            <ww:textfield label="'Password'" name="'user.password'" required="true"/>
            <ww:textfield label="'VerifyPassword'" name="'verifyPassword'" required="true"/>
            <ww:textfield label="'Email'" name="'user.email'" required="true"/>
            <ww:textfield label="'Age'" name="'user.age'" required="true"/>
            <ww:submit value="'Submit'"/>
         </ww:form>
</td></tr>
</table>
</body>
</html>
我们上面的例子使用的是服务器端验证。 WebWork 也为我们提供了方便的客户端验证。它将验证自动生成 JavaScript 脚本。如果要使用客户端验证只需改变相应的验证类型就可以了(输入页面的表单必需使用 <ww:form> 标签,并设置属性“ validate="true" ”)。具体的验证类型可以在 WebWork 的包 com.opensymphony.webwork.validators 中找到。

posted @ 2006-09-01 13:40 Binary 阅读(659) | 评论 (0)编辑 收藏