Grails Development User Guide 16 Mar 2014
Grails开发者手册
在一般的开发流程中,我们通常是按照Domains->Controllers | Services(如果有必要的 话)->Views->UrlMappings这样的流程来开发。所以下面的章节将按照以上的顺序来对Grails做简单的介绍。更详细的 Grails介绍请参看Grails User Guide |
建立Domain类
基本属性
通过您喜欢的IDE或者是Grails提供的命令,我们可以很轻松地创建一个Domain类,这里我们只介绍通过Grails命令进行创建,使用如下命令可以创建一个helloworld包下名为User的Domain类
$ grails create-domain-class org.easycloud.wct.auth.User
上述命令将会创建一个grails-app/domain/org/easycloud/wct/auth/User.groovy的文件,内容如下
package org.easycloud.wct.auth
class User {
static constraints = {
}
}
我们需要为User类添加若干个属性,例如:
package helloworld
class User {
String login
String password
String firstName
String lastName
String email
String url
Long loginTimes
Date dateCreated
static constraints = {}
}
上面的Person类,我们为其添加了4个属性,ID是默认加入到Domain类的,所以我们这里不需担心。这样,Grails自动为我们在数据库中建立一个含有相应字段的表,后续我们会介绍如何自定义映射表的表名和列名。
基本的CRUD
Create
import org.easycloud.wct.auth.User
def u = new User(login: 'admin', password: 'changeit', firstName: 'Ke', lastName: 'Meng', email: 'mengke@163.com', url: 'http://www.mengke.com', loginTimes: 0l)
u.save()
Read
import org.easycloud.wct.auth.User
def u = User.get(1)
assert 1 == u.id
assert 'admin' == u.login
该方法将指定id的用户从数据库中读取出来,如果你需要一个只读的实体,你可以使用read方法
def u = User.read(1)
在上面的语句中,Hibernate引擎不会记录u的脏状态,也不会将u持久化,此外,你可以通过load方法来load一个User实例的代理对象
def u = User.load(1)
直到你调用了getId之类的方法前,上面语句不会立即访问数据库。
Update
import org.easycloud.wct.auth.User
def u = User.get(1)
assert 'changeit' == u.password
u.password = 'admintest'
u.save()
def ut = User.get(1)
assert 'admintest' == u.password
Delete
import org.easycloud.wct.auth.User
def u = User.get(1)
u.delete()
def ut = User.get(1)
assert null == ut
通过UI查看数据库
Grails内置了一个简单的数据库UI,它可以帮助我们方便的查看数据表以及操作相关测试数据(尤其是对于H2内存库),启动应用 并使用浏览器访问http://localhost:8080/wct/dbconsole 在JDBC URL项中填写对应环境的数据源,如当前开发环境
jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000
Constraints
约束条件主要用于验证,常用的约束条件如下
- nullable: false即为该字段不能为空
字符串常用约束条件
- blank: false即为该字符串字段不能为空串””
如何保存一个空字符串(TBD)
- email: true 该字段必须是一个email地址
- inList: [“one”, “two”, “three”, “four”] 该字段必须是列表内字符串之一,另外如果我们不设置maxSize和size约束的话,该字段采用列表内字符串长度最长的长度作为该字段的最大长度,如本例即为5(”three“)
- matches: “[a-zA-Z]+” 该字段必须匹配给定正则
- maxSize: 20 该约束需要设置一个整数,指定对应字段的最大长度
- minSize 同maxSize,指定对应字段的最小长度
- size: 1..20 该约束需要设置一个整数范围,指定对应字段的长度范围
- unique: true 即为该字段值必须唯一
- url: true 该字段必须是一个url地址
数字以及日期等常用约束条件
- max: 20 该字段的值必须小于等于设定值20
- min: 2 该字段的值必须大于等于设定值2
- range: 2..20 该字段的值必须介于2到20之间
对象关系
多对一和一对一
class Face {
Nose nose
}
class Nose {
}
上面的例子我们生成一个从Face到Nose的单向多对一映射,如果我们需要一个双向映射,可以按照参照如下例子
class Face {
Nose nose
}
class Nose {
static belongsTo = [face:Face]
}
上面的例子我们保存一个Face的话,它所携带的nose实例会被一起保存,当删除一个Face实例时,从属于它的nose会被一起删除。该例子中生成如下两个表:
Face:
字段名 | 类型 | 备注 |
---|---|---|
ID | ||
VERSION | ||
NOSE_ID |
Nose:
字段名 | 类型 | 备注 |
---|---|---|
ID | ||
VERSION |
实体继承关系
TBD
生成Controller
创建controller一般有两种方式,一种是create-controller,另外是根据已有的Domain类生成一个controller,前者grails会帮我们生成一个空白的controller,而后者会为指定的Domain类生成基本的CRUD action
命令行:
$ grails create-controller book
$ grails generate-controller [domain class name]
上面第一条语句会帮我们创建一个名为BookController的空白Controller类,而第二条语句会帮我们创建一个与Domain类对应的Controller,里面包含index,save,update,delete等方法。
Intellij IDEA用户:
创建一个空白controller
package org.easycloud.wct
class DefaultController {
def index() {}
}
根据已有的Domain类生成Controller
当Domain类的Controller已经存在时,点击导航栏中的Controller按钮会自动定位到Controller类
Eclipse(ggts)用户
创建一个空白Controller
根据已有的Domain类生成Controller
自定义配置参数
在开发过程中,我们可能会需要添加各种自定义参数,比如图片上传路径,登录密码加密方式等,需要在Config.groovy中添加如下配置即可
grails.demo.config1 = 'demo'
grails.demo.config2 = ['demo2', 'blahblah']
或
grails {
demo {
config1 = 'demo1'
config2 = ['demo2', 'blahblah']
}
}
在上例中,grails.demo.config1为字符串类型,grails.demo.config2为数组类型
注意,你不能写成如下方式
grails.demo {
config1 = 'demo1'
config2 = ['demo2', 'blahblah']
}
然后再Controllers中需要读取该参数的话,在action中使用如下语句读取
def demoConfig1 = grailsApplication.config.grails.demo.config1
如果需要在Services中使用该参数,需要按如下方式读取
class MyService {
def grailsApplication
String greeting() {
def demoConfig1 = grailsApplication.config.grails.demo.config1
return "Hello ${demoConfig1}"
}
}
一般情况下我们需要针对不同的运行环境使用不同的配置,所以我们最好使用environment-based配置
environments {
production {
grails {
demo {
config1 = 'demo1'
config2 = ['demo2', 'blahblah']
}
}
}
development {
grails {
demo {
config1 = 'devDemo1'
config2 = ['devDemo2', 'blahblah']
}
}
}
test {
grails {
demo {
config1 = 'testDemo1'
config2 = ['testDemo2', 'blahblah']
}
}
}
}
上述配置可以简化为
grails {
demo {
environments {
production {
config1 = 'demo1'
config2 = ['demo2', 'blahblah']
}
development {
config = 'devDemo1'
config2 = ['devDemo2', 'blahblah']
}
test {
config = 'testDemo1'
config2 = ['testDemo2', 'blahblah']
}
}
}
}
// 读取方式仍然为
def demoConfig1 = grailsApplication.config.grails.demo.config1
生成Views
配置URLMappings
Debug Mode
由于Grails 2.3.1启用了远程调试,所以传统基于JVM的Debug方式无法正常使用,下面使用如下方式启用Intellij的Debug功能
- 配置一个Remote Debug:Toolbar -> Run/Debug -> Edit Configuration -> 左上角”+” -> Remote -> 配置Name(随便写),然后按下图配置Host和Port,并保存
- 使用Grails命令
$ grails run-app --debug-fork
或在Intellij界面中配置
Run/Debug Configuration 中得Command Line配置
$ run-app --debug-fork
- 启动项目,待到日志输出如下信息时
| Packaging Grails application...
| Packaging Grails application....
| Packaging Grails application.....
| Running Grails application
Listening for transport dt_socket at address: 5005
切换到配置好的Remote-Debug,
点击debug即可。
Eclipse用户请按照官方文档创建Remote-Debug即可,其他步骤与Intellij相同
Troubleshooting
启动Tomcat时,报java.lang.IncompatibleClassChangeError: the number of constructors during runtime and compile time for … do not match. Expected ? but got ?
该错误一般是由于包重复造成的,如果是部署在服务器的war包报此错,可以解压war包,查看其lib目录下groovy包是否有重复