Hibernate, TopLink, 和 Java
Persistence API (JPA) 是目前最流行的三个ORM 方面的中间件,虽然没有一个是完美的。
GORM 是基于Hibernate开发的。这就意味着所有与Hibernate相关的知识都可以在Grails中灵活运用。例如:HBM 映射文件和注解都可以获得全部的支持。
面向对象的数据库和Grals
一些开发人员使用面向对象的数据库来解决数据库和对象之间的不匹配问题。但是使用GORM和传统的关系型数据库是Grails推荐的持久化策略。
创建一对多的关系:
把POGO保存到数据库的困难很容易被低估。事实上,如果把每个POGO都映射到各自的表上,事情是非常简单的。POGO的属性对应表的列。但是当复杂性增加时,如:有两个对象,两个对象间互相关联,那么事情马上就变得困难起来。
例如:我们有个Domain 类Trip.groovy:
class Trip {
String name
String city
Date startDate
Date endDate
String purpose
String notes
}
|
每个属性都可以很容易的映射到Trip表的对应的列上。
旅行通常都可能需要飞机,所以有理由创建一个Airline 类:
class Airline {
String name
String url
String
frequentFlyer
String notes
}
|
现在需要关联这两个类。安排使用Xyz航空的到芝加哥的旅行,需要在Trip类中添加Airline属性。这种方式叫 object composition。
class Trip {
String name
String city
...
Airline airline
}
|
这种方式在面向对象的语言中工作的很好,但是在关系型数据库中就有些不同了。在表中的每条记录都有一个主键。在Trip表中添加airline_id列就会连接两条记录。一对多的关系是指:一个 airline可以和多个trip相关联。
你已经在Trip中添加了一个 Airline 属性。为了完成一个一对多的关系,需要在Airline中添加一个hasMany 属性:
class Airline {
static hasMany
= [trip:Trip]
String name
String url
String
frequentFlyer
String notes
}
|
关联删除
按照上面的例子,有可能在数据库中遗留一些不完整的记录:当删除Airline对象时,相关的Trip记录就会指向一个不存在的记录。为了避免这种情况的发生,需要在一对多关系的多的一方添加静态的belongsTo 属性。
静态的hasMany是一个hashmap: 键是trip;值是Trip类。如果需要在Airline类中添加其他的一对多关系,只需要添加相应的键值对就可以。
现在创建一个AirlineController 类来查看一下一对多的关系是如何使用的:
class AirlineController {
def scaffold =
Airline
}
|
Scaffold的作用是告诉Grails动态产生基本的 list(), save(), 和 edit() 方法。他也会自动的产生相应的GSP视图。确保在 TripController 和 AirlineController 中包含 def
scaffold。
数据验证
除了指定显示顺序外,静态的 constraints 块还可以用来指定验证的规则。例如:你可以指定最大的长度;你可以确保String 会符合一定的模式(是不是email或URL);你甚至可以指定这个属性是不是必须输入的。通过Grails的在线文档,可以查看所有的可用的验证规则。
下面向Airline中添加验证规则:
class Airline {
static
constraints = {
name(blank:false, maxSize:100)
url(url:true)
frequentFlyer(blank:true)
notes(maxSize:1500)
}
static hasMany
= [trip:Trip]
String name
String url
String
frequentFlyer
String notes
String
toString(){
return name
}
}
|
可以在grails-app/i18n 目录下的message.propterties文件中自定义警告信息。当字符串的长度超过255时,会自动转换成textArea。
Grails ORM DSL
可以通过使用HBM 映射文件或注释来改变Hibernate的缺省行为。但是Grails提供了另外的一种方式:通过静态的mapping 块来改变缺省的表名和列名:
class Airline {
static mapping
= {
table
'some_other_table_name'
columns {
name
column:'airline_name'
url
column:'link'
frequentFlyer column:'ff_id'
}
}
static
constraints = {
name(blank:false, maxSize:100)
url(url:true)
frequentFlyer(blank:true)
notes(maxSize:1500)
}
static hasMany
= [trip:Trip]
String name
String url
String
frequentFlyer
String notes
String
toString(){
return name
}
}
|
当在遗留的数据库上创建grails程序时,mapping块是非常有用的。ORM DSL 允许做更多的事情,比如:修改数据类型;主键的产生策略;指定组合主键、可以修改Hibernate的缓存设置。
了解 DataSource.groovy
到目前为止我们关注的都是单独的类。现在我们来做一些全局的设置。被所有Domain类所共享的数据库设置都被保存在一个单独的文件里:
grails-app/conf/DataSource.groovy,
dataSource {
pooled = false
driverClassName
= "org.hsqldb.jdbcDriver"
username =
"sa"
password =
""
}
hibernate {
cache.use_second_level_cache=true
cache.use_query_cache=true
cache.provider_class='org.hibernate.cache.EhCacheProvider'
}
// environment specific settings
environments {
development {
dataSource {
dbCreate =
"create-drop" // one of 'create', 'create-drop','update'
url =
"jdbc:hsqldb:mem:devDB"
}
}
test {
dataSource {
dbCreate =
"update"
url =
"jdbc:hsqldb:mem:testDb"
}
}
production {
dataSource {
dbCreate =
"update"
url =
"jdbc:hsqldb:file:prodDb;shutdown=true"
}
}
}
|
在dataSource 中可以修改用来连接数据库的driverClassName, username, 和 password。Hibernate 允许进行缓存的设置。但是真正有趣的事情发生在environments 中。
Grails可以运行在三种模式下:development,test 和 production。当你输入grails prod run-app时,你在告诉Grails使用production中定义的数据库设置。environment 中的设置会覆盖dataSource 中的设置。
Url设置是 JDBC 连接字符串。注意在产品模式下HSQLDB 使用的是基于文件的存储模式。在
development 和 test 模式下, HSQLDB 使用的是基于内存的存储模式。
dbCreate 他是 hibernate.hbm2ddl.auto 的别名,用来指定 Hibernate如果管理你的表。设置
dbCreate 为 create-drop 告诉 Hibernate 在程序启动时创建表,在程序关闭时删除表;如果是create, Hibernate 会创建新的表并进行必要的修改,但是重启后记录会被删除;Update 保存所有的记录,并进行必要的表的创建和修改。
如果在遗留数据库上进行开发,强烈建议不要使用dbCreate。 这样Hibernate就不会进行schema的操作。
添加自定义的 environment也非常简单。例如:在DataSource.groovy中创建一个beta 块。使用 grails -Dgrails.env=beta run-app,就会使用beta的设置。
改变数据库
在允许Hibernate管理表的情况下,使用新的数据库非常简单:创建数据库、登录、拷贝驱动程序到lib目录下,修改DataSource.groovy 中的设置。
DB2 的设置
driverClassName
= "com.ibm.db2.jcc.DB2Driver"
username =
"db2admin"
password =
"db2admin"
url =
"jdbc:db2://localhost:50000/trip"
|
MySQL 的设置
driverClassName
= "com.mysql.jdbc.Driver"
username =
"grails"
password =
"server"
url =
"jdbc:mysql://localhost:3306/trip?autoreconnect=true"
|