# [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/)