# [Grails] GORM 參考資料 [GORM for Hibernate](http://gorm.grails.org/6.1.x/hibernate/manual/#domainClasses) 通過規約,Grails 會自動把放在 `racetrack/grails-app/domain` 里面的類識別為 domain class,且知道 domain class 需持久化 --- ## Grails 數據庫規約 * DB table 名與 domain class 名一致。 * coloum name 和 properties name 一致。 * 對於多單詞組成的 properties name 以底線分開單詞 例:maxRunners 跟 max_runners 對應。 * id 是 PK,被定義為 auto_increment。 * 每個 Grails domain class 都需要 id 和 version 屬性,所以,在編譯期 GORM 會把它們注入到 class 里面。 註:version 屬性為 Hibernate 支持事務管理和樂觀鎖用 ## Domain Modelling in GORM ### Many-to-one & one-to-one Example ```groovy= class Face { Nose nose } ``` ```groovy= class Nose { } ``` #### ``belongsTo`` Tells GORM to cascade commands: e.g., delete this object if the "parent" is deleted. ```groovy= static belongsTo = [] ``` Example: ```groovy= class Face { Nose nose } ``` ```groovy= class Nose { static belongsTo = [face:Face] } ``` #### `` hasOne`` Tells GORM to associate another domain object as an owner in a 1-1 mapping. ```groovy= static hasOne = [] ``` Example ```groovy= class Face { static hasOne = [nose:Nose] static constraints = { nose unique: true } } ``` ```groovy= class Nose { Face face } ``` :::info **hasOne** 僅在雙向關係中運作 ::: ### One-to-many #### `` hasMany`` Tells GORM to associate other domain objects for a 1-n or n-m mapping ```groovy= static hasMany = [productSpecs:ProductSpec] ``` Example ```groovy= class Author { static hasMany = [books: Book] String name } ``` ```groovy= class Book { String title } ``` 可以使用 ```cascade``` 刪除關聯的項目 ```groovy= class Book { String name static hasMany = [reviews: Review] static mappping = { reviews cascade: 'all-delete-orphan' } } class Review { String author String quote static belongsTo = [book: Book] } ``` #### ```cascade``` 可使用的參數一覽 - ```merge``` - merges the state of a detached association - ```save-update``` - cascades only saves and updates to an association - ```delete``` - cascades only deletes to an association - ```lock``` - useful if a pessimistic lock should be cascaded to its associations - ```refresh``` - cascades refreshes to an association - ```evict``` - cascades evictions (equivalent to discard() in GORM) to associations if set - ```all``` - cascade all operations to associations - ```all-delete-orphan``` - Applies only to one-to-many associations and indicates that when a child is removed from an association then it should be automatically deleted. Children are also deleted when the parent is. #### ```mappedBy``` Specifies which property should be used in a mapping ```groovy= static mappedBy = [] ``` 如果一對多的多那方具有兩個相同類型的屬性,則必須使用 mappedBy 指定要映射的集合 ```groovy= class Airport { static hasMany = [flights: Flight] static mappedBy = [flights: "departureAirport"] } ``` ```groovy= class Flight { Airport departureAirport Airport destinationAirport } ``` ### Many-to-many Grails 藉由在關係的雙方定義 ```hasMany```,以及在擁有此多對多關係的那方定義 ```belongsTo``` 建立多對多關係。 ```groovy= class Book { static belongsTo = Author static hasMany = [authors:Author] String title } ``` ```groovy= class Author { static hasMany = [books:Book] String name } ``` ___ ## Validation & Constraints Grails **validation** capability is built on Spring's Validator API and data binding capabilities. However Grails takes this further and provides a unified way to define validation constraints with its **constraints** mechanism. Most commonly they are applied to [domain classes](https://docs.grails.org/3.0.x/guide/single.html#GORM), however [URL Mappings](https://docs.grails.org/3.0.x/guide/single.html#urlmappings) and [Command Objects](https://docs.grails.org/3.0.x/guide/single.html#commandObjects) also support constraints. --- ### Declaring Constraints #### Example for domain class constraints ```groovy= class User { String login String password String email static constraints = { login size: 5..15, blank: false, unique: true password size: 5..15, blank: false email email: true, blank: false } } ``` #### Example for ==Referencing domain class properties== from constraints ```groovy= class Product { /* Default (injected) attributes of GORM */ Long id Long version StationSpec startStationSpec StationSpec endStationSpec BookingSpec bookingSpec static constraints = { bookingSpec(nullable:true) startStationSpec(nullable: true) endStationSpec(nullable: true) } } ``` :::warning By default, all domain class properties are not nullable (i.e. they have an implicit nullable: false constraint). ::: --- ### GORM & Constraints 假設有個屬性如下所示的 class ```groovy= String name String description ``` 預設情況下,Grails 在 MySQL 中會將其定義為 | Column | Data Type | | ----------- | ------------ | | name | varchar(255) | | description | varchar(255) | 但 description 的商業邏輯定義可能是「最大為 1000 字」, 若是如此,在創建資料表時可改為以下設定方式。 | Column | Data Type | | ----------- | --------- | | description | TEXT | 並加上 constraints 確保不會超過 1000 字 ```groovy= static constraints = { description maxSize: 1000 } ``` To use GORM 7.0.4 for Hibernate in Grails 3 you can specify the following configuration in build.gradle: ```groovy= dependencies { compile "org.grails.plugins:hibernate5:7.0.4" compile "org.hibernate:hibernate-ehcache" } ``` ___ ## Dependency configurations >`compile`、`runtime`、`compileOnly`、`testCompile`、`testRuntime`、`testCompileOnly`...etc. **`compile`** – 專案在編譯的階段就需要這些相依函式庫內容,這包含相依庫的子依賴,這些內容最後都會加到 classpath。 例如 Spring、Hibernate 的相依庫。 **`runtime`** – 繼承於 `compile`。專案在執行的時候會需要這些相依庫。例如 MySQL driver 的相依庫。 註:假設專案使用一個相依 A,而 A 又相依 B,專案編譯階段只需要 A,但執行階段時其實是需要 A 和 B,這是為何配置 compile 的相依可以被 runtime 看見,但反過來卻不一定正確。 **`compileOnly`** -繼承於 compile。這些相依庫基本上只有專案編譯的階段被使用,專案執行階段的時候不被使用,這些內容最後不會被加入到 `runtime` 的 `classpath` 裡。像 `source-only annotations`。 註:某相依庫的 API 在編譯時段被需要,但實作的內容則是由執行時的應用程式、其他相依、環境所提供。 參考資料:[Gradle相依性配置筆記](https://www.ajoshow.com/2017/08/06/20170806/)