[Day 06] DSL 其他和资料库互动的方式

如果只会单一资料表的 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"] 了。


<<:  D10 第五周 (回忆篇)

>>:  [Day11] 策略最佳化模组改造(1)

[Day16] Andoroid - Kotlin笔记:null type & none-null type

这边先预祝大家中秋节快乐 连假比较忙的关系今天就挑个简单的主题来写 kotlin对於null的处理相...

基础的Git上传方法与指令

1.以下程序码中有中文的,一律要更换成自己要的名字 2.程序码中有许多空格,请务必注意不要漏掉!! ...

STM32的开发生态,Cube、HAL库、LL库

这个分享适合刚入门STM32的新手,快速了解STM32的开发生态 我昨天最後讲到我个人建议从标准库学...

【Vue】引用 bootstrap 5 不再依赖 JQuery |专案实作

npm 引用 bootstrap $ npm install bootstrap CDN 引用 bo...

抓取资料库数据 - SQL基础语法(中)

上次我们已经学会要怎麽从资料库依照各个表取出我们想要的栏位,也可以透过条件筛选的方式过滤我们想要的资...