grails备忘录(二)

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