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的在線文檔,可以查看所有的可用的驗證規則。
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。
可以通過使用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的緩存設置。
到目前為止我們關注的都是單獨的類。現在我們來做一些全局的設置。被所有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" |