使用 Grails 快速开发 Web 应用程序--Web 开发的关键要素

通常情况下,Web 开发可以概括为以下五方面内容:

  1. 表单提交(验证)与接收(接收用户数据的主要方式);
  2. 表单输出(允许用户修改数据的主要方式);
  3. 数据列表(显示用户查询结果的主要方式);
  4. 超级链接的构造与响应(使用户透明的提交简单数据或者跳转到其它页面);
  5. 使用 Session 维持用户会话;

下面,我们将通过阅读 Grails 生成的代码,来理解它是如何分别实现这几方面内容的。

再创建一个名为 Member 的 Domain Class:

grails create-domain-class Member

 

编辑 Team 和 Member 这两个类,代码如下:

//Team.groovy
class Team {
String teamName
Date foundDate
static constraints = {
teamName(size:3..50,blank:false,unique:true)
}
static hasMany = [members:Member]
String toString(){
return teamName
}
}

//Member.groovy
class Member {
Team team
String name
String gender = 'F'
String address
String email
String phone
static belongsTo = [Team]
static constraints = {
name(size:3..20,blank:false,unique:true)
email(email:true,blank:false)
gender(inList:["F", "M"] )
}
}

 

上述代码描述了 Member 类的属性,并使用 belongsTohasmany 描述了 Team 类与 Member 类之间的一对多关系和多对一关系。Grails 会自动在数据库中为这两张表创建主外键。

接下来为 Member 类创建实现 CRUD 的 Controller 和 View。在控制台运行命令行 grails generate-all Member,同时也输入 grails generate-all Team 更新 Team 类的 Controller 和 View。

下面按照前面定义的五个 Web 开发的基本任务对 Member 类自动生成的代码进行分析:

表单提交与接收

打开 grails-app/views/member/creat.gsp,关注表单提交相关的代码(仅摘录表单相关的代码):

<g:form action="save" method="post">
<label for='dateOfBirth'>Date Of Birth:</label>
<g:datePicker name='dateOfBirth' value="${member?.dateOfBirth}">
</g:datePicker>
<label for='phone'>Phone:</label>
<input type="text" value="${member?.phone?.encodeAsHTML()}" name='phone'/>
<label for='email'>Email:</label>
<input type="text" value="${member?.email?.encodeAsHTML()}" name='email' />
<label for='name'>Name:</label>
<input type="text" value="${member?.name?.encodeAsHTML()}" name='name' />
<label for='gender'>Gender:</label>
<input type="text" value="${member?.gender?.encodeAsHTML()}" name='gender' />
<label for='team'>Team:</label>
<g:select optionKey="id" from="${Team.list()}" name='team.id' value="${member?.team?.id}">
</g:select>
<input type="submit" value="Create"></input>
</g:form>

 

g:form 会生成 <from> 标签,同时指定的接收表单数据的 action 为 save。gsp 的表单提交,大量使用标准的 Html tag,如 <input>,这样可以降低学习成本,无需学习过多特定的 tag。Grails 在表单接收端的代码同样也十分简单。Grails 不再使用 Servlet 中冗长的 request.getParameter(XXX),它有类似 Struts 却比 Struts 更简单的方式。如上述代码,接收用户表单提交的数据并把它构造为 Domain Class,只需要一行代码:member.properties = params。完整的 Controller Action 代码摘录如下:

class MemberController {

def save = {
def member = new Member()
member.properties = params
if(member.save()) {
//保存成功,重定向到 show Action
redirect(action:show, id:member.id)
}
else {
//保存失败,返回并报错
render(view:'create', model:[member:member])
}
}

 

在上述代码中,params 是一个 Map,包含全部表单提交的数据。把 Map 的值赋给 Domain Class 的 properties 属性后,Domain Class 就会把 Map 类的同名键的值赋给自身相应的属性,自动完成类型转换。这一行代码实现的功能通常需要十几行 Java 代码才能实现,由此也体现了 Grails 开发的“快”。

再补充一点,使用构造函数也可以实现用 params 给 Domain Class 赋值,如:

def member = new Member(params)

 

由于 Member 是 Domain 类,所以它会被自动添加用于实现数据库增删查改的方法,执行 save 方法进行保存。如果保存成功,if 语句会执行“真”,否则会执行假。虽然数据库读写的任务并不是 Web 开发的基本任务,但如果能够简化这一操作,无疑会极大的提高开发效率,Grails 提供了一个良好的范例。

使用 Domain 类的 properties 属性去获取表单的值是 Grails 下最常见的表单接收方式,但不是唯一的方式。如果要获取某个单一的属性值,还有更简单的方法:可以使用 Map 的 Key 取值。如只获取表单项 <input name="email"/> 的值,可使用:params.email 或者 params['email']。

更进一步,表单提交的内容还可以自动全部写入 Java 类的实例中。还是以上述表单为例把表单内容写入 Java 实例中,定义 Java 类如下:

JMember.java
class JMember {
private String phone;
private String email;
private String name;
private Team team;
private Date dateOfBirth;
private String gender = 'F';
private String address;
public String getPhone(){ return phone;}
public String setPhone(String phone){this.phone = phone}
//…以下省略其它的 setter 和 getter
}

 

在接收的表单中编写如下代码:

class MemberController {

def save = {
JMember member = new JMember()
// 自动绑定
bindData(member,params)

}

}

 

也很神奇,不是吗?JMember 是个 JavaBean,Controller 类特有的“bindData”方法可以为 JavaBean 填充数据。(提示:在 Groovy 语言中,JavaBean 的使用也被简化了,如 JMember 的实例 member 可以通过 member.name 去访问或修改 name 属性,而无需显示的调用 member.getName()member.setName(xxx)

除了给 Domain 类的 properties 属性赋值和使用 Controller 类的 bindData 方法外,Grais 还提供了一种表单提交的方法,叫做 Command Object。Domain Class 可以很好的支持表单验证,但它必须与数据库的一张表关联,如果某一表单提交不是为了保存数据到数据库中,则不能使用 Domain Class。那么如何让非 Domain Class 支持表单的验证?Command Object 正好可以解决这个问题。

在文件夹 src/groovy 中创建一个名为 XXXCommand 的 Groovy 类,比如 SearchCommand.groovy,然后在 Command 类中加入约束条件(加入方法与 Domain Class 相同)。然后在 Controller 类里引用 Command Object,引入方法如下:

class MemberController {
def search = { SearchCommand cmd ->
if (cmd.hasErrors()) {
redirect(action:'error')
} else {
// do something else

}
}
}

 

SearchCommand cmd-> 表示 Grails 在调用 search Action 的时候,会传递给 Action 一个参数,这个参数就是 SearchCommand 的实例,名叫 cmd。SearchCommand 和表单对应的代码如下:

class SearchCommand {
String teamname
String username
String gender
static constraints = {
gender (inList:[ "F", "M"])
}
}

 

<g:form controller="member" action=”search” method="post" >
<label for='teamName'>Team Name:</label>
<input type="text" id='teamName' name='teamName'/>
<label for='name'>Name:</label>
<input type="text" id='name' name='name'/>
<label for='gender'>Gender:</label>
<g:select id='gender' name='gender' from=' ${[""] +
new Member().constraints.gender.inList.collect{it.encodeAsHTML()}}' />
<g:submit value="search"/>
</g:form>

 

从上面的代码可以看出,Command 对象在表单的构造并无太大区别,只是在接收时存在一点区别,使用起来并不复杂。

 

表单输出

表单的提交在 Grails 中十分简单,同样,表单输出也不难。正如上文,对于常见的 HTML 表单控件,只需要修改 value 属性,就可以显示控件中的内容:

<input type="text" value="${member?.phone?.encodeAsHTML()}" name='phone'/>

上述代码重点在两个地方,一是问号“?”的作用,二是 encodeAsHTML 方法。member?.phone 表达的意思是:如果 member 不为空,返回 member.phone,否则返回空字符串,同样道理,如果 member.phone 不为空,返回 member.phone.encodeAsHTML()。从字面的意思可以看出,这个方法可以把字符串中的“<”、“>”等转换为 HTML 对应的 &lt; &gt; 等。这个方法可以根据具体业务,决定是否调用(其实对于 phone 这个属性而言,是没有必要调用它的,但 Grails 自动生成的代码调用了此方法,用户可以自己手动改为 member?.phone)。对于相对复杂一点的表单输出,如下拉框(select box),可以使用 grails 自带的标签库 <g:select>

<g:select optionKey="id" from="${Team.list()}" name='team.id' value="${member?.team?.id}">

此时就可以同样使用 value 属性来指定表单项的内容了。但是 ${} 中的变量又是从哪来的呢?与 Struts 类似,它来自 Controller。观察 edit Action:

def edit = {
def member = Member.get( params.id )

if(!member) {
flash.message = "Member not found with id ${params.id}"
redirect(action:list)
} else {
return [ member : member ]
}
}

从 return 行对应的代码可以看到,Controller 向 Veiw 传递了 Model,这是典型的 MVC 模式。

 

表单的验证

Grails 对提供 Domain 类和 Command Object 的表单提供了“一栈式”的验证支持。首先在 Domain 类或 Command 类中添加约束信息,比如 Member 类的代码:

class Member { 
Team team
String name
String gender = 'F'
String address
String email
String phone
static belongsTo = [Team]
static constraints = {
name(size:3..20,blank:false,unique:true)
email(email:true,blank:false)
gender(inList:["F", "M"] )
}
}

在上述代码中加入对 Member 类的约束,调用 Domain 类对象的 validate 方法或 save 方法检验当前数据是否符合约束条件(Command 类可使用 hasError() 方法)。如果不符合,则返回 false

在相应的 gsp 页面中也需要有相应代码用于输出验证结果:

<g:hasErrors bean="${member}">
<div class="errors">
<g:renderErrors bean="${member}" as="list" />
</div>
</g:hasErrors>

上述代码实现以列表的形式输出 member 对象包含的全部错误信息。相应的,如果想更有针对性的指明具体是哪个表单项提交的数据有问题,可以使用如下的代码:

<td valign='top'
class='value ${hasErrors(bean:member,field:'name','errors')}'>
<input type="text" name='name'
value="${member?.name?.encodeAsHTML()}"/>
</td>

.error 的 CSS 项:

.errors { border: 1px solid red }

Grails 对错误信息提供了 i18n(国际化)支持。可以通过修改 Contact/grails-app/i18n 不同语言的资源文件来定制输出错误信息。这里修改的是 messages_zh_CN.properties:

member.name.blank=用户名不能为空
member.name.unique=用户名{2}已经存在,请使用其它用户名
member.name.size.toosmall=用户名长度不得少于{3}个字符
member.name.size.toobig=用户名长度不得超过{3}个字符
member.email.blank=Email地址不能为空
member.email.email.invalid=Email地址不合法
member.gender.not.inList=无效的性别
team.teamname.blank=Team名称不能为空
team.teamname.size.toosmall=Team名称长度不得少于{3}个字符
team.teamname.size.toobig=Team名称长度不得超过{3}个字符
team.teamname.unique=Team名称{2}已经存在,请使用其它名称….

这里要强调一点,一定要使用 UTF-8 格式来保存上述文件(不能使用 GB2312),保存完成后,无需手工调用 JDK 的 native2ascii.exe 去转换,因为在 Grails 启动的时候,会自动完成该转换。关于不同约束项对应的名称,可以通过 官方文档 获取。

 

数据列表

数据列表是 Web 应用最重要的一种数据输出方式,使用 Grails 实现数据列表十分容易。用一句话概括就是:由 Controller 提供数据,使用 <g:each> 遍历数据,再用 ${} 输出数据。

<g:each in="${memberList}">
<tr>
<td>${it.id?.encodeAsHTML()}</td>
<td>${it.dateOfBirth?.encodeAsHTML()}</td>
<td>${it.phone?.encodeAsHTML()}</td>
<td>${it.email?.encodeAsHTML()}</td>
<td>${it.address?.encodeAsHTML()}</td>
<td>${it.name?.encodeAsHTML()}</td>
</tr>
</g:each>

<g:each> 可以遍历数据,memberList 是 Controller 类中对应 list Action 的返回值。遍历过程中每次取出一个元素,默认名称为“it”,如果想改用其它名称,可以用 var 属性声明:<g:each in=”${memberList}” var=”member”>

 

超级链接的构造与响应

超级链接通常在 Web 应用中可以实现类似按扭的功能。一方面指明要跳转到的页面,另一方面还要提交少量的数据。比如,在显示 Member 列表的页面里,加入一个用于显示 Member 的 Link,只需要加入如下代码:

<g:each in="${memberList}">
<tr>

<td><g:link action="show" id="${it.id}">Show</g:link></td>
</tr>
</g:each>

通过指定 controlleraction 两个属性可以确定当前 Link 会跳转到哪个页面(如果不指定 controller,则应用当前的 controller),id 属性可以传递一个用于标记唯一性的属性值。而 <g:link>XXX</g:link> 中间的内容则为该链接显示的文本。

对应的 Action 接收 Link 数据的代码很简单:

def show = {
[ member : Member.get( params.id ) ]
}

通过 params.id 可以得到 Link 提交的数据。

对于传统的 ?ParameterName=value 的参数组织方式的链接,Grails 也提供了支持:可使用 g:link 标签的 params 属性,用一个 Map 给它赋值,则 Map 的 Key 被输出为参数名,而 Key 对应的 value 被输出为参数的值:

<g:link controller="member" 
action="create" params="['team.id':team.id]">Add Members</g:link>

上面的 Tag 最终会输出为 URL: /Contact/member/create?team.id=2 (假定 team.id 的值是 2)。

 

Session 的使用

在 Grails 中使用 Session 十分简单,Controller/GSP 中可以访问一个名叫 session 的对象,它与 Map 的用法十分相似。比如说,在 session 中保存一个名叫 age 的对象,在 Controller/GSP 中,可以写成 session.age=XXsession['age']=XX。调用 session 的 invalidate 方法,则可以清空 Session 的内容。

 

DB 相关操作

上面所讲的内容涵盖了 Web 开发的几个基本方面,理论上讲,掌握了这几个基本内容就可以进行 Web 开发了,但是 Grails 还提供了一些可以极大提高开发效率的内容,比如 DB 读写。DB 的读写虽然不属于 Web 范畴,但无可争议的是,几乎没有 Web 应用是不使用 DB 的。下面对 Grails 简化的 Hibernate 操作进行一下简单介绍:


表 2.DB 相关操作

save 保存 Domain 对象的数据到对应的库表中(可能是 Insert 也可能是 Update)
findBy 动态方法,查找并返回第一条记录,方法名可以变化
如:findByName("Tom") 会返回所有 name 属性为 Tom 的对象(只返回第一条记录)findByNameAndPassword("Tom","Mot")会返回所有 name 属性为”Tom”并且 password 属性为”Mot”的对象
findAllBy 与 findBy 类似,返回全部记录
executeQuery 执行一个 HQL 的查询(HQL 的使用,请参考 Hibernate 官方文档)
createCriteria 执行一个 Hibernate 的 Criteria 查询(下文会有一个 Criteria 的试例)
get 返回指定 id 的对象
count 执行”select count(*) from XX”的操作
delete 执行删除操作。

更多 DB 相关的操作,请参考 相关文档

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Grails 技术精解与Web开发实践【源码+样章】----下载不扣分,回帖加1分,欢迎下载,童叟无欺 第1章 导论 1 1.1 RoR的革命与Web开发的新时代 1 1.2 RoR并不完美 2 1.2.1 Ruby语言方面的不足 2 1.2.2 对历史遗留项目的支持较为困难 2 1.3 Grails的诞生解决了一些遗憾 3 1.3.1 Groovy语言 3 1.3.2 Grails站在了巨人的肩膀之上 3 1.3.3 Grails有良好的扩展性 3 1.4 对Grails的一些误解 3 1.5 本书的使用说明 4 1.6 本章小结 4 第一篇 入门篇 第2章 Hello Grails 6 2.1 Grails的安装 6 2.1.1 JDK的安装与配置 6 2.1.2 Grails的安装 7 2.2 创建Grails工程 8 2.3 Grails的MVC架构 11 2.4 Scaffold应用程序 14 2.5 开发工具的使用 17 2.6 本章小结 19 第3章 Groovy VS Java 20 3.1 Groovy的基本类型与运算符 21 3.1.1 字符串 21 3.1.2 数字 22 3.1.3 Groovy的类 23 3.1.4 运算符 24 3.2 Groovy的控制结构 25 3.3 Groovy的集合 27 3.3.1 列表 27 3.3.2 映射 28 3.3.3 区间 29 3.4 Groovy的闭包 30 3.4.1 闭包的定义 30 3.4.2 闭包的代表 31 3.4.3 闭包在GDK中的使用 31 3.5 本章小结 33 第二篇 实际应用 第4章 商品维护 36 4.1 准备工作 36 4.2 查看商品列表 40 4.3 创建和编辑商品 44 4.4 本章小结 48 第5章 商品搜索 49 5.1 构造查询表单 49 5.2 复杂的数据库查询 50 5.2.1 HibernateCriteriaBuilder 的初窥 51 5.2.2 数据库的分页查询 54 5.2.3 将查询改造为inner join 59 5.3 显示分页导航 60 5.4 本章小结 62 第6章 用户注册与登录 63 6.1 表单验证与资源文件 63 6.2 用户注册 69 6.3 用户登录 73 6.3.1 登录的数据库查询 73 6.3.2 使用Session维持会话 74 6.3.3 自定义Codec实现对 密码加密 75 6.4 登录保护 76 6.5 本章小结 79 第7章 购物车与订单 80 7.1 购物车的查看与管理 80 7.1.1 定义购物车的Domain类 80 7.1.2 定义OrderService类 82 7.1.3 显示购物车 84 7.1.4 维护购物车 85 7.2 订单的提交 90 7.2.1 定义订单的Domain类 90 7.2.2 提交订单的表单页面 90 7.2.3 订单的保存 94 7.3 订单的查看 95 7.4 本章小结 99 第8章 系统后台管理 100 8.1 页面布局的使用 100 8.1.1 Grails Layout的基础知识 100 8.1.2 为系统后台管理创建 统一的decorator 103 8.2 文件上传的实现 107 8.2.1 开发表单页面 107 8.2.2 在Controller中接收文件 108 8.3 修改订单状态 109 8.4 本章小结 110 第9章 Grails的自动化测试 111 9.1 Grails自动化测试基础知识 111 9.2 编写测试用例 113 9.2.1 对Domain类进行测试 113 9.2.2 对Service类进行测试 116 9.2.3 对Controller进行测试 118 9.2.4 对Taglib进行测试 120 9.3 本章小结 121 第10章 部署应用 122 10.1 Grails对部署的支持 122 10.2 配置应用程序 124 10.3 本章小结 127 第三篇 深入了解Grails 第11章 深入GORM 130 11.1 自定义映射 130 11.1.1 基本映射 130 11.1.2 配置主键 131 11.1.3 “锁”与Version 133 11.1.4 事件与自动时间戳 134 11.1.5 映射Blob字段 134 11.1.6 定义非持久化属性 135 11.2 深入理解Domain间的关系 136 11.2.1 一对一关系 136 11.2.2 一对多关系 137 11.2.3 多对多关系 139 11.2.4 继承关系 141 11.2.5 合成关系 143 11.3 数据库查询小结 143 11.3.1 GORM提供了便捷的 查询方法 143 11.3.2 基于HQL的查询 145 11.4 对GORM进行性能优化 146 11.4.1 设置抓取模式 147 11.4.2 使用二级缓存 147 11.5 使用GRAG工具生成Domain 151 11.6 本章小结 154 第12章 与Spring整合 155 12.1 依赖注入与Spring容器基础 155 12.1.1 依赖注入 155 12.1.2 Spring容器基础 157 12.2 在Grails使用Spring 158 12.3 本章小结 160 第13章 深入Controller 161 13.1 Controller中常用的属性与方法 161 13.2 自定义URL Mapping 164 13.3 Web Flow 167 13.4 本章小结 172 第14章 深入Groovy Server Page 174 14.1 GSP基础知识 174 14.1.1 GSP输出表达式 174 14.1.2 GSP中预定义的变量 与作用域 175 14.2 GSP标签库 175 14.2.1 常用的内置标签 176 14.2.2 开发自定义标签 179 14.3 Grails对Ajax的支持 182 14.4 本章小结 184 第15章 实现Web Service 185 15.1 REST风格的Web Service 185 15.1.1 什么是REST 185 15.1.2 在Grails中实现REST 185 15.1.3 在Client端调用服务 187 15.2 基于SOAP的传统Web Service 188 15.3 本章小结 189 第16章 使用Grails插件 190 16.1 插件的安装 190 16.2 插件的组织结构 196 16.3 插件的使用 197 16.3.1 Acegi插件 197 16.3.2 Debug插件 204 16.4 本章小结 205 第四篇 Grails解密 第17章 高级Groovy特性 208 17.1 动态方法调用与属性访问 208 17.1.1 动态方法调用 208 17.1.2 动态属性访问 208 17.2 invokeMethod和getProperty 209 17.3 MOP动态基础 211 17.3.1 遍历方法和属性 211 17.3.2 动态添加方法 213 17.3.3 动态添加属性 215 17.3.4 使用方法对象 216 17.3.5 为某一特定的实例 添加方法 217 17.4 本章小结 218 第18章 Grails插件开发 219 18.1 创建与发布插件 219 18.2 插件能做什么 221 18.2.1 添加Spring配置信息 223 18.2.2 与Spring容器交互 224 18.2.3 修改web.xml 224 18.2.4 添加动态方法 226 18.2.5 捕获变更 227 18.3 插件的依赖关系 229 18.4 在安装或升级时执行附加操作 230 18.5 本章小结 230 第19章 浅析Grails的源程序 231 19.1 准备工作 231 19.1.1 下载源码 231 19.1.2 编译Grails源码 231 19.2 HibernateCriteriaBuilder的原理 233 19.3 开启Hibernate Query Cache 237 19.4 本章小结 241 第20章 未来Grails版本的新特性 242 20.1 GORM的新特性 242 20.1.1 更多的GORM事件 242 20.1.2 映射基本类型的集合 243 20.1.3 对Domain的只读访问 243 20.1.4 定义默认排序字段 243 20.1.5 改进的findBy 245 20.2 对插件系统的改进 245 20.3 数据绑定 245 20.4 在GSP中使用JSP的标签 246 20.5 加密配置文件中的数据库密码 246 20.6 本章小结 246 参考文献 247 索引 248 Grails技术精解与Web开发实践 目录 XII XI

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值