iT邦幫忙

2021 iThome 鐵人賽

DAY 6
0

如果只會單一資料表的 CRUD 操作,那麼有很多需求是沒有辦法滿足的。

今天我們來聊聊,怎麼用 DSL 的方式進行其他的操作。

Batch Insert

如果我們要一次寫入多筆資料,我們可以透過 batchInsert() 這樣處理

val cityNames = listOf("Paris", "Moscow", "Helsinki")
cities
    .batchInsert(cityNames) { name ->
    	this[cities.name] = name
	}

寫入之後,我們看看資料是否存在於資料庫裡面

Cities  
    .selectAll()  
    .forEach {  
        println("City #${it[Cities.id]}: ${it[Cities.name]}")  
    }

這樣我們就可以看到

City #1: Paris
City #2: Moscow
City #3: Helsinki

我們一開始宣告的資料,成功寫在資料庫裡面了

Limit 和 offset

如果我們有限制需要取出資料的筆數,我們可以用 limit() 函數,搭配 offset 參數取出資料

Cities  
    .selectAll()  
    .limit(2, 1)  
    .forEach {
		println("City #${it[Cities.id]}: ${it[Cities.name]}")  
    }

可以看到

City #2: Moscow
City #3: Helsinki

Order By

我們可以用 orderBy() 函數,幫我們對取出的資料進行排序

Cities
    .selectAll()
	.orderBy(Cities.name to SortOrder.ASC)
	.forEach {  
		 println("City #${it[Cities.id]}: ${it[Cities.name]}")  
	}

執行後,可以看到我們的資料順序改變了

City #3: Helsinki
City #2: Moscow
City #1: Paris

Join

如果我們有多張資料表,像是

object Cities : IntIdTable() {  
    val name = varchar("name", 50)  
}  
  
object Users : IntIdTable() {  
    val cityId = integer("cityId")  
    val name = varchar("name", 50)  
}

並且資料表的內容如下

SchemaUtils.create(Cities)  
SchemaUtils.create(Users)  
val cityNames = listOf("Paris", "Moscow", "Helsinki")  
val users = listOf(  
	mapOf("name" to "Alice", "cityId" to "1"),  
   	mapOf("name" to "Bob", "cityId" to "2"),  
 	mapOf("name" to "Carol", "cityId" to "2"),  
 	mapOf("name" to "Dave", "cityId" to "3"),  
 	mapOf("name" to "Eve", "cityId" to "3"),  
)  
Cities  
    .batchInsert(cityNames) { name ->  
 	this[Cities.name] = name  
}  
Users  
    .batchInsert(users) { user ->  
 		user["name"]?.let { this[Users.name] = it }  
 		user["cityId"]?.let { this[Users.cityId] = it.toInt() }  
 }

想取出這兩張資料表的內容,並利用 cityIdcity.id 做一對一的比對,我們可以透過 JOIN 的方式達成

Users  
    .join(  
        Cities,  
 		JoinType.INNER,  
 		additionalConstraint = { Users.cityId eq Cities.id })  
    .selectAll()  
    .forEach {  
 		println("${it[Users.name]}: ${it[Cities.name]}")
    }

這樣我們會看到

Alice: Paris
Bob: Moscow
Carol: Moscow
Dave: Helsinki
Eve: Helsinki

成功的印出了我們使用者的名稱,以及使用者對應城市的名稱。

下面我們來介紹一下上面的操作中,使用到的 kotlin 特殊語法

什麼是 listOf

在寫入多筆資料時,我們使用了 listOf() 這個函數

val cityNames = listOf("Paris", "Moscow", "Helsinki")

這個函數,根據官方的說明 List,提供一個類似陣列的結構。

由於 kotlin 的特性,所以能夠知道 cityNames 是一個 List<String> 的結構,並且在

Cities  
    .batchInsert(cityNames) { name ->  
 	this[Cities.name] = name  
}  

內進行迴圈時,保證了 name 一定是 String 型態,避免程式運行時出現型態衝突的錯誤。

什麼是 this

我們看到這段程式
 

Cities  
    .batchInsert(cityNames) { name ->  
 	this[Cities.name] = name  
}  

裏面突然用了 this 這個關鍵字。

這是因為在 batchInsert() 時,後方 lambda 實際的輸入值,變成了 cityNames 內部的各個值,也就是這邊所寫的 name

所以,要宣告我們寫入的對象,我們變成要呼叫 batchInsert() 參數最後 Lambda 函數內所用到的 BatchInsertStatement 物件。要存取這個物件,用 this 就可以很直觀的取得。

什麼是 let()

在寫入 Users 資料表時,這邊使用的程式碼如下

val users = listOf(  
	mapOf("name" to "Alice", "cityId" to "1"),  
   	mapOf("name" to "Bob", "cityId" to "2"),  
 	mapOf("name" to "Carol", "cityId" to "2"),  
 	mapOf("name" to "Dave", "cityId" to "3"),  
 	mapOf("name" to "Eve", "cityId" to "3"),  
)  

Users  
    .batchInsert(users) { user ->  
 		user["name"]?.let { this[Users.name] = it }  
 		user["cityId"]?.let { this[Users.cityId] = it.toInt() }  
 }

mapOf() 的結構比較好推測出來,就是其他語言的 key-value pair 或者 map 結構。但是為什麼這邊要用個 ?.let 來處理後續的行為呢?

這是因為,透過 map建立出來的 user 資料,結構為 Map<String, String>,如果我們透過 user["name"] 嘗試取出資料時,這筆資料有可能會是 null

在 kotlin 語言中,如果沒有特別宣告可以接受 null 的資料結構,在編譯時就會檢查是否有程式嘗試寫入可能為 null 的值,並拋出編譯錯誤。

也就是說,如果我們不用某種方式,保證我們寫入的值不會是 null 的話,那麼這段程式是無法編譯的。

所以,我們做了一段檢查

user["name"]?.let { this[Users.name] = it }

只有在 user["name"] 不是 null 的時候,才會執行 let() 裏面的內容。

let() 函數的行為,根據 kotlin 官方文件 let,是這樣說的

let can be used to invoke one or more functions on results of call chains.

這裡我們利用這個函數,讓 this[Users.name] = user["name"] 的操作,必須在確認 user["name"] 不是 null 之後才會執行。

let() 後面的 lambda 內,由於呼叫 let() 的對象已經是 user["name"],所以我們不用再重複宣告一次這個變數。只要在 let() 裏面宣告 it,kotlin 就知道我們說的是 user["name"] 了。


上一篇
[Day 05] Exposed 和資料庫進行互動的方式之一:DSL
下一篇
[Day 07] 透過 DAO 和資料庫進行互動
系列文
Kotlin 怎麼操作資料庫?談談 Kotlin Exposed 框架30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言