前面我们聊到了如何存取资料库,以及遇到 N+1 问题时该如何发现以及解决问题。
今天我们来谈谈 Exposed 框架如何非同步的存取资料。
在 Kotlin 程序语言中,支援透过协程(Coroutine)的方式操作,可以在不消耗大量资源的状况下,达成非同步操作的需求。
可惜的是,由於 Exposed 框架的实作上,有一些记忆体储存的位置,是储存在线程内。如果协程在分配时,切换到不同的线程进行操作,可能会导致无法预期的问题。所以我们不能将同一个 transaction()
切换到不同的协程进行操作。
虽然同一个 transaction()
无法切换到不同协程,不过我们可以透过其他方式,来达成非同步的需求。
transaction()
外处理资料之前的范例内,我们都是在 transaction()
内处理完资料并印出内容。
transaction {
SchemaUtils.create(Users)
User.new {
name = "Alice"
}
User.new {
name = "Bob"
}
User
.all()
.forEach {
println("name: ${it.name}")
}
}
如果我们希望将资料传输到 transaction()
以外,我们可以在 transaction()
函数前面,用一个变数接收回传值
val users = transaction {
SchemaUtils.create(Users)
User.new {
name = "Alice"
}
User.all().toList()
}
println(users.javaClass.kotlin)
users.forEach{
println(it.name)
}
这边我们透过 toList()
,将原本的 User.all()
内容转换成 java.util.ArrayList
类别。
执行这段程序之後,我们就可以看到 users
的类别和内容
class java.util.ArrayList
Alice
要注意的是,这段程序码到现在还是同步执行的。如果我们对资料库的存取很慢的话,会影响後面程序的运作。
我们用 java.lang.Thread.sleep
来模拟资料库存取很耗时间时的情况。
val users = transaction {
sleep(3000)
SchemaUtils.create(Users)
User.new {
name = "Alice"
}
User.all().toList()
}
val users2 = transaction {
sleep(3000)
SchemaUtils.create(Users)
User.new {
name = "Bob"
}
User.all().toList()
}
val users3 = transaction {
sleep(3000)
SchemaUtils.create(Users)
User.new {
name = "Carol"
}
User.all().toList()
}
(users+users2+users3).forEach{
println(it.name)
}
这段程序可以成功的印出内容
Alice
Bob
Carol
但是运作起来要消费的时间很长,因为每段程序都必须要等前面的transaction()
程序执行完毕,也就是等三秒之後,才会往下执行。
也就是说,要跑完三次transaction()
,执行的时间至少需要花费九秒以上,才能执行完成。
suspendedTransactionAsync()
要让程序能够不被 sleep(3000)
卡住,先执行後面的部分,我们可以利用 suspendedTransactionAsync()
函数改写我们的程序。
首先,将我们的 main()
宣告成 suspend
函数
suspend fun main()
再来,我们将原本的 transaction()
改写成 suspendedTransactionAsync()
val users = suspendedTransactionAsync {
sleep(3000)
SchemaUtils.create(Users)
User.new {
name = "Alice"
}
User.all().toList()
}
val users2 = suspendedTransactionAsync {
sleep(3000)
SchemaUtils.create(Users)
User.new {
name = "Bob"
}
User.all().toList()
}
val users3 = suspendedTransactionAsync {
sleep(3000)
SchemaUtils.create(Users)
User.new {
name = "Carol"
}
User.all().toList()
}
这个函数回传的内容,就不是我们之前所取得的 ArrayList
了。我们可以实际印出类别名称看看
println(users.javaClass.kotlin)
会得到
class kotlinx.coroutines.DeferredCoroutine
并且我们执行时会发现到,印出println(users.javaClass.kotlin)
内容的时间变得很快。似乎没有受到 sleep(3000)
的影响。
这是因为 suspendedTransactionAsync()
这个函数并没有直接提供给我们和资料库互动所取出的内容,而是先回传一个 DeferredCoroutine
物件,程序就直接往下执行了。
要和资料库互动,将 DeferredCoroutine
物件变成 ArrayList
物件,我们要透过 await()
函数来改写我们的程序
(users.await()
+ users2.await()
+ users3.await()).forEach {
println(it.name)
}
这样执行後,一样可以取得我们的内容。并且由於这三段没有互相等待对方执行的时间,所以执行时间会比起原先要短,不需要等九秒钟以上才能执行完毕。
>>: 【Day03-表格】为什麽熊猫(pandas)是用来处理表格的工具?
Hexo 网站设定当中,除了有标题的设定外,还有所谓的「副标题」来辅助主标题外想补充说明的内容。比方...
介绍Jenkins的章节即将进入尾声了。事实上你可能会想Jenkins默认介面这麽老气,怎麽就成为全...
购物车采用session储存,结构为Map<String,Integer>。Key为is...
大家午安 ~ 刚刚打开介面发文时,看到有 iThome 邦友订阅文章,真的是无比开心 Q 感谢大家的...
测试 为什麽要写测试 何时该订定测试 和哪些人协作 ...