如何避免Kotlin暴露的N + 1查询问题。 (通过DAO的Reference.id.value字段获取值时)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何避免Kotlin暴露的N + 1查询问题。 (通过DAO的Reference.id.value字段获取值时)相关的知识,希望对你有一定的参考价值。

我在Kotlin JVM项目上使用Exposed作为O / R映射器。(版本:0.17.6)

当我从DAO API(由Exposed的reference方法定义的列中)获得外键列的值时,我遇到了N + 1查询问题。

我以某种解决方法解决了问题,但是有人知道正确的解决方案吗?

这里是情况。

有两个表(用户,用户地址),一个表(用户地址表)在另一个表(用户表)上有外键引用。

object Users : LongIdTable("users") {
    val name = varchar("name", 30)
}
class User(id: EntityID<Long>) : LongEntity(id) {
    companion object : LongEntityClass<User>(Users)

    var name by Users.name
}

object UserAddresses : LongIdTable("user_addresses") {
    val user = reference("user_id", Users)
    val address = varchar("address", 30)
}
class UserAddress(id: EntityID<Long>) : LongEntity(id) {
    companion object : LongEntityClass<UserAddress>(UserAddresses)

    var user by User.referencedOn(UserAddresses.user)
    var address by UserAddresses.address

    fun toRow(): UserAddressRow {    
        return UserAddressRow(
            id.value,
            user.id.value,
            address
        )
    }
}

data class UserAddressRow(val id: Long, val userId: Long, val address: String)

[当我尝试获取List<UserAddressRow>时,出现N + 1查询问题。我发现当我调用Reference.getValue时,将在以下位置执行SELECT查询。https://github.com/JetBrains/Exposed/blob/master/exposed-dao/src/main/kotlin/org/jetbrains/exposed/dao/Entity.kt#L129

@Test
fun test() {
    transaction {
        UserAddress.all().map {
            it.toRow()
        }
    }
}    

TestData

INSERT INTO users (id, name) VALUES (1, 'A');
INSERT INTO users (id, name) VALUES (2, 'B');
INSERT INTO user_addresses (user_id, address) VALUES (1, 'X');
INSERT INTO user_addresses (user_id, address) VALUES (1, 'Y');
INSERT INTO user_addresses (user_id, address) VALUES (2, 'Z');

SQL查询日志(公开的调试日志)

[] 2019-11-22 10:41:55.656 [main] DEBUG [cid- uid--] Exposed - SELECT user_addresses.id, user_addresses.user_id, user_addresses.address FROM user_addresses
[] 2019-11-22 10:41:55.668 [main] DEBUG [cid- uid--] Exposed - SELECT users.id, users."name" FROM users WHERE users.id = 1
[] 2019-11-22 10:41:55.670 [main] DEBUG [cid- uid--] Exposed - SELECT users.id, users."name" FROM users WHERE users.id = 2

如果我使用readValue而不是user.id,则问题已解决。我不确定此解决方法是否正确。请帮助我。

   fun toRow(): UserAddressRow {    
        return UserAddressRow(
            id.value,
            readValues[UserAddressTable.user].value,
            address
        )
    }
答案

我注意到下面的简单模式会更好。

//Define userId column in DAO (UserAddress class), and use it
class UserAddress(id: EntityID<Long>) : LongEntity(id) {
    companion object : LongEntityClass<UserAddress>(UserAddresses)

    var user by User.referencedOn(UserAddresses.user)
    var userId by UserAddresses.user
    var address by UserAddresses.address

    fun toRow(): UserAddressRow {
        return UserAddressRow(
            id.value,
            userId.value,
            address
        )
    }
}

//There is only one SQL statement executed
//SELECT user_addresses.id, user_addresses.user_id, user_addresses.address FROM user_addresses

以上是关于如何避免Kotlin暴露的N + 1查询问题。 (通过DAO的Reference.id.value字段获取值时)的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot Kotlin 暴露存储过程

Django Serializer 嵌套创建:如何避免对关系的 N+1 查询

Salesforce REST API 如何避免在查询参数中泄露敏感数据

Kotlin 暴露 SELECT 单条记录

如何避免Kotlin 的陷阱?

Rails,如何避免对关联中的总数(计数、大小、counter_cache)进行“N + 1”查询?