编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

Grails指南21对象关系映射进阶

wxchong 2024-06-24 20:03:41 开源技术 12 ℃ 0 评论

多对一(单向)关联举例:

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

}

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表