<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>郁闷的长亭听雨</title>
    <description></description>
    <link>http://geszjava.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>测试爬虫</title>
        <author>geszJava</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://geszjava.javaeye.com">geszJava</a>&nbsp;
          链接：<a href="http://geszjava.javaeye.com/blog/115360" style="color:red;">http://geszjava.javaeye.com/blog/115360</a>&nbsp;
          发表时间: 2007年08月22日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>测试爬虫</p>
<p>测试爬虫</p>
<p>测试爬虫</p>
<p>测试爬虫</p>
<p>测试爬虫</p>
<p>测试爬虫</p>
<p>测试爬虫</p>
<p>测试爬虫</p>
<p>测试爬虫</p>
          <br/>
          <span style="color:red;">
            <a href="http://geszjava.javaeye.com/blog/115360#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 22 Aug 2007 20:26:16 +0800</pubDate>
        <link>http://geszjava.javaeye.com/blog/115360</link>
        <guid>http://geszjava.javaeye.com/blog/115360</guid>
      </item>
      <item>
        <title>grails0.22压力测试</title>
        <author>geszJava</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://geszjava.javaeye.com">geszJava</a>&nbsp;
          链接：<a href="http://geszjava.javaeye.com/blog/33715" style="color:red;">http://geszjava.javaeye.com/blog/33715</a>&nbsp;
          发表时间: 2006年11月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          grails0.3终于发布了,不过好像改了很多东西,正在看文档. <br />grails0.22的压力测试报告 <br />CPU: Pentium 2.66G D <br />OS:WindowsXP <br />JDK:1.5.0_07-b03 <br />AppServer: Tomcat 5.5 <br />DB:Mysql 5.0.22 <br />可能服务器只买一台,所以测试的时候数据库服务器和web服务器放一起的 <br />request per second 大约是16, 客户端开100个线程.<br />每个页面的sql大约是10-20,数据库的数据少量,全部采用分页.<br />tomcat的thread大约是100,最大内存是500M,机器内存是512M,运行时候内存稳定在670M左右. <br />CPU占用率是100%[这个没办法,数据库和web服务器放一起],每个页面完成时间大约是2-5s.<br /><br /><pre name="code" class="java">Web Application Stress Tool Version:1.1.293.1  
  
Number of test clients:       1  
  
Number of hits:               5126  
Requests per Second:          17.09  
  
Socket Statistics   
--------------------------------------------------------------------------------   
Socket Connects:              7097  
Total Bytes Sent (in KB):     1790.19  
Bytes Sent Rate (in KB/s):    5.97  
Total Bytes Recv (in KB):     71989.15  
Bytes Recv Rate (in KB/s):    239.96  
  
Socket Errors   
--------------------------------------------------------------------------------   
Connect:                      0  
Send:                         0  
Recv:                         0  
Timeouts:                     0  
  
RDS Results   
--------------------------------------------------------------------------------   
Successful Queries:           0  
  
Script Settings   
================================================================================   
Server:                       server   
Number of threads:            100  
  
Test length:                  00:05:00  
Warmup:                       00:00:00  
Cooldown:                     00:00:00  
  
Use Random Delay:             Yes   
Min Delay Time:               0  
Max Delay Time:               3000  
  
Follow Redirects:             Yes   
Max Redirect Depth:           30  
  
Clients used in test   
================================================================================   
localhost   
  
Clients not used in test   
================================================================================   
  
Result Codes   
Code      Description                   Count        
================================================================================   
200       OK                            5126         
  
Page Summary   
Page                            Hits      TTFB Avg  TTLB Avg  Auth      Query        
================================================================================   
GET /rw/base/loginAll           369       2452.69   2452.69   No        No           
GET /rw/information/home        365       5123.72   5154.94   No        No           
GET /rw/information/showCat/1   351       3133.90   6419.70   No        No           
GET /rw/information/showCat/2   339       2882.94   6218.54   No        No           
GET /rw/information/showCat/3   328       3134.97   7031.73   No        No           
GET /rw/information/showCat/8   324       3045.27   3045.63   No        No           
GET /rw/information/showCat/9   320       3213.80   3214.14   No        No           
GET /rw/information/showCat/10  318       3265.87   3266.26   No        No           
GET /rw/userResume/handlePerso  312       5540.86   5580.46   No        No           
GET /rw/userResume/showUserRes  308       4479.56   4494.86   No        No           
GET /rw/userResume/searchWantA  300       3983.56   3988.89   No        No           
GET /rw/companyUser/home        298       173.18    3488.33   No        No           
GET /rw/insideJobUser/home      291       126.57    3712.41   No        No           
GET /rw/cooperativeUser/home    286       71.25     3732.51   No        No           
GET /rw/information/showCat/6   311       3055.65   3061.80   No        No           
GET /rw/information/showCat/7   306       2919.74   2920.11   No        No    </pre>
          <br/>
          <span style="color:red;">
            <a href="http://geszjava.javaeye.com/blog/33715#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 11 Nov 2006 16:58:48 +0800</pubDate>
        <link>http://geszjava.javaeye.com/blog/33715</link>
        <guid>http://geszjava.javaeye.com/blog/33715</guid>
      </item>
      <item>
        <title>grails的命名策略和数据库连接</title>
        <author>geszJava</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://geszjava.javaeye.com">geszJava</a>&nbsp;
          链接：<a href="http://geszjava.javaeye.com/blog/24135" style="color:red;">http://geszjava.javaeye.com/blog/24135</a>&nbsp;
          发表时间: 2006年08月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          grails直接使用hibernate的ImprovedNamingStrategy,虽然说是个不错的选择,不过有时候我们的工作不会象框架设计者想的那么简单.作为一个ORM,提供可配置命名策略是必要的.不过在grails中,如果你不想修改grails源代码那么就只能使用hibernate的默认命名策略.这是一个非常糟糕的设计...因为在GrailsDomainBinder这个类中是这样定义命名策略的:private static final NamingStrategy namingStrategy = ImprovedNamingStrategy.INSTANCE;很郁闷的使用了硬编码,而不是象DataSource那样直接写到groovy文件中进行配置.<br />下面这段代码是从ApplicationDataSource.groovy中摘出来的,分别是mysql和sqlserver的连接方法<br />[code:1]<br />//for mysql<br />//设置数据库为utf-8,选择第二项<br />//charsetfilter为utf-8,groovy文件为ansi<br />//用非jdbc客户端连的时候需要先设置:SET NAMES 'gbk'<br />//相当于:SET character_set_client='utf8'<br />//SET character_set_connection='utf8'<br />//SET character_set_results='utf8'<br /><br />   boolean pooling = true<br />   String dbCreate = "create-drop" // one of 'create', 'create-drop','update'<br />   //String url = "jdbc:mysql://localhost/test"<br />   String url = "jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=utf-8"<br />   String driverClassName = "com.mysql.jdbc.Driver"<br />   String username = "test"<br />   String password = "test"<br /><br />   def dialect = MySQLDialect.class<br />/*<br />//for sql server<br />//设置sqlserver为简体中文<br />   boolean pooling = true<br />   String dbCreate = "create-drop" // one of 'create', 'create-drop','update'<br />   String url = "jdbc:microsoft:sqlserver://127.0.0.1:1433"<br />   String driverClassName = "com.microsoft.jdbc.sqlserver.SQLServerDriver"<br />   String username = "test"<br />   String password = "test"<br />   def dialect = SQLServerDialect.class<br />*/<br /><br />[/code:1]
          <br/>
          <span style="color:red;">
            <a href="http://geszjava.javaeye.com/blog/24135#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 29 Aug 2006 07:31:34 +0800</pubDate>
        <link>http://geszjava.javaeye.com/blog/24135</link>
        <guid>http://geszjava.javaeye.com/blog/24135</guid>
      </item>
      <item>
        <title>给grails添加text映射支持</title>
        <author>geszJava</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://geszjava.javaeye.com">geszJava</a>&nbsp;
          链接：<a href="http://geszjava.javaeye.com/blog/24132" style="color:red;">http://geszjava.javaeye.com/blog/24132</a>&nbsp;
          发表时间: 2006年08月27日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          在grails中,domain class的String字段总是被映射成varchar,当然可以自己写xml映射文件解决这个问题,不过没有一劳永逸的解决方法了吗?于是开始搜索grails的论坛,发现这个特性还没有实现,开发人员倒是给出了一个issue:在constraints中实现type约束: propertyname(type:"text"),很郁闷了,还是自己来修改一下grails的源代码吧.<br />由于要在constraints中添加type约束,那么需要实现一个可以识别type的约束类,如下(在org.codehaus.groovy.grails.validation.ConstrainedProperty中):<br />先添加一个约束类字符串的声明, 反正他也不会用在validate,就在校验处理的时候什么也不作好了:<br />public static final String MAPPING_TYPE_CONSTRAINT = "type";<br />记得把这个类添加到map中<br />constraints.put(MAPPING_TYPE_CONSTRAINT, MappingTypeConstraint.class);<br /><br />	static class MappingTypeConstraint extends AbstractConstraint {<br />		private String mapping_type;<br />		<br />		public boolean supports(Class type) {<br />			if (type == null)<br />				return false;<br />			return String.class.isAssignableFrom(type);<br />		}<br /><br />		/*<br />		 * (non-Javadoc)<br />		 * <br />		 * @see org.codehaus.groovy.grails.validation.ConstrainedProperty.AbstractConstraint#setParameter(java.lang.Object)<br />		 */<br />		public void setParameter(Object constraintParameter) {<br />			if (!(constraintParameter instanceof Comparable))<br />				throw new IllegalArgumentException(<br />						"Parameter for constraint ["<br />								+ MAPPING_TYPE_CONSTRAINT<br />								+ "] of property ["<br />								+ constraintPropertyName<br />								+ "] of class ["<br />								+ constraintOwningClass<br />								+ "] must implement the interface [java.lang.Comparable]");<br /><br />			this.mapping_type = (String) constraintParameter;<br />			super.setParameter(constraintParameter);<br />		}			<br /><br />		public String getType() {<br />			return this.mapping_type;<br />		}<br />		<br />		public String getName() {<br />			return MAPPING_TYPE_CONSTRAINT;<br />		}<br /><br />		protected void processValidate(Object target, Object propertyValue,<br />				Errors errors) {<br />		}<br />	}<br /><br />还要添加两个方法:<br />	public Comparable getType() {<br />		MappingTypeConstraint c = (MappingTypeConstraint) this.appliedConstraints<br />				.get(MAPPING_TYPE_CONSTRAINT);<br />		if (c == null) {<br />			Range r = getRange();<br />			if (r == null) {<br />				return null;<br />			} else {<br />				return r.getFrom();<br />			}<br />		}<br />		return c.getType();<br />	}<br />	<br />	public void setType(String type) {<br />		if (!String.class.isInstance(propertyType)) {<br />			throw new MissingPropertyException(<br />					"Mapping type constraint can only be applied to a String property",<br />					MAPPING_TYPE_CONSTRAINT, owningClass);<br />		}<br /><br />		Constraint c = (Constraint) this.appliedConstraints<br />				.get(MAPPING_TYPE_CONSTRAINT);<br />		if (type == null) {<br />			this.appliedConstraints.remove(MAPPING_TYPE_CONSTRAINT);<br />		} else {<br />			if (c != null) {<br />				c.setParameter(type);<br />			} else {<br />				c = new MatchesConstraint();<br />				c.setOwningClass(this.owningClass);<br />				c.setPropertyName(this.propertyName);<br />				c.setParameter(type);<br />				this.appliedConstraints.put(MAPPING_TYPE_CONSTRAINT, c);<br />			}<br />		}<br />	}<br />再添加个方法,偷懒点,只考虑"text"这种映射, 实际上我也只需要"text"映射:<br />	public boolean hasMapTypeConstraints() {<br />		return appliedConstraints.containsKey(MAPPING_TYPE_CONSTRAINT);<br />	}<br />好了,前期准备工作基本上做好了,<br />然后去org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsDomainBinder<br />修改一下bindSimpleValue方法就ok了:<br /><br />	private static void bindSimpleValue(GrailsDomainClassProperty grailsProp, SimpleValue simpleValue,Mappings mappings) {<br />		// set type<br />		<br />		simpleValue.setTypeName(grailsProp.getType().getName());<br /><br />		//添加text映射支持<br />		Map cs = (Map)grailsProp.getDomainClass().getConstrainedProperties();<br />		if (cs != null) {<br />			ConstrainedProperty cp = (ConstrainedProperty)cs.get(grailsProp.getName());<br />			if (cp != null && cp.hasMapTypeConstraints()) {<br />				simpleValue.setTypeName(Hibernate.TEXT.getName());<br /><br />				System.out.println("TEXT Mapping: for =>" + grailsProp.getName());<br />			} <br />	<br />		}<br /><br />		<br />		Table table = simpleValue.getTable();<br />		Column column = new Column();<br />		if(grailsProp.isManyToOne())<br />			column.setNullable(false);<br />		<br />		column.setValue(simpleValue);<br />		bindColumn(grailsProp, column);<br />								<br />		if(table != null) table.addColumn(column);<br />		<br />		simpleValue.addColumn(column);		<br />	}<br /><br />ok,重新打包放到grails的dist目录下面,这样我们就可以在grails的domainclass中使用这样的约束:<br />def constraints = {<br />	content(length:10..20000,blank:false, type:"text")<br />}<br />于是content的类型就映射到了Hibernate.Text上面去了.而不是原先的varchar
          <br/>
          <span style="color:red;">
            <a href="http://geszjava.javaeye.com/blog/24132#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 27 Aug 2006 01:45:33 +0800</pubDate>
        <link>http://geszjava.javaeye.com/blog/24132</link>
        <guid>http://geszjava.javaeye.com/blog/24132</guid>
      </item>
      <item>
        <title>GORM翻译</title>
        <author>geszJava</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://geszjava.javaeye.com">geszJava</a>&nbsp;
          链接：<a href="http://geszjava.javaeye.com/blog/24098" style="color:red;">http://geszjava.javaeye.com/blog/24098</a>&nbsp;
          发表时间: 2006年08月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          第一次翻译,翻译的不好,希望大牛们可以校正,说实话grails关注的人实在太少,哎,现在弄的缺乏信心了.<br /><br />Grails : GORM <br />Grails Object Relational Mapping (GORM)<br />介绍<br />域对象是任何商业应用的核心. 这些对象保持商业过程的状态同样也实现行为. 他们通过one-to-one或者one-to-many等关系相互连接起来.<br /> GORM是Grails对象关系映射(ORM) 的实现. 他使用Hibernate 3 引擎(一个非常流行和灵活的开源ORM 解决方法),GORM同时支持动态和静态的域对象创建,在缺省情况下,Grails采用动态的域对象创建,这样可以不用写hibernate的配置文件.<br />你同样可以使用Java写一个Grails的域对象,[参见 Hibernate Integration 这里介绍了怎样用java写一个grails域对象但是仍然可以使用域对象的动态方法]<br />创建一个域对象<br />Grails中一个域对象从本质上来说是一个带有id和version属性的Groovy类.<br />Book.groovy<br />class Book {<br />    Long id<br />    Long version<br /><br />    String title<br />}<br />你可以在你的Grails项目的根目录下面使用如下命令快捷的创建一个域对象定义:<br />grails create-domain-class<br />创建关系<br />定义一个一对多的关系就是简单的定义一个Set类型的属性,然后把属性加入到” relatesToMany”这个map中:<br />Author.groovy<br />class Author {<br />    Long id<br />    Long version<br /><br />    def relatesToMany = [ books : Book ]<br /><br />    String name<br />    Set books = new HashSet()<br />}<br />Groovy一个主要的特性是他支持静态键入,这就意味这我们不需要把one-to-one, many-to-one关联放入relationships这个map中:<br />Book.groovy<br />class Book {<br />    Long id<br />    Long version<br /><br />    Author author<br />    String title<br />}<br />one-to-many拥有者就是定为one的这一端 (在上面的例子中是 "Author.groovy"), 但如果你创建了一个one-to-one的映射,那么定义这个关联关系的拥有者就非常重要了. 例如如果我们有一个Author类,它没有和Book定义关联关系, (ie in a one-to-one scenario), 那么Grails会假定Book是Author的拥有者.实际上这是非常严重的错误. 因为这意味着当你删除一个Book实例的时候,这个删除操作同样会去删除该实例对应的Author,因为拥有者是Book而不是Author.这不是我们要得到的结果. <br />因此我们必需在one-to-one或者many-to-one这样的关系中定义一个被拥有这样的属性. 在Grails中我们在被拥有者这一端定义一个"belongsTo" 属性:<br />Book.groovy<br />class Book {<br />    Long id<br />    Long version<br /><br />    def belongsTo = Author<br /><br />    Author author<br />    String title<br />}<br />如下,一个域对象可以同时被多个对象拥有:<br />@Property belongsTo = [Author,Publisher]<br />换句话说:<br />•	一个拥有者能够拥有其他域对象 <br />•	被拥有这使用'belongsTo' 属性指向他的拥有者 <br />•	如果一个拥有者被删除了,所有属于他的被拥有者也将消失 <br />关系概要<br />下面这个表格定义了两个类A和B之间可能的关联关系<br />关系可能是单向的 (->) 或者双向的(&lt;->),也拥有关系的类型one-to-one (1:1), one-to-many (1:m), many-to-one (m:1), and many-to-many (n:m).<br />拥有者的字体是粗体.<br />A:B 	单向 	双向 <br />1:1 	A -> B 	A &lt;-> B ; B.belongsTo = [A] <br />1:m 	A -> Set ; A.relatesToMany= [B] 	A -> Set ; A.relatesToMany = [B]; B -> A  <br /><br />m:1 	A -> B 	A -> B ; B -> Set ; B.relatesToMany = [A]  <br /><br />n:m 	Grails中未实现	Grails中未实现<br /> 在one-to-many中如果one这端是拥有者的话那么,不需要belongsTo 定义<br /> opposite of  <br />映射继承<br />GORM使用 table-per-heirarchy 继承,从本质上来说父对象和所有子对象共享共用一张表:<br />class Content {<br />     Long id<br />     Long version<br /><br />     String author<br />}<br />class BlogEntry extends Content {<br />    URL url<br />}<br />class Book extends Content {<br />    String ISBN<br />}<br />class PodCast extends Content {<br />    byte[] audioStream<br />}<br />上面的定义允许你运行多态查询:<br />def content = Content.findAll() // find all blog entries, books and pod casts<br />content = Content.findAllByAuthor('Joe Bloggs') // find all by author<br /><br />def podCasts = PodCast.findAll() // find only pod casts<br /> <br />Technical note for those interested: Under the covers, only one table is used in the table-per-hierarchy model. A class column specifies the subclass and any fields in the subclasses are included on the same table. Subclass fields cannot be "required" because the underlying columns need to be nullable for the other subclasses. This doesn't however prevent you from using Validation constraints to ensure that subclass fields are "required".<br />技术提示:在继承关系中所有对象只有一张表,所有父类属性和子类属性都放在这张表里面,子类的属性不能是”require”因为他有可能在其他子类中是可以为空的,但是这并不妨碍你对子类的字段使用校验条件”constraints”使用required条件<br />Optional(可选的) 和Transient(瞬时的)属性<br />在缺省情况下,所有的属性都是persistent(可持久化) 和required(必需的),你可以使用名为”optionals”(可选的)和”transients”(瞬时的)类型为List属性来改变他:<br />Book.groovy<br />class Book {<br />    Long id<br />    Long version<br /><br />    def optionals = [ "releaseDate" ]<br />    def transients = [ "digitalCopy" ]<br /><br />    Author author<br />    String title<br />    String author<br />    Date releaseDate<br />    File digitalCopy<br />}<br />CRUD 操作<br />Grails域对象使用动态的持久化方法容易的在域对象上进行CRUD (Create/Read/Update/Delete)操作:<br />Create<br />为了在数据库中创建一个域对象的实例,域对象可以使用save方法,save方法可以把关系中的其他对象也级联保存. 在下面这个例子中,我们仅仅调用一个author的”save”方法,那么Author和Book实例都将存储到数据库中:<br />def a = new Author(name:"Stephen King")<br />def b = new Book(title:"The Shining",author:b)<br />a.books.add(b)<br /><br />// persist<br />a.save()<br />注意:在通常情况下,推荐使用在你的域对象中添加一个方法管理域对象关系,而不是象上面那样直接加入一个实例到”books”这个Set中.例如下面的例子中就添加了一个addBook方法:<br />def addBook(book) {<br />   if(!books)books = new HashSet()<br />   book.author = this<br />   books.add(book)<br />   return this<br />}<br />GORM 将会管理你的域对象数据存储到数据库, 但是不会自动为你管理对象之间的关系的优化.因此代码将变成:<br />def a = new Author(name:"Stephen King")<br />a.addBook(new Book(title:"The Shining"))<br /> .addBook(new Book(title:"IT"))<br /> .save()<br />Read<br />Grails中有很多中方式得到域对象实例, 更多的信息可以参见 Domain Class Querying, 然而如果我们知道域对象实例的id属性,那么我们可以用get静态方法来得到:<br />Book.get(1)<br />或者使用 "findAll", "list" 或 "listOrderByX" 来获取多个对象:<br />Book.findAll() // retrieve all<br />   Book.list(10) // lists first 10 instances<br />   Book.listOrderByTitle() // lists all the instances ordered by the "title" property<br />Update<br />更新一个域对象实例和save方法不同,  他可能不会明确调用save方法来保存. 如果没有异常发生,那么对象所做的修改将会被自动保存到数据库[但是这个对象必需是从数据库获取的,否则必需显式调用save,因为在调用save的时候,如果数据库里没有这个对象,会多执行一跳sql语句获取对象的主键]:<br />def b = Book.get(1)<br />b.releaseDate = new Date()<br />这种行为可能不是一直是我们所希望,特别是当我们和validation条件结合的情况下, (可能你不希望你的域对象实例没有经过检验就存到数据库中.因此当你直接调用save方法的时候,如果对象不符合检验条件,那么他将不会被存入数据库:<br />def b = Book.get(1)<br />b.title = null // can't have a null title<br />b.save() // won't save as fails to validate<br />有时,我们可能需要在校验失败的时候仍然保存对象到数据库,那么我们可以显式调用validate方法来校验:<br />def b = Book.get(1)<br />b.publisher = "Print It"<br /><br />if(!b.validate()) {<br />   b.publisher = Publisher.DEFAULT<br />}<br />在上面的例子中,对象在校验失败的时候仍然保存到数据库. 如果你希望validate方法在校验失败的时候不存入数据库,你可以使用validate(true):<br />b.validate(true)<br />可选的是,如果你向明确控制对象的持久化(保存到数据库)你可以使用discard方法,他将会把对象从持久化框架中剔除,从而不把对象修改保存到数据库中:<br />def b = Book.get(1)<br />b.title = "A New Title"<br /><br />// something happenedd to change your mind<br />b.discard()<br />Delete<br />域对象实例能使用delete方法从数据库中删除掉:<br />def b = Book.get(1)<br />b.delete()<br />域对象查询<br />使用动态方法查询<br />Grails有很多种方法通过动态方法查询域对象的实例, 详细的情况参考 DomainClass Dynamic Methods:<br />def results = Book.findByTitle("The Stand")<br /><br />results = Book.findByTitleLike("Harry Pot%")<br />results = Book.findByReleaseDateBetween( firstDate, secondDate )<br />results = Book.findByReleaseDateGreaterThan( someDate )<br />results = Book.findByTitleLikeOrReleaseDateLessThan( "%Something%", someDate )<br /><br />// find by relationship<br />results = Book.findAllByAuthor( Author.get(1) )<br />使用实例查询<br />你可以使用find方法查询域对象仅仅通过传递一个域对象实例.<br />def b = Book.find( new Book(title:'The Shining') )<br />使用criteria builder查询(criteria builder是hibernate架构的一种便捷查询方式)<br />对于高级的查询或者域对象交叉查询可以使用Criteria (详见 Builders):<br />def c = Book.createCriteria()<br />def results = c {<br />     like("author.name", "Stephen%")<br />     between("releaseDate", firstDate, secondDate )<br />}<br />使用HQL语言查询(HQL:Hibernate Query Language[hibernate查询语言])<br />另外,可以使用hibernate本身的查询语言HQL:<br />def results = Book.find("from Book as b where b.title like 'Lord of the%'")<br />或者带有参数:<br />def results = Book.find("from Book as b where b.title like ?", ["The Shi%"])<br /> <br /><br />Document generated by Confluence on Jul 14, 2006 09:01
          <br/>
          <span style="color:red;">
            <a href="http://geszjava.javaeye.com/blog/24098#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 17 Aug 2006 14:31:01 +0800</pubDate>
        <link>http://geszjava.javaeye.com/blog/24098</link>
        <guid>http://geszjava.javaeye.com/blog/24098</guid>
      </item>
      <item>
        <title>在grails job中碰到的问题的解决</title>
        <author>geszJava</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://geszjava.javaeye.com">geszJava</a>&nbsp;
          链接：<a href="http://geszjava.javaeye.com/blog/24093" style="color:red;">http://geszjava.javaeye.com/blog/24093</a>&nbsp;
          发表时间: 2006年08月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          想在job中直接调用velocity,于是把velocity-1.4.jar放在projectName/lib下面,写好job的源代码,然后启动,却出现classloader问题,提示class找不到.很郁闷的,但是不要紧,grails的job支持注入,于是在spring的配置文件中加入bean定义:<br />[code:1]<br />	&lt;bean id="velocityHelper" class="com.rw.framework.util.velocity.VelocityHelper"<br />		singleton="true" init-method="init" autowire="byName"><br />	&lt;/bean><br />[/code:1]<br />由于是byName,那么job的代码便是:<br />[code:1]<br />import com.rw.framework.util.velocity.VelocityHelper<br /><br />class TestJob {<br />	def VelocityHelper velocityHelper;<br /><br />	def startDelay = "1000"<br />	def timeout = "10000"<br />	<br />	def name = "TestJob"<br />	def group = "rwGroup"<br /><br /><br />	def execute() {	<br />		def context = velocityHelper.getContext()<br />		context.put("userName", "rw")<br />		System.out.println(velocityHelper.merge(context, modifyPath("test.vm")));<br />	}<br />}<br /><br />[/code:1]<br />启动,可以看到velocityHelper已经被注入job,并且可以流畅的执行了.在job中也可以注入service,只不过我们都不用在applicationContext.xml中自己定义了,grails很体贴的把这部分xml在启动的时候动态的加载到SpringConfig中了.同样,controller中也可以类似的调用.
          <br/>
          <span style="color:red;">
            <a href="http://geszjava.javaeye.com/blog/24093#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 15 Aug 2006 15:26:40 +0800</pubDate>
        <link>http://geszjava.javaeye.com/blog/24093</link>
        <guid>http://geszjava.javaeye.com/blog/24093</guid>
      </item>
      <item>
        <title>Grails的Ajax</title>
        <author>geszJava</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://geszjava.javaeye.com">geszJava</a>&nbsp;
          链接：<a href="http://geszjava.javaeye.com/blog/24092" style="color:red;">http://geszjava.javaeye.com/blog/24092</a>&nbsp;
          发表时间: 2006年08月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          grails支持ajax,目前可以使用三种库: prototype,yahoo,dojo,当然也可以自己在taglib中添加支持其他的库的功能.<br />在grails中创建一个ajax调用还是比较简单的,首先创建一个Controller:DemoCnotroller.groovy<br />[code:1]<br />class DemoController {<br />	def ajaxCaller = {<br />		return [:]<br />	}<br />	def ajaxTest = {<br />		return ["str":"示例"]<br />	}<br />}<br />[/code:1]<br />再在grails-app/views/demo下面创建一个view:<br />ajaxTest.gsp<br />[code:1]<br />这是一个Ajax${str}<br />[/code:1]<br />然后我们创建一个ajax调用的页面,同样也是在grails-app/views/demo下面,名字就叫ajaxCaller.gsp<br />[code:1]<br />&lt;html><br />&lt;head><br />	&lt;meta http-equiv="Content-Type" content="text/html; charset=gb2312"/><br />	&lt;title>ajax测试页面&lt;/title><br />	&lt;g:javascript library="prototype" /><br />&lt;/head><br />&lt;body><br />	&lt;div class="body"><br />	&lt;g:if test="${flash['message']}"><br />		&lt;div class="message">${flash['message']}&lt;/div><br />	&lt;/g:if><br />	&lt;g:hasErrors bean="${user}"><br />		&lt;div class="errors"><br />			&lt;g:renderErrors bean="${user}" as="list" /><br />		&lt;/div><br />	&lt;/g:hasErrors><br /><br />	&lt;div id="mess"><br />	这是原先的信息<br />	&lt;/div><br />	&lt;g:remoteLink action="ajaxTest" update="mess">ajax测试&lt;/g:remoteLink><br />&lt;/body><br />&lt;/html><br />[/code:1]<br />然后启动grails,输入http://localhost:8080/projectName/demo/ajaxCaller,然后按一下ajax测试这个链接,是不是出来ajax的调用内容了?<br />在grails中,ajax调用其实可以看做一个action调用.值得注意的是在action中render函数目前对中文支持不好,最好不要使用他.
          <br/>
          <span style="color:red;">
            <a href="http://geszjava.javaeye.com/blog/24092#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 15 Aug 2006 15:12:56 +0800</pubDate>
        <link>http://geszjava.javaeye.com/blog/24092</link>
        <guid>http://geszjava.javaeye.com/blog/24092</guid>
      </item>
      <item>
        <title>Grails的Javascript验证DIY</title>
        <author>geszJava</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://geszjava.javaeye.com">geszJava</a>&nbsp;
          链接：<a href="http://geszjava.javaeye.com/blog/24088" style="color:red;">http://geszjava.javaeye.com/blog/24088</a>&nbsp;
          发表时间: 2006年08月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          看到Grails的validation标签居然是没实现,一下子大受打击!很郁闷,但是没办法,偶们还是自己来实现一下这个重要的功能把.<br />Grails的tag其实一般都在\grails-app\taglib下面,可以自行修改源程序来修改tag的表现.validation标签就在ValidationTagLib.groovy这个文件里面.只要修改一下代码就可以了,由于实现过程比较繁杂,直接贴出源代码,需要注意的是:<br />第一个地方:” import org.geszjava.grails.utils.ChineseProperty as CP;”这条语句,偶是自己写一个类来进行属性名和表单说明的转换,这一步算是偷懒了.有好处也有坏处,好处是不用写那么多Local信息了,直接拿default的来就可以了.坏处是Locale的处理上有问题.当然我现在的项目只支持中文.:),以后有时间可以考虑本地化问题.BTW:grails自己的本地化支持目前好像还没有?不太清楚.呵呵.<br />第二个地方:”size”这种Type用的是”minLength,maxLength”,而不是原先的intRange,具体怎么回事用脑袋想想也知道:两者在javascript上的实现天差地远.<br />第三个地方:splitType(),由于有了”minLength,maxLength”这种类型,那么整合的时候当然不能直接写成validationMinLength,maxLength()这样的东东,可以说是个比较严重的错误,所以这里顺便更改了一下<br />第四个地方: 看看这段代码<br />[code:1]switch(vt) {
	                    case 'mask': 
	                    	out &lt;&lt; "function ${form}_mask() {";
		                    break;
	                    case 'intRange': 
	                    	out &lt;&lt; "function ${form}_intRange() {";
	                    	break;
	                    case 'floatRange': 
	                    	out &lt;&lt; "function ${form}_floatRange() {";
	                    	break;
	                    case 'maxLength': 
	                    	out &lt;&lt; "function ${form}_maxlength() {";
	                    	break;
	                    case 'minLength': 
	                    	out &lt;&lt; "function ${form}_minlength() {";
	                    	break;
	                    case 'email':
	                    	out &lt;&lt; "function ${form}_email() {";
	                    	break;
	                    case 'creditCard':
	                    	out &lt;&lt; "function ${form}_creditCard() {";
	                    	break;
	                    case 'required':
	                    	out &lt;&lt; "function ${form}_required() {";
	                    	break;           
	                    default:
	                    	out &lt;&lt; "function ${form}_${vt}() {";
	                    	break;
               		}[/code:1]

原先的代码太粗略,minlength和maxlength不能工作,所以写个比较麻烦的代码,其实修改一下那个MAP就可以了,不过那样修改的东西太多了,不合算.

[code:1]

/* Copyright 2004-2005 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT c;pWARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import java.text.MessageFormat
import org.springframework.validation.Errors;
import org.springframework.context.NoSuchMessageException;
import org.springframework.web.servlet.support.RequestContextUtils as RCU;
import org.codehaus.groovy.grails.commons.GrailsClassUtils as GCU;
import org.geszjava.grails.utils.ChineseProperty as CP;


/**
*  A  tag lib that provides tags to handle validation and errors
*
* @author Graeme Rocher
* @since 17-Jan-2006
*/

class ValidationTagLib {
    /**
     * Checks if the request has errors either for a field or global errors
     */
    def hasErrors = { attrs, body ->
        def model = attrs['model']
        def checkList = []
        if(model) {
            checkList = model.findAll { k,v ->
                grailsApplication.isGrailsDomainClass(v.class)
            }
        }
        if(attrs['bean']) {
            checkList &lt;&lt; attrs['bean']
        }
        else {
			if(request.attributeNames) {
				request.attributeNames.each { ra ->
					if(ra) {
                        if(ra instanceof Errors)
                            checkList &lt;&lt; ra
                        else if(grailsApplication.isGrailsDomainClass(ra.class))
                            checkList &lt;&lt; ra
					}
				}
			}
        }

        for(i in checkList) {
            def errors = null
            if(grailsApplication.isGrailsDomainClass(i.class)) {
                if(i.hasErrors())
                    errors = i.errors
            }
            else if(i instanceof Errors) {
               errors = i
            }
            if(errors) {
                if(attrs['field']) {
                    if(errors.hasFieldErrors(attrs['field'])) {
                        body()
                    }
                }
                else {
                    body()
                }
            }
        }
    }

    /**
     * Loops through each error for either field or global errors
     */
    def eachError = { attrs, body ->
        def model = attrs['model']
        def errorList = []
        if(model) {
            errorList = model.findAll { k,v ->
                grailsApplication.isGrailsDomainClass(v.class)
            }
        }
        if(attrs['bean']) {
            errorList &lt;&lt; attrs['bean']
        }
        else {
            request.attributeNames.each {
                def ra = request[it]
                if(ra) {
                    if(ra instanceof Errors)
                        errorList &lt;&lt; ra
                    else if(grailsApplication.isGrailsDomainClass(ra.class))
                        errorList &lt;&lt; ra
                }
            }
        }

        for(i in errorList) {
            def errors = null
            if(grailsApplication.isGrailsDomainClass(i.class)) {
                if(i.hasErrors())
                    errors = i.errors
            }
            else if(i instanceof Errors) {
               errors = i
            }
            if(errors && errors.hasErrors()) {
                if(attrs['field']) {
                    if(errors.hasFieldErrors(attrs['field'])) {
                        errors.getFieldErrors( attrs["field"] ).each {
                            body(it)
                        }
                    }
                }
                else {
                    errors.allErrors.each {
                        body( it )
                    }
                }
            }
        }
    }

    /**
     * Loops through each error and renders it using one of the supported mechanisms (defaults to "list" if unsupported)
     */
    def renderErrors = { attrs, body ->
        def renderAs = attrs.remove('as')
        if(!renderAs) renderAs = 'list'

        if(renderAs == 'list') {
            out &lt;&lt; "&lt;ul>"
            eachError(attrs, {
                out &lt;&lt; "&lt;li>"
                message(error:it)
                out &lt;&lt; "&lt;/li>"
              }
            )
            out &lt;&lt; "&lt;/ul>"
        }
    }
    /**
     * Resolves a message code for a given error or code from the resource bundle
     */
    def message = { attrs ->
          def messageSource = grailsAttributes
                                .getApplicationContext()
                                .getBean("messageSource")

          def locale = RCU.getLocale(request)

          if(attrs['error']) {
                def error = attrs['error']
                def defaultMessage = ( attrs['default'] ? attrs['default'] : error.defaultMessage )
                def message = messageSource.getMessage( error.code,
                                                        error.arguments,
                                                        defaultMessage,
                                                        locale )
                if(message) {
                    out &lt;&lt; message
                }
                else {
                    out &lt;&lt; error.code
                }
          }
          if(attrs['code']) {
                def code = attrs['code']
                def defaultMessage = ( attrs['default'] ? attrs['default'] : code )

                def message = messageSource.getMessage( code,
                                                        null,
                                                        defaultMessage,
                                                        locale )
                if(message) {
                    out &lt;&lt; message
                }
                else {
                    out &lt;&lt; code
                }
          }
    }
    // Maps out how Grails contraints map to Apache commons validators
    static CONSTRAINT_TYPE_MAP = [ email : 'email',
                                             creditCard : 'creditCard',
                                             match : 'mask',
                                             blank: 'required',
                                             nullable: 'required',
                                             maxSize: 'maxLength',
                                             minSize: 'minLength',
                                             range: 'intRange',
                                             size: 'maxLength,minLength',
                                             length: 'maxLength,minLength' ]
    /**
     * Validates a form using Apache commons validator javascript against constraints defined in a Grails
     * domain class
     *
     * TODO: This tag is a work in progress
     修改
     */
    def validate = { attrs, body ->
        def form = attrs["form"]
        def againstClass = attrs["against"]
        if(!form)
            throwTagError("Tag [validate] is missing required attribute [form]")

        if(!againstClass) {
            againstClass = form.substring(0,1).toUpperCase() + form.substring(1)
        }

        def app = grailsAttributes.getGrailsApplication()
        def dc = app.getGrailsDomainClass(againstClass)

        if(!dc)
            throwTagError("Tag [validate] could not find a domain class to validate against for name [${againstClass}]")

        def constrainedProperties = dc.constrainedProperties.collect { k,v -> return v }
        def appliedConstraints = []

        constrainedProperties.each {
           appliedConstraints += it.collect{ it.appliedConstraints }
        }

        appliedConstraints = appliedConstraints.flatten()
        def fieldValidations = [:]
        appliedConstraints.each {

            def validateType = CONSTRAINT_TYPE_MAP[it.name]
            if(validateType) {
                if(fieldValidations[validateType]) {
                    fieldValidations[validateType] &lt;&lt; it
                }
                else {
                     fieldValidations[validateType] =  [it]
                }
            }
        }

        out &lt;&lt; '&lt;script type="text/javascript">\n'
        def scriptNameUtil = "org/apache/commons/validator/javascript/validateUtilities.js"
            
        def inStreamUtil = getClass().classLoader.getResourceAsStream(scriptNameUtil)
        if(inStreamUtil) {
            out &lt;&lt; inStreamUtil.text
        }        
        
        fieldValidations.each { k,v ->
           def validateType = k

           if(validateType) {

                def validateTypes = [validateType]

                if(validateType.contains(",")) {
                    validateTypes = validateType.split(",")
                }

                validateTypes.each { vt ->
                    // import required script
                    def scriptName = "org/apache/commons/validator/javascript/validate" + vt.substring(0,1).toUpperCase() + vt.substring(1) + ".js"
                    def inStream = getClass().classLoader.getResourceAsStream(scriptName)
                    if(inStream) {
                        out &lt;&lt; inStream.text
                    }
                    
                    switch(vt) {
	                    case 'mask': 
	                    	out &lt;&lt; "function ${form}_mask() {";
		                    break;
	                    case 'intRange': 
	                    	out &lt;&lt; "function ${form}_intRange() {";
	                    	break;
	                    case 'floatRange': 
	                    	out &lt;&lt; "function ${form}_floatRange() {";
	                    	break;
	                    case 'maxLength': 
	                    	out &lt;&lt; "function ${form}_maxlength() {";
	                    	break;
	                    case 'minLength': 
	                    	out &lt;&lt; "function ${form}_minlength() {";
	                    	break;
	                    case 'email':
	                    	out &lt;&lt; "function ${form}_email() {";
	                    	break;
	                    case 'creditCard':
	                    	out &lt;&lt; "function ${form}_creditCard() {";
	                    	break;
	                    case 'required':
	                    	out &lt;&lt; "function ${form}_required() {";
	                    	break;           
	                    default:
	                    	out &lt;&lt; "function ${form}_${vt}() {";
	                    	break;
               		}
                    
                    //out &lt;&lt; "function ${form}_${vt.toLowerCase()}() {"
                    	
                    v.each { constraint ->
                           out &lt;&lt; "this.${constraint.propertyName} = new Array("
                           out &lt;&lt; "'${constraint.propertyName}'," // the field
                           //out &lt;&lt; '"Test message"' // TODO: Resolve the actual message
                           def clazzName = againstClass?.toLowerCase()
                           out &lt;&lt; getMessage(vt, clazzName, constraint.propertyName, constraint)

                           switch(vt) {
                                case 'mask': out &lt;&lt; ",function() { return '${constraint.regex}'; }";break;
                                case 'intRange': out &lt;&lt; ",function() { if(arguments[0]=='min') return ${constraint.range.from}; else return ${constraint.range.to} }";break;
                                case 'floatRange': out &lt;&lt; ",function() { if(arguments[0]=='min') return ${constraint.range.from}; else return ${constraint.range.to} }";break;
                                case 'maxLength': 
                                	if (!isSizeConstraint(constraint)) {
	                                	out &lt;&lt; ",function() {return ${constraint.maxSize};}";
                                	} else {
                                		out &lt;&lt; ",function() {return ${constraint.range.to};}";
                                	}
                                	break;
                                case 'minLength': 
                                	if (!isSizeConstraint(constraint)) {
	                                	out &lt;&lt; ",function() {return ${constraint.minSize};}";
                                	} else {
                                		out &lt;&lt; ",function() {return ${constraint.range.from};}";
                                	}
                                	break;
                           }
                           out &lt;&lt; ');\n'
                    }
                    out &lt;&lt; "}\n"
                }
            }
        }
        out &lt;&lt; 'function validateForm(form) {\n'
         fieldValidations.each { k,v ->
         	   def splittedTypes = splitType(k);
         	   for (spt in splittedTypes) {
	               def validateType = spt.substring(0,1).toUpperCase() + spt.substring(1)
	               out &lt;&lt; "if(!validate${validateType}(form)) return false;\n"
         	   }
         }
        out &lt;&lt; 'return true;\n';
        out &lt;&lt; '}\n'
      	out &lt;&lt; "document.forms['${attrs['form']}'].onsubmit = function() {return validateForm(this);}\n"
        out &lt;&lt; '&lt;/script>'
    }
    
    //添加
    def static messageMap = [
    	"mask" : "{0}不符合正则表达式[{1}]",
    	"creditCard" : "{0}不是一个合法的信用卡号",
    	"email" : "{0}不是一个合法的Email地址",
    	"intRange" : "{0}不在{1}到{2}之内",
    	"floatRange" : "{0}不在{1}到{2}之内",
    	"maxLength" : "{0}的长度大于{1}",
    	"minLength" : "{0}的长度小于{1}",
    	"required" : "{0}不能为空",
     ]                           
     
     //添加
    def static splitType(type) {
    	 if (type.indexOf(",") >= 0) {
    		 StringTokenizer st = new StringTokenizer(type, ",");
    		 def retList = []
    		 while (st.hasMoreTokens()) {
    			 retList &lt;&lt; st.nextToken();
    		 }
    		 return retList;
    	 } else {
    		 return [type];
    	 }
    }
    
    def static isSizeConstraint(constraint) {
    	if (constraint.getClass().getName() == "org.codehaus.groovy.grails.validation.ConstrainedProperty\$SizeConstraint") 
    		return true
    	else return false
    }
     //添加
	def getMessage(vt, clazzName, propertyName, constraint) {
    	def cn = CP.getChinesePropertyName(clazzName + "." + propertyName)
    	if (!cn) cn = propertyName
    	Object[] args = new Object[3]
		args[0] = cn
		def omsg = messageMap[vt]
		if (!omsg) omsg = "存在未知错误"
    	def msg = "";
    	
		switch(vt) {
			case 'email': 
				break
			case 'creditCard': 
				break
			case 'mask': 
				args[1] = constraint.regex
				break
			case 'intRange': 
				args[1] = constraint.range.from
				args[2] = constraint.range.to
				break
			case 'floatRange': 
				args[1] = constraint.range.from
				args[2] = constraint.range.to
				break;
			case 'maxLength': 
				if (!isSizeConstraint(constraint)) {
					args[1] = constraint.maxSize
				} else {
					args[1] = constraint.range.to
				}
				break
			case 'minLength': 
				if (!isSizeConstraint(constraint)) {
					args[1] = constraint.minSize
				} else {
					args[1] = constraint.range.from
				}
 				break				
    	}

		return "\"" + MessageFormat.format(omsg, args) + "\""
    }
}
[/code:1][/code]
          <br/>
          <span style="color:red;">
            <a href="http://geszjava.javaeye.com/blog/24088#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 11 Aug 2006 11:11:50 +0800</pubDate>
        <link>http://geszjava.javaeye.com/blog/24088</link>
        <guid>http://geszjava.javaeye.com/blog/24088</guid>
      </item>
      <item>
        <title>grails的mvc相关功能</title>
        <author>geszJava</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://geszjava.javaeye.com">geszJava</a>&nbsp;
          链接：<a href="http://geszjava.javaeye.com/blog/24074" style="color:red;">http://geszjava.javaeye.com/blog/24074</a>&nbsp;
          发表时间: 2006年08月02日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          grails的Domain Class/*.gsp(或者*.jsp)/Controller是一个典型的MVC框架。<br /> <br />可能gsp让一些人感到陌生，在我看来可以把gsp看做一个模板，类似于velocity或者freemarker之类的东西。所有的标签其实是一个特定渲染动作。毫无疑问，目前他并没有多少可选的输出，他运用的是spring的MVC结构，当然可能不如webwork来得强大。<br /> <br />模板的include功能还是比较强的，可以使用类似于“&lt;g:render template="/modulename/abc" />”之类的语句来进行include，当然，在/view/modulename下面必须有"_abc.gsp"这样的文件存在。  而且可以给这个render提供数据 &lt;g:render template="/modulename/abc" collection="${books}" />或者bean="${book}" 等，说到底，他还是一个修饰数据的模板。可以使用这个功能来包含我们的top，bottom，left等。<br /> <br />在controller中，比较重要的功能是Action Interceptors（Action拦截器），这也是rails的重要功能。譬如这样一段action代码：<br />def beforeInterceptor = [action:this.&auth,except:'login']<br />// defined as a regular method so its private<br />def auth() {<br />     if(!session.user) {<br />            redirect(action:'login')<br />            return false<br />     }<br />}<br />def login = {<br />     // display login page<br />}<br /><br />注意这段语句：def beforeInterceptor = [action:this.&auth,except:'login']<br /><br />就是一个典型的login拦截器，这样在调用其他action之前，会调用auth这个action闭包，如果返回false，那么action将不会被执行。从而重定向到login这个action。同样我们的拦截器可以指定except，这个属性实际上是指定拦截的范围，这里是排除拦截login这个action，否则将会使用户无法登陆。<br /><br />其他的功能也都很有用，不过比较简单，详细的可以看http://docs.codehaus.org/display/GRAILS/Controllers，有request数据绑定，fileupload，action chain，domain object dynamic method等。grails也提供中文首页，地址是http://docs.codehaus.org/display/GRAILS/Chinese-Home，不过很多东西都还没有中文文档，只能我们自己去看英文文档。<br /><br />关于MVC中的Model(Domain class)可以参考GORM，这个东西地层是Hibernate的，非常的简单实用，被Hiberante搞昏了的同志可以看看这个，GORM中只提供1:1,1:m这两种数据关联，可以单向也可以双向，非常的简单，个人认为n:m这种数据关联应该避免使用，这种数据关联结构将会使应用变得低效和复杂，如果需要多对多的关联，那么可以自己写java的类和hbm文件。
          <br/>
          <span style="color:red;">
            <a href="http://geszjava.javaeye.com/blog/24074#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 02 Aug 2006 16:45:00 +0800</pubDate>
        <link>http://geszjava.javaeye.com/blog/24074</link>
        <guid>http://geszjava.javaeye.com/blog/24074</guid>
      </item>
      <item>
        <title>grails的job</title>
        <author>geszJava</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://geszjava.javaeye.com">geszJava</a>&nbsp;
          链接：<a href="http://geszjava.javaeye.com/blog/24073" style="color:red;">http://geszjava.javaeye.com/blog/24073</a>&nbsp;
          发表时间: 2006年08月02日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          打算再我的项目中引入静态页面的生成，因为这几天看代码看的太紧张了，于是想去实现他。首先想到的是Grails如何来进行controller的执行，因为我的这个job需要Domain Class的支持，进一步说就是我需要能让程序可以享受Domain Class的Domain Class Methods的便利，不仅如此，还可以保证Domain Class代码的单纯性，想想也是，既然Grails提供了Spring+Hibernate的解决方案，为什么不用他呢。吼吼。<br />    于是开始看源代码，不得不说的是，Eclipse的引用功能的确是非常的贴心，至少读源代码方面是一流的，于是从DomainClassMethods->GrailsDomainConfigurationUtil->ConfigurableLocalSessionFactoryBean->SpringConfig－>GrailsDispatcherServlet，OK！路子走通了，那么来看看GrailsDispatcherServlet里面做些什么呢？于是仔细look...发现了一个很重要的类GrailsApplication，可以说这个类是Grails的基础，毫无疑问，他可以脱离web server环境，因为grails有个Console类，就是直接引用GrailsApplication的，相当于Grails可以在其他应用程序中跑起来！<br />    当然这个发现并没给我带来多大帮助。。。我还是需要从GroovyPageServlet开始分析，到底Grails是如何建立web环境，并且执行controller的原理。于是进入GroovyPageServlet，马上就可以跟踪到SimpleGrailsControllerHelper这里，这里是Grails处理controller的地方，于是发现了两个很重要的method: handleURI(),handleAction()，自此基本上可以解决静态页面生成的问题了。<br />   但是，这时猛然想起，grails自己本身就支持job，而且是大名鼎鼎的Quartz！亏大了。。于是马上打开User Guide，找到Job Scheduling (Quartz)这个章节，看了看，非常简单，grails create-job就可以搞定，无外乎在项目下面建立一个jobs目录，然后写个类，上面提供了一个非常简单的example：<br />class MyJob {<br />  def startDelay = 100<br />  def timeout = 1000<br /><br />  def name = "MyJob"<br />  def group = "MyGroup"<br /><br />  def execute(){<br />    print "Job run!"<br />  }<br />}<br />可惜的是，这个example是错误的！这个也是我出师不利以后调试源代码才发现的，grails 0.2中，"startDelay"和"timeout"这两个field是用String.class来解析的，如果写成number的时候，你的schedule是十分钟执行一次，很郁闷，因为他缺省的就是6000000,呵呵。正确的写法是：<br />class MyJob {<br />  def startDelay = "100"<br />  def timeout = "1000"<br /><br />  def name = "MyJob"<br />  def group = "MyGroup"<br /><br />  def execute(){<br />    print "Job run!"<br />  }<br />}<br />就这么简单，不过在佩服grails项目组的同时，也稍微鄙视一下粗心的Micha?? K??ujszo，(抱歉，我这里是乱码)，这个错误实在不该犯！<br /> <br />另外，在job里面可以使用Domain Class的Domain Class Methods,这可是个好东东，很贴心的设计。
          <br/>
          <span style="color:red;">
            <a href="http://geszjava.javaeye.com/blog/24073#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 02 Aug 2006 16:44:24 +0800</pubDate>
        <link>http://geszjava.javaeye.com/blog/24073</link>
        <guid>http://geszjava.javaeye.com/blog/24073</guid>
      </item>
      <item>
        <title>Grails0.2的一个Bug[输出的html文件内容缺失]修理过程</title>
        <author>geszJava</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://geszjava.javaeye.com">geszJava</a>&nbsp;
          链接：<a href="http://geszjava.javaeye.com/blog/24072" style="color:red;">http://geszjava.javaeye.com/blog/24072</a>&nbsp;
          发表时间: 2006年08月02日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          [Blog搬家到Javaeye]<br />一听到grails推出了0.2版本以后，就兴忡忡的拿过来摆弄了一番。按照快速开始的流程做下来<br />1.grails create-app<br /><br />2.grails create-domain-class<br /><br />3.grails create-controller<br /><br />稍微修改了一下Controller，令其使用scaffold<br /><br />一个简单的Domain-class的CUID页面就形成了。<br /><br />4.grails run-app ok，grails开始启动了。<br /><br />输入英文，ok，可以使用。但是偶们是中文国家，所以输入中文“我爱北京”，似乎也行，再输入“我爱北京天安门”，这会就出现问题了！页面上最后的地方显示出了"&lt;/b"这样的字样，于是再看输入“我爱北京”的时候的输出，同样html内容缺失！<br /><br />很郁闷，但是不得不承认Grails0.2有了一个很严重的Bug：View经过渲染以后发送到客户端的内容产生了缺失！<br /><br />幸好，Grails有源代码，拿过来研究一下。于是便开始了Grails的Debug之旅<br /><br />1.首先考虑到grails0.1当中似乎没出现这种Bug，怀疑是0.2引入的bug，这好办，打开心爱的Beyond Compare，对grails0.1的src和0.2的src进行比较。因为出问题的是View关于IO的，很快就定位到web模块的org.codehaus.groovy.grails.web.pages这个包下面，仔细比较两个代码包之间的差异，可惜的是没有发现有价值的线索！抓狂了，但是在另外一个包下面发现了一个比较重大的改动org.codehaus.groovy.grails.web.servlet.mvc.SimpleGrailsController中已经把HttpServletResponse换成了GrailsHttpServletResponse，而对比GrailsHttpServletResponse的时候发现里面已经增加了一个跟Bug比较相关的字段: contentTypeSet,于是想可能问题出在charset上面把，因为charset方面的原因，极有可能导致Stream相关操作在size方面出现计算错误而导致内容的缺失。<br /><br /> <br /><br />2.既然发现了可能的原因，马上祭其Eclipse进行调试。<br /><br />   a.grails war 把示例程序打包成war文件<br /><br />   b.Eclipse导入war文件形成一个工程first<br /><br />   c.删除first工程下的webcontent/WEB-INF/lib/grails-0.2.jar<br /><br />   d.Eclipse导入grails0.2的build.xml形成Grails源码工程<br /><br />   e.把grails工程的输出链接到first工程下的webcontent/WEB-INF/classes下面。<br /><br />上述准备工作做好了以后就可以开始进行调试了。web方面的调试我还是使用特方便的Lomboz。 first工程－》debug on server就可以了。<br /><br />第一步，首先把SimpleGrailsController中的GrailsHttpServletResponse改回去，调试。。。不行，故障依旧。<br /><br />第二步，于是我想可能问题会出在io方面，慢慢的进入到GSPResonseWriter这个类里面，观察他的out0字段，发现在输出之前我们所需要的正确内容正在里面，一个很重大的发现！这意味着可以不用去调试Grails的任何其他模块，如果出问题，就是后继IO模块出错了，因为out0对象是直接和ResponseFacade交互的，于是直接跳到close()方法中，观察到这里的out0对象还是没有错，但是很意外的发现这样一段代码：<br /><br />  if (!response.isCommitted()) {<br />     response.setContentLength(out0.size());<br />  }<br /><br />对比0.1版本的源文件，发现这里并没作任何改动。怀疑grails0.2对编码方式的处理作了更改，把这段代码删除，ok！这段代码的作用大概是为了提高io性能，但是有了反而出错了，或许应该采用更好的解决方法，而不是直接out0.size()这样的方式！<br /><br />到此，grails这个输出内容缺失的bug获得了解决。前后花费了一个星期天的时间，虽然有些郁闷，但是有点成就感。HOHO
          <br/>
          <span style="color:red;">
            <a href="http://geszjava.javaeye.com/blog/24072#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 02 Aug 2006 16:42:44 +0800</pubDate>
        <link>http://geszjava.javaeye.com/blog/24072</link>
        <guid>http://geszjava.javaeye.com/blog/24072</guid>
      </item>
  </channel>
</rss>