如果只会单一资料表的 CRUD 操作,那麽有很多需求是没有办法满足的。
今天我们来聊聊,怎麽用 DSL 的方式进行其他的操作。
如果我们要一次写入多笔资料,我们可以透过 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
参数取出资料
Cities
.selectAll()
.limit(2, 1)
.forEach {
println("City #${it[Cities.id]}: ${it[Cities.name]}")
}
可以看到
City #2: Moscow
City #3: Helsinki
我们可以用 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
如果我们有多张资料表,像是
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() }
}
想取出这两张资料表的内容,并利用 cityId
和 city.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"]
了。
这边先预祝大家中秋节快乐 连假比较忙的关系今天就挑个简单的主题来写 kotlin对於null的处理相...
1.以下程序码中有中文的,一律要更换成自己要的名字 2.程序码中有许多空格,请务必注意不要漏掉!! ...
这个分享适合刚入门STM32的新手,快速了解STM32的开发生态 我昨天最後讲到我个人建议从标准库学...
npm 引用 bootstrap $ npm install bootstrap CDN 引用 bo...
上次我们已经学会要怎麽从资料库依照各个表取出我们想要的栏位,也可以透过条件筛选的方式过滤我们想要的资...