[Day 09] 资料和资料之间的一对多关联

透过 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 9] 从零开始的股票预测 - 筹码面

>>:  Day4:梯度下降法(Gradient descent)

Day20 探讨setting(2)

昨天介绍到SECRET_KEY,不晓得前一天的东西大家有没有完全了解了呢! 那今天我们再来接着继续介...

用我的 iMac 把 Clear Linux 安装到 USB 外接硬碟上

一般而言,要安装 Linux,又要保留原本的系统,就得再先了解一下硬碟分割,通常都会以 GRUB ...

Day 19:Kotlin 分组(groupBy)集合资料用法

本篇文章同步发表在 HKT 线上教室 部落格,线上影音教学课程已上架至 Udemy 和 Youtu...

Day2: 什麽是演算法?— Algorithms

图片来源:*https://www.dcard.tw/f/funny/p/225385728 大家...

【Day 12】 实作 - 透过 AWS 服务 - Athena 建立以及查询资料表

大家午安~ 在第 8、9 天我们完成 Data Collection 以及 Google Analy...