iT邦幫忙

2021 iThome 鐵人賽

DAY 9
1

透過 DAO 方式存取資料,除了用傳統的 join 方式處理資料表之間的關聯外,也可以直接從物件之間的關聯來思考。

下面我們來介紹 DAO 物件之間的關聯如何設計。

一對多關係

像我們之前範例的 cityuser 之間的關聯,我們可以想成是一個 city 能夠對應多個 user 的一對多關聯。

city 的部分跟之前是一樣的

object Cities : IntIdTable() {  
    val name = varchar("name", 50)  
}  
  
class City(id: EntityID<Int>) : IntEntity(id) {  
    companion object : IntEntityClass<City>(Cities)  
    var name by Cities.name  
}  

user 的部分,要改成用 reference 的方式和 city 進行關聯。

我們可以用 reference()referencedOn,來改變 user 的資料表和 DAO 物件宣告

object Users : IntIdTable() {  
    val city = reference("city", Cities)  
    val name = varchar("name", 50)  
}  
  
class User(id: EntityID<Int>) : IntEntity(id) {  
    companion object : IntEntityClass<User>(Users)  
    var city by City referencedOn Users.city  
 	var name by Users.name  
}

宣告完了之後,我們就可以用物件的方式,來處理 cityuser 之間的關聯

SchemaUtils.create(Cities)  
SchemaUtils.create(Users)  
val paris = City.new {  
 	name = "Paris"  
}  
User.new {  
 	city = paris  
    name = "Alice"  
}  
  
User  
    .all()  
    .forEach {  
 		println("${it.name}: ${it.city.name}")  
    }

我們就可以看到資料的關聯了

Alice: Paris

optional

有時候我們可能會希望某個關聯是可選擇的,這時我們可以用 optional 的方式,調整我們的資料表和 DAO 物件。

比方說,或許我們會希望允許 user 可以不輸入 city 的資料。

我們可以這樣調整 user 的資料表和 DAO 物件。

object Users : IntIdTable() {  
    val city = reference("city", Cities).nullable()  
    val name = varchar("name", 50)  
}  
  
class User(id: EntityID<Int>) : IntEntity(id) {  
    companion object : IntEntityClass<User>(Users)  
    var city by City optionalReferencedOn Users.city  
 	var name by Users.name  
}

這樣調整之後,我們就可以在寫入 user 時略過 city 物件。

SchemaUtils.create(Cities)  
SchemaUtils.create(Users)  
val paris = City.new {  
 	name = "Paris"  
}  
User.new {  
 	city = paris  
    name = "Alice"  
}  
User.new {  
 	name = "Bob"  
}  
  
User  
    .all()  
    .forEach {  
 		println("${it.name}: ${it.city?.name}")  
    }

這樣我們印出資料時就會看到

Alice: Paris
Bob: null

到這邊,資料的一對多關聯就就介紹完畢了。

什麼是 ?.

我們在 optional 關係時,印出 city.name 時,改變了一點程式

${it.city?.name}

這是為什麼呢?

Kotlin 語言在大多數的狀況下,都是不會出現 null 的。不過在 optional 的狀況下,我們有可能會出現 it.city 為 null 的狀態,導致 it.city.name 出錯的狀況。

幸運的是,Kotlin 在編譯時就可以知道這個狀況,並且在編譯時跳出提示

Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type City?

根據他的提示,我們將程式調整成 null-safe 的版本

${it.city?.name}

這樣就會在 it.citynull 的狀況下,不嘗試存取 it.city.name,避免了嘗試存取 null 參數的錯誤。


上一篇
[Day 08] Kotlin DAO 其他和資料庫互動的方式
下一篇
[Day 10 ]資料和資料之間的多對多關聯
系列文
Kotlin 怎麼操作資料庫?談談 Kotlin Exposed 框架30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言