Controllers
创建一个Controller
grails create-controller org.bookstore.hello
渲染(render)
把响应以不同的形式呈现。
render "some text"
render(text: "<xml>some xml</xml>", contentType: "text/xml", encoding: "UTF-8")
// render a template to the response for the specified model
def theShining = new Book(title: 'The Shining', author: 'Stephen King')
render(template: "book", model: [book: theShining])
render(view: "viewName", model: [book: theShining])
render(contentType: "application/json") {
book(title: b.title, author: b.author)
}
请求方法定义
默认,所有的方法可以对应所有的Http请求的方法。
我们可以自己配置Http请求方法与action的对应:
static allowedMethods = [action1:'POST',
action3:['POST', 'DELETE']]
def action1() { … }
def action2() { … }
def action3() { … }
bindData
语法:bindData(target, params, includesExcludes, prefix)
用法:
// binds request parameters to a target object
bindData(target, params)
// exclude firstName and lastName
bindData(target, params, [exclude: ['firstName', 'lastName']])
// only use parameters starting with "author." e.g. author.email
bindData(target, params, "author")
bindData(target, params, [exclude: ['firstName', 'lastName']], "author")
// using inclusive map
bindData(target, params, [include: ['firstName', 'lastName']], "author")
Chain
使用flash storage让从一个action跳转到另一个action时保持模型不变。
chain(action: "details", model: [book: shawshankRedemption])
语法:chain(controller*, namespace*, action, id*, model, params*)
默认的action
可以指定默认的action。static defaultAction = "list"
errors对象和hasErrors()方法
errors保存了该controller中的所有的错误。hasErrors()返回controller中是否有错误。
flash对象
一个临时对象,保存并且只保存转到下一个action时候session中的对象,当跳转到下一个action后清除。
def index() {
flash.message = "Welcome!"
redirect(action: 'home')
}
def home() {}
forward和redirect
forward:服务端跳转
redirect:浏览器跳转
grailsApplication
GrailsApplication的实例。
def bookClass = grailsApplication.classLoader.loadClass("Book")
namespace
不同package的controller可以定义在同一个namespace中;如果没有定义namespace,相同package的命名空间一样。
static namespace = 'reports' //定义命名空间为reports
params
Http请求的参数。
def book = Book.get(params.id)
request和response
HttpServletRequest和HttpServletResponse的实例对象。
respond
根据Accept的指定,以最合适的格式输出。比如:JSON、XML等。
respond Book.get(1)
respond Book.get(1), [formats:['xml', 'json']]
可以在responseFormats中指定,static responseFormats = ['xml', 'json']
scope
修改controller的作用域
static scope = "session"
servletContext
servletContext是 ServletContext 的实例对象。
input = servletContext.getResourceAsStream("/WEB-INF/myscript.groovy")
session
HttpSession的实例对象。
def logout() {
log.info "User agent: " + request.getHeader("User-Agent")
session.invalidate()
redirect(action: "login")
}
withForm 和 withFormat
withForm示例:
<g:form useToken="true" ...>
withForm {
// good request
}.invalidToken {
// bad request
}
withFormat示例:
def list() {
def books = Book.list()
withFormat {
html bookList:books
js { render "alert('hello')" }
xml { render books as XML }
}
}
withForm:用来处理表单提交
withFormat:根据请求的Accept,呈现不同的response
Service
创建一个Service
grails create-service org.bookstore.Book
对应地grails生成一个BookService文件。
import grails.transaction.Transactional
@Transactional
class BookService {
def serviceMethod() {
}
}
作用域
static scope = "session"
score的值有:prototype,request,flash,flow,conversation,session,singleton。其中singleton为默认。
transactional
使用static transactional = true
设置事务。
也可以使用@Transactional
注解。
GORM
创建一个领域对象
grails create-domain-class helloworld.Person
基础的CRUD操作
create
def p = new Person(name:"Fred",age:40,lastVisit: new Date())
p.save()
read
def p = Person.get(1)
def p = Person.read(1)
def p = Person.load(1)
read和load的区别:对于load方法认为该数据在数据库中一定存在,可以放心的使用代理来延迟加载,如果在使用过程中发现了问题,只能抛异常;而对于get方法,一定要获取到真实的数据,否则返回null。
update
def p = Person.get(1)
p.name = "Bob"
p.save()
delete
def p = Person.get(1)
p.delete()
GORM中的关联关系
Many-to-one and one-to-one
many-to-one
class Face{
Nose nose
}
class Nose{
}
以上代码我们建立了一个多对一的关系。
class Face{
Nose nose
}
class Nose{
static belongsTo = [face:Face]
}
这段代码是在先去的基础上添加了belongsTo,从而建立了级联关系。
one-to-one
class Face{
static hasOne = [nose:Nose]
}
class Nose{
Face face
}
这段代码建立了Face和Nose的一对一关联关系。
class Face{
static hasOne = [nose:Nose]
static constraints = {
nose unique: true
}
}
class Nose{
Face face
}
添加nose unique: true
后,表示face必须有一个nose。
class Person {
String name
Person parent
static belongsTo = [ supervisor: Person ]
static mappedBy = [ supervisor: "none", parent: "none" ]
static constraints = { supervisor nullable: true }
}
对象自身的关联关系
one-to-many
class Author {
static hasMany = [books: Book]
String name
}
class Book {
String title
}
上述代码定义了一对多的关联关系。
如果要求他们之间建立级联关系,则为Book修改如下:
class Book {
static belongsTo = [author: Author]
String title
}
如果多的一方有两个或以上相同的类型,可以如下代码:
class Airport {
static hasMany = [outboundFlights: Flight, inboundFlights: Flight]
static mappedBy = [outboundFlights: "departureAirport",
inboundFlights: "destinationAirport"]
}
class Flight {
Airport departureAirport
Airport destinationAirport
}
many-to-many
class Book {
static belongsTo = Author
static hasMany = [authors:Author]
String title
}
class Author{
static hasMany = [books:Book]
String name
}
GORM中的组合
class Person {
Address homeAddress
Address workAddress
static embedded = ['homeAddress', 'workAddress']
}
class Address {
String number
String code
}
Sets, Lists and Maps
上面的那些代码中定义了hasMany的属性其实就是一个Set。
当然也可以定义many为其他类型:
class Author {
// SortedSet books //定义为SortedSet
// List books //定义为List
// Map books //定义为Map
static hasMany = [books: Book]
}
保存和更新
def p = Person.get(1)
p.save() //没有立马保存到库
try {
p.save(flush: true) //立马保存到库
}
catch (org.springframework.dao.DataIntegrityViolationException e) {
// deal with exception
}
try {
p.save(failOnError: true) //校验失败时抛出异常
}
catch (ValidationException e) {
// deal with exception
}
删除
def p = Person.get(1)
p.delete()
try {
p.delete(flush: true)
}
catch (org.springframework.dao.DataIntegrityViolationException e) {
flash.message = "Could not delete person ${p.name}"
redirect(action: "show", id: p.id)
}
还可以用以下写法:
Customer.executeUpdate("delete Customer c where c.name = :oldName",
[oldName: "Fred"])
级联更新和级联删除
不管是一对一、一对多或者是多对多,只要定义了belongsTo,就等于定义了级联操作。
class Airport {
String name
static hasMany = [flights: Flight]
}
class Flight {
String number
static belongsTo = [airport: Airport]
}
new Airport(name: "Gatwick")
.addToFlights(new Flight(number: "BA3430"))
.addToFlights(new Flight(number: "EZ0938"))
.save()
def airport = Airport.findByName("Gatwick")
airport.delete()
以上代码定义了Airport、Flight,并且定义了Airport和Flight之间的级联关系。在新建Airport对象时,添加了些Flight,也就新建增了些Flight记录。删除airport时,也就删除了与它关联的Flight记录。
用belongsTo定义双向的one-to-many
class A { static hasMany = [bees: B] }
class B { static belongsTo = [a: A] }
这样设置会让级联策略为:one的一方是ALL,many一方是NONE
单向的one-to-many
class A { static hasMany = [bees: B] }
class B { }
这样的设置会让级联策略为SAVE_UPDATE
不用belongTo定义双向的one-to-many
class A { static hasMany = [bees: B] }
class B { A a }
这样的设置会让级联策略为one的一方为SAVE-UPDATE,many的一方为NONE
用belongsTo的单向的one-to-one
class A { }
class B { static belongsTo = [a: A] }
级联策略为:拥有的一方为ALL,belongsTo的一方为NONE