一:事件和自动实现时间戳
GORM支持事件注册,只需要将事件作为一个闭包即可,当某个事件触发,比如删除,插入,更新。为了添加一个事件需要在你的领域
类中添加相关的闭包。
(1):事件类型
A:beforeInsert事件
触发当一个对象保存到数据库之前
class Person {
Date dateCreated
def beforeInsert = { dateCreated =
new Date() }
}
B: beforeUpdate事件
更新前事件
class Person {
Date dateCreated
Date lastUpdated
def beforeInsert = { dateCreated =
new Date() }
def beforeUpdate = { lastUpdated =
new Date() }
}
C:beforeDelete事件
触发但一个对象被删除
class Person {
String name
Date dateCreated
Date lastUpdated
def beforeDelete = {
new ActivityTrace(eventName:
"Person Deleted",data:name).save() }
}
D:onLoad事件
触发当一个对象从数据库取出
class Person {
String name
Date dateCreated
Date lastUpdated
def onLoad = { name =
"I'm loaded" }
}
(2):自动时间戳
上面的例子演示了使用事件来更新一个
lastUpdated
和
dateCreated
属性来跟踪对象的更新。事实上,这些设置不是必须的。通过
简单的定义一个
lastUpdated
和
dateCreated
属性 ,GORM会自动的为你更新。
如果,这些行为不是你需要的,可以屏蔽这些功能。如下设置
class Person {
Date dateCreated
Date lastUpdated
static mapping = { autoTimestamp
false }
}
二: 自定义ORM映射
Grails 的域对象可以映射到许多遗留的模型通过 关系对象映射域语言。接下来的部分将带你领略它是可能的通过ORM DSL这是必要
的,如果你高兴地坚持以约定来定义GORM对应的表,列名等。你只需要这个功能,如果你需要定制GORM 映射到遗留模型或进行
缓存自定义映射是使用静态的
mapping
块定义在你的域类中的:
class Person {
..
static mapping = {
}
}
(1) :表名和列名
A:表名
类映射到数据库的表名可以通过使用
table
关键字来定制
class Person {
..
static mapping = { table 'people' }
}
在上面的例子中,类会映射到
people
表来代替默认的
person
表
B:列名
同样,也是可能的定制某个列到数据库。比如说,你想改变列名例子如下
class Person {
String firstName
static mapping = { table 'people' firstName column:'First_Name' }
}
在这个例子中,你定义了一个
column
块,此块包含的方法调用匹配每一个属性名称(本例子中是
firstName
),接下来使用命
名的
column
来指定字段名称的映射
C:列类型
GORM还可以通过DSL的type属性来支持Hibernate类型,包括特定Hibernate的
org.hibernate.types.UserType的子类。比如,有
一个PostCodeType,你可以象下面这样使用:
class Address {
String number
String postCode
static mapping = { postCode type:PostCodeType }
}
另外如果你想将它映射到Hibernate的基本类型而不是Grails的默认类型,可以参考下面代码:
class Address {
String number
String postCode
static mapping = { postCode type:'text' }
}
上面的例子将使postCode列映射到数据库的SQL TEXT或者CLOB类型
三:映射关系
A:一对一映射
在关联中,你也有机会改变外键映射联系,在一对一的关系中,对列的操作跟其他常规的列操作并无二异,例子如下
class Person {
String firstName
Address address
static mapping = { table 'people' firstName
column:'First_Name'
address column:'Person_Adress_Id'
}
}
默认情况下,
address
将映射到一个名称为address_id 的外键。但是使用上面的映射,我们改变外键列为
Person_Adress_Id
B:一对多映射
在一个双向的一对多关系中,你可以象前节中的一对一关系中那样改变外键列,只需要在多的一端中改变列名即可。然而,在单
向关联中,外键需要在关联自身中(即一的一端-译者注)指定。比如,给定一个单向一对多联系
Person
和
Address
,下面的代
码会改变
address
表中外键
class Person {
String firstName
static hasMany = [addresses:Address]
static mapping = { table 'people'
firstName column:'First_Name'
addresses column:'Person_Address_Id'
}
}
如果你不想在
address
表中有这个列,可以通过中间关联表来完成,只需要使用joinTable参数即可:
class Person {
String firstName
static hasMany = [addresses:Address]
static mapping = {
table 'people'
firstName column:'First_Name'
addresses joinTable:[name:'Person_Addresses', key:'Person_Id', column:'Address_Id']
}
}
C:多对多映射
默认情况下,Grails中多对多的映射是通过中间表来完成的,以下面的多对多关联为例:
class Group {
…
static hasMany = [people:Person]
}
class Person {
…
static belongsTo = Group
static hasMany = [groups:Group]
}
在上面的例子中,Grails将会创建一个
group_person
表包含外键
person_id
和
group_id
对应
person
和
group
表。假如你需
要改变列名,你可以为每个类指定一个列映射
class Group {
…
static mapping = { people column:'Group_Person_Id' }
}
class Person {
…
static mapping = { groups column:'Group_Group_Id' }
}
你也可以指定中间表的名称
class Group {
…
static mapping = { people column:'Group_Person_Id',joinTable:'PERSON_GROUP_ASSOCIATIONS' }
}
class Person {
…
static mapping = { groups column:'Group_Group_Id',joinTable:'PERSON_GROUP_ASSOCIATIONS' }
}
四:缓存策略
设置缓存
hibernate 本身提供了自定义二级缓存的特性,这就需要在
grails-app/conf/DataSource.groovy
文件中配置:
hibernate { cache.use_second_level_cache=
true
cache.use_query_cache=
true
cache.provider_class='org.hibernate.cache.EhCacheProvider'
}
当然,你也可以按你所需来定制设置。比如,你想使用分布式缓存机制
缓存实例
假如要在映射代码块中启用缺省的缓存,可以通过调用
cache
方法实现:
class Person {
..
static mapping = { table 'people' cache
true }
}
上面的例子中将配置一个读-写(read-write)缓存包括lazy和non-lazy属性。假如你想定制这些特性,你可以如下所示:
class Person {
..
static mapping = { table 'people' cache usage:'read-only', include:'non-lazy' }
}
缓存关联对象
就像使用Hibernate的二级缓存来缓存实例一样,你也可以来缓存集合(关联),比如:
class Person {
String firstName
static hasMany = [addresses:Address]
static mapping = { table 'people' version
false addresses column:'Address', cache:
true }
}
class Address {
String number
String postCode
}
上面的例子中,我们在addresses集合启用了一个读-写缓存,你也可以使用
cache:'read-write' // or 'read-only' or 'transactional'
更多配置请参考缓存用法
缓存用法
下面是不同缓存设置和他们的使用方法
read-only
- 假如你的应用程序需要读但是从不需要更改持久化实例,只读缓存或许适用read-write
- 假如你的应用程序需要更新数据,读-写缓存或许是合适的nonstrict-read-write
- 假如你的应用程序仅偶尔需要更新数据(也就是说,如果这是极不可能两笔交易,将尝试更新同一项目同时)并且时进行) ,并严格交易隔离,是不是需要一个非严格-读写缓存可能是适宜的transactional
- transactional
缓存策略提供支持对全事务缓存提供比如JBoss的TreeCache。这个缓存或许仅仅使用在一个JTA环境,同时你必须在grails-app/conf/DataSource.groovy
文件中配置hibernate.transaction.manager_lookup_class
五: 自定义数据库标识符
你可以通过DSL来定制GORM生成数据库标识,缺省情况下GORM将根据原生数据库机制来生成ids,这是迄今为止最好的方法,但
是仍存在许多模式,不同的方法来生成标识。
为此,Hibernate特地定义了id生成器的概念,你可以自定义它要映射的id生成器和列,如下:
class Person {
..
static mapping = {
table 'people' version
false
id generator:'hilo', params:[table:'hi_value',column:'next_value',max_lo:100]
}
}
在上面的例子中,我们使用了Hibernate内置的'hilo'生成器,此生成器通过一个独立的表来生成ids。此外还有许多不同的生成器可以
配置,具体参考Hibernate在这个主题上的相关文档。
注意,如果你仅仅想定制列id,你可以这样
class Person {
..
static mapping = { table 'people' version
false
id column:'person_id'
}
}
六:复合主键
GORM支持复合标识(复合主键--译者注)概念(标识由两个或者更多属性组成)。这不是我们建议的方法,但是如果你想这么做,
这也是可能的:
class Person {
String firstName
String lastName
static mapping = { id composite:['firstName', 'lastName'] }
}
上面的代码将通过
Person
类的
firstName
和
lastName
属性来创建一个复合id。当你后面需要通过id取一个实例时,你必须用这个对
象的原型
def p = Person.get(
new Person(firstName:
"Fred", lastName:
"Flintstone"))
println p.firstName
七:
数据库索引
为得到最好的查询性能,通常你需要调整表的索引定义。如何调整它们是跟特定领域和要查询的用法模式相关的。使用GORM的
DSL你可以指定那个列需要索引
class Person {
String firstName
String address
static mapping = { table 'people' version
false
id column:'person_id'
firstName column:'First_Name', index:'Name_Idx'
address column:'Address', index:'Name_Idx, Address_Index'
}
}
八:乐观锁和版本定义
就像在
乐观锁和悲观锁部分讨论的,默认情况下,GORM使用乐观锁和在每一个类中自动注入一个
version
属性,此属性将映射数据
库中的一个
version
列
如果你映射的是一个遗留数据库(已经存在的数据库--译者注),这将是一个问题,因此可以通过如下方法来关闭这个功能:
class Person {
..
static mapping = { table 'people' version
false }
}
九:立即加载和延迟加载
延迟加载集合
就像在
立即加载和延迟加载部分讨论的,默认情况下,GORM 集合使用延迟加载的并且可以通过
fetchMode
来配置 。但如果你更喜
欢把你所有的映射都集中在
mappings
代码块中,你也可以使用ORM的DSL来配置获取模式:
class Person {
String firstName
static hasMany = [addresses:Address]
static mapping = { addresses lazy:
false }
}
class Address {
String street
String postCode
}
延迟加载单向关联
在 GORM中,one-to-one和many-to-one关联缺省是非延迟加载的。这在有很多实体(数据库记录-译者注)的时候,会产生性能
问题,尤其 是关联查询是以新的SELECT语句执行的时候,此时你应该将one-to-one和many-to-one关联的延迟加载象集合那样进
行设置:
class Person {
String firstName
static belongsTo = [address:Address]
static mapping = { address lazy:
true // lazily fetch the address }
}
class Address {
String street
String postCode
}
这里我们设置
Person
的
address
属性为延迟加载
十: 事务编程
Grails是构建在Spring的基础上的,所以使用Spring的事务来抽象处理事务编程,但GORM类通过
withTransaction方法使得处理更简
单,方法的第一个参数是Spring的
TransactionStatus对象
典型的使用场景如下:
def transferFunds = {
Account.withTransaction { status ->
def source = Account.get(params.from)
def dest = Account.get(params.to)
def amount = params.amount.toInteger()
if(source.active) {
source.balance -= amount
if(dest.active) {
dest.amount += amount
}
else {
status.setRollbackOnly()
}
}
}
}
在上面的例子中,如果目的账户没有处于活动状态,系统将回滚事务,同时如果有任何异常抛出在事务的处理过程中也将会自动回滚。假如你不想回滚整个事务,你也可以使用"save points"来回滚一个事务到一个特定的点。你可以通过使用Spring的SavePointManager接口来达到这个目的。
withTransaction方法为你处理begin/commit/rollback代码块作用域内的逻辑。