多对一(单向)关联举例:
class Face {
Nose nose
}
class Nose {
}
一个脸只能有一个鼻子,但一个鼻子可以对应多个脸,即多张脸可以共用一个鼻子,多对一。概括来说,这是由脸到鼻子的单向多对一关系,注意,是单向。
#########################
一对一(伪-双向)关联举例:
class Face {
Nose nose
}
class Nose {
static belongsTo = [face:Face]
}
一个脸对应一个鼻子,且一个鼻子只属于一张脸,即脸与鼻子是一一对应的,一对一。belongsTo,即从属于,在此例子中不难看出,Face脸为主,Nose鼻子为从,这就意味着你可以创建一个脸,再附加一个鼻子,保存脸时,鼻子也将一并存入数据库,即级联添加:
new Face(nose:new Nose()).save() //正确做法
错误做法:
new Nose(face:new Face()).save() //主从颠倒,报错
值得注意的是,由于从属关系,当脸删除时,鼻子也将一并删除,即级联删除:
def f = Face.get(1)
f.delete() // 脸与鼻子将一并被删除
#########################
一对一(真-双向)关联举例:
class Face {
static hasOne = [nose:Nose]
}
class Nose {
static belongsTo = [face:Face] //支持“级联添加”、“级联修改”、“级联删除”
}
等价于
class Face {
static hasOne = [nose:Nose]
}
class Nose {
Face face //支持“级联添加”、“级联修改”、“级联删除”,即便没用belongsTo
}
注意事项:hasOne只能用于双向关联中
#########################
外键约束举例:
class Face {
static hasOne = [nose:Nose]
static constraints = {
nose unique: true
}
}
class Nose {
Face face
}
#########################
一对多(单向)关联举例:
class Author {
static hasMany = [books: Book]
String name
}
class Book {
String title
}
一个作者出多本书,但书中没有作者相关的信息,即作者与书之间是一对多单向关联的。
#########################
实际中使用案例:
def author Author.get(1)
for (book in author.books) {
println book.title
}
#########################
一对多(双向)关联中的级联相关说明:
class Author {
static hasMany = [books: Book]
String name
}
class Book {
Author author //支持“级联添加”、“级联修改”,不支持“级联删除”
String title
}
###########
class Author {
static hasMany = [books: Book]
String name
}
class Book {
static belongsTo = [author: Author] //支持“级联添加”、“级联修改”、“级联删除”(belongsTo)
String title
}
#########################
mappedBy的使用方法举例:
class Hand {
static hasMany = [leftFingers: Finger, rightFingers: Finger]
static mappedBy = [leftFingers: "leftHand", rightFingers: "rightHand"]
}
class Finger {
Hand leftHand
Hand rightHand
// static belongsTo = [leftHand: Hand, rightHand: Hand]
static constraints = {
leftHand(nullable: true, blank: true)
rightHand(nullable: true, blank: true)
}
}
#########################
多对多关联:
class Book {
static belongsTo = Author //说明主体是Author,Book是从
static hasMany = [authors:Author]
String title
}
class Author {
static hasMany = [books:Book]
String name
}
具体使用方法举例:
new Author(name:"Stephen King")
.addToBooks(new Book(title:"The Stand"))
.addToBooks(new Book(title:"The Shining"))
.save(flush: true) //正确,Author是主
错误做法:
new Book(name:"Groovy in Action")
.addToAuthors(new Author(name:"Dierk Koenig"))
.addToAuthors(new Author(name:"Guillaume Laforge"))
.save() //错误,Author是主
#########################
默认使用joinTable的情况:
class Person {
static hasMany = [nicknames: String]
}
joinTable的定制与修改:
class Person {
static hasMany = [nicknames: String] //昵称
static mapping = {
nicknames joinTable: [name: 'bunch_o_nicknames', //昵称列表
key: 'person_id', //用户id
column: 'nickname', //昵称字段
type: "text"] //昵称字段-数据类型
}
}
#########################
批量删除:
Person.where {
name == "Fred"
}.deleteAll()
Customer.executeUpdate("delete Customer c where c.name = :oldName", [oldName: "Fred"])
#########################
立即加载与延迟加载:
class Airport {
String name
static hasMany = [flights: Flight]
}
class Flight {
String number
Location destination
static belongsTo = [airport: Airport]
}
class Location {
String city
String country
}
Grails默认采取的加载方式是延迟加载。延迟加载中会涉及到N+1查询问题,如下:
def airport = Airport.findByName("Gatwick") //忽略
for (flight in airport.flights) { //N
println flight.destination.city //1
}
推荐解决方法:
Author.list(fetch: [location: 'join']).each { a ->
println a.location.city
}
Author.findAllByNameLike("John%", [ sort: 'name', order: 'asc', fetch: [location: 'join'] ]).each { a->
...
}
Author.where {
name == "Stephen King"
}.join('location').list()
#########################
为解决立即加载中存在的性能问题,出现了立即加载变体:
class Airport {
String name
static hasMany = [flights: Flight]
static mapping = {
flights batchSize: 10
}
}
#########################
乐观锁与悲观锁
乐观锁举例:
def airport = Airport.get(10)
try {
airport.name = "Heathrow"
airport.save(flush: true)
}
catch (org.springframework.dao.OptimisticLockingFailureException e) {
// deal with exception
}
悲观锁举例1:
def airport = Airport.get(10)
airport.lock() // lock for update
airport.name = "Heathrow"
airport.save()
悲观锁举例2:
def airport = Airport.lock(10) // lock for update
airport.name = "Heathrow"
airport.save()
悲观锁举例3:
def airport = Airport.findByName("Heathrow", [lock: true])
悲观锁举例4:
def airport = Airport.createCriteria().get {
eq('name', 'Heathrow')
lock true
}
#########################
脏数据检查例1:
def airport = Airport.get(10)
println airport.isDirty()
airport.properties = params
println airport.isDirty()
脏数据检查例2:
def airport = Airport.get(10)
println airport.isDirty()
airport.properties = params
println airport.isDirty('name')
脏数据检查例3:
def airport = Airport.get(10)
println airport.isDirty()
airport.properties = params
def modifiedFieldNames = airport.getDirtyPropertyNames()
for (fieldName in modifiedFieldNames) {
// do something based on changed value
}
本文暂时没有评论,来添加一个吧(●'◡'●)