老姐經過我身邊的時候看了一下我的螢幕,好奇的問:「下方這些 log 像是 SQL 指令?」
「對呀, Exposed 會把 DAO 用的 SQL 指令印出來,方便我校對邏輯或是改善查詢方式。像是妳剛剛看到的那幾行邏輯沒錯,但是查詢次數太多了,考慮到雲端服務會用資料庫查詢次數計價,最好使用 Eager loading 。」
「那是什麼?」老姐表示這個專有名詞她沒印象。
直接解釋有點抽象,所以我打開資料庫相關程式碼檔案秀給她看。「我的題目 table 有參考到其他 table ,比如作者是使用者 table,標籤也有標籤 table。」
object Topics : UUIDTable() {
val tags = reference("tags", TopicTags)
val author = reference("author", Users)
...
}
class Topic(id: EntityID<UUID>) : UUIDEntity(id) {
companion object : UUIDEntityClass<Topic>(Topics)
...
var author by User referencedOn Topics.author
var tags by TopicTag referencedOn Topics.tags
val chats by Chat.referrersOn(Chats.topic,true)
}
「題目列表直接拿的話,裡面每讀一個題目都要查詢一次作者和標籤。總題數如果是 N ,查詢量就是 1 + 2 * N。」
020-09-29 20:46:49.344 [nioEventLoopGroup-4-1] DEBUG Exposed - SELECT TOPICS.ID, TOPICS.TITLE, TOPICS.DESCRIPTION, TOPICS.TAGS, TOPICS.AUTHOR, TOPICS.COMPLETED, TOPICS.CREATED_AT, TOPICS.UPDATED_AT FROM TOPICS
2020-09-29 20:46:49.353 [nioEventLoopGroup-4-1] DEBUG Exposed - SELECT USERS.ID, USERS.AVATAR, USERS."NAME" FROM USERS WHERE USERS.ID = '64f5c7f3-9c4e-4397-8e0e-5113be4186e0'
2020-09-29 20:46:49.354 [nioEventLoopGroup-4-1] DEBUG Exposed - SELECT TOPICTAGS.ID, TOPICTAGS.TAGS, TOPICTAGS.CREATED_AT FROM TOPICTAGS WHERE TOPICTAGS.ID = '2f80dccb-2a38-4f32-b2df-0aa91b636101'
...
「如果利用快取,查詢量就會是 1+1+1 次,第一次拿題目串,第二次拿這些題目作者,第三次拿這些題目標籤。然後利用程式配對組合。利用 IN 運算子 達成範圍查詢。 」
2020-09-29 20:46:49.365 [nioEventLoopGroup-4-1] DEBUG Exposed - SELECT TOPICS.ID, TOPICS.TITLE, TOPICS.DESCRIPTION, TOPICS.TAGS, TOPICS.AUTHOR, TOPICS.COMPLETED, TOPICS.CREATED_AT, TOPICS.UPDATED_AT FROM TOPICS
2020-09-29 20:46:49.451 [nioEventLoopGroup-4-1] DEBUG Exposed - SELECT USERS.ID, USERS.AVATAR, USERS."NAME" FROM USERS WHERE USERS.ID IN ('1892daf3-a10d-45be-bf01-857723f48f84', '64f5c7f3-9c4e-4397-8e0e-5113be4186e0', '97ce0cf7-8765-4d3b-bccf-7c6d80e84218', 'a5908650-a919-4a2e-9f14-6a990d658814', 'c6bcd1ff-8d50-4637-ad54-42f58cb48026', 'd3f85e90-debe-44be-830f-9fbb7715dc45', 'ea638493-8d52-4732-a843-41df01b0281f')
2020-09-29 20:46:49.452 [nioEventLoopGroup-4-1] DEBUG Exposed - SELECT TOPICTAGS.ID, TOPICTAGS.TAGS, TOPICTAGS.CREATED_AT FROM TOPICTAGS WHERE TOPICTAGS.ID IN ('2f80dccb-2a38-4f32-b2df-0aa91b636101', '46d94d7f-e856-401a-8203-e786650f2904', '7ac74d60-45f4-4848-a884-895caced9c06', '9a3350cb-dfaa-4180-a66f-768da3eb8ce0')
「原來如此,那 DAO 程式碼寫法差別在哪?」老姐看懂了。
「只要在 with 括號裡面註明要一筆拿的參考欄位就可以。」
//Topic.all().map {//直接拿
Topic.all().with(Topic::author, Topic::tags).map {//Eager loading
TopicResponse(
id = it.id.value,
name = it.title,
avatar = it.author.avatar,
attendance = (0..10).random().toString() + "人",
tags = it.tags.title
)
}
//val chats by Chat referrersOn Chats.topic//新版本才支援預設快取
val chats by Chat.referrersOn(Chats.topic,true)
我突然想起來很重要的一件事,「對了,如果欄位用的是 referrersOn
要注意,目前最新的版本 0.27.1 還沒預設快取
,要另外設定。官網的 issue 有說未來會改。」
https://github.com/JetBrains/Exposed/issues/1046
老姐滿臉困惑的說:「我剛剛就想問 referrersOn
和 referencedOn
的意思了。」
「兩者是鏡射關係,A referencedOn
B 就會造成 B referrersOn
A。以我們題目和作者的關係來說,題目建立會記錄作者是誰,因此可以反過來查該作者有建立多少題目。」
class User(id: EntityID<UUID>) : UUIDEntity(id) {
companion object : UUIDEntityClass<User>(Users)
...
val topics by Topic.referrersOn(Topics.author, true)
}
我說著乾脆連 DAO Wiki 網頁都拿出來:「想知道更多 DAO 可以看這邊介紹 https://github.com/JetBrains/Exposed/wiki/DAO ,我也是聽人說 Eager loading 後到這邊研究的。」
本次鐵人賽的作品在放進更多內容後已經成書,書名是《老姐要用Kotlin寫專案:從 Server 到 Android APP 的開發生存日記》,歡迎購買唷。https://www.tenlong.com.tw/products/9789864348978