You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何复用Exposed的DAO类实现JSON序列化或数据传输?

Kotlin Exposed DAO复用与JSON序列化问题解决

问题背景

从Java MyBatis转到Kotlin Exposed开发,习惯用实体类同时承担数据库操作、JSON序列化和模块间数据传输的角色,但Exposed的DAO实体(比如示例中的User类)无法手动直接实例化,手动创建会抛出IllegalStateException: Property klass should be initialized before get异常,想明确是否有简便方法复用DAO类完成序列化,或是必须额外编写字段一致的DTO类。


解决方案

1. 用Exposed官方API创建DAO实例(数据库场景首选)

如果是从JSON解析数据后需要入库,直接用User.new构建实体即可,无需手动实例化:

// 假设从JSON解析得到username和password
val username = "test_user"
val password = "test_pass"

// 事务内创建并持久化实体
transaction {
    val user = User.new {
        this.username = username
        this.password = password
    }
    // 此时user可直接用于JSON序列化
}

如果需要临时构建未持久化的实体用于传输,可通过wrapRows包装空结果集实现:

transaction {
    val emptyRow = Users.selectAll().where { Users.id eq -1 }.emptyResultSet().singleOrNull()
    val user = emptyRow?.let { User.wrapRow(it) }
    user?.username = username
    user?.password = password
    // 该实例可用于序列化,但禁止调用持久化方法
}

2. 给DAO类添加序列化支持

Exposed DAO的委托属性可能导致序列化异常,可通过Jackson注解忽略内部属性解决:

import com.fasterxml.jackson.annotation.JsonIgnoreProperties

@JsonIgnoreProperties("entityId", "klass", "table")
class User(id: EntityID<Int>) : IntEntity(id) {
    companion object : IntEntityClass<User>(Users)
    var username by Users.username
    var password by Users.password
}

这种方式无需额外编写DTO,直接复用DAO类完成序列化。

3. 定义DTO类+扩展方法(通用方案)

如果需要严格分离数据库实体和传输对象,可编写DTO并通过扩展方法简化转换:

// 定义DTO类
data class UserDTO(val username: String, val password: String)

// DAO转DTO扩展方法
fun User.toDTO() = UserDTO(username, password)

// DTO转DAO扩展方法
fun UserDTO.toUser(): User = transaction {
    User.new {
        username = this@toUser.username
        password = this@toUser.password
    }
}

4. 放弃DAO API,用普通数据类配合Table操作

如果更习惯MyBatis的Bean模式,可直接用普通数据类配合Exposed Table完成数据库操作,数据类可自由实例化和序列化:

object Users : IntIdTable() {
    val username = varchar("username", 64)
    val password = varchar("password", 64)
}

data class UserDTO(val id: Int? = null, val username: String, val password: String)

// 插入示例
transaction {
    val id = Users.insert {
        it[username] = "test_user"
        it[password] = "test_pass"
    } get Users.id
    val user = UserDTO(id.value, "test_user", "test_pass")
}

// 查询示例
transaction {
    Users.selectAll().map {
        UserDTO(it[Users.id].value, it[Users.username], it[Users.password])
    }
}

结论

  • 若坚持使用DAO API,优先用new/wrapRows创建实例,配合Jackson忽略注解实现序列化;
  • 追求职责分离和通用性,推荐定义DTO类并通过扩展方法完成转换;
  • 想贴近Java开发习惯,直接用普通数据类配合Table操作,避免DAO的内部限制。

内容的提问来源于stack exchange,提问作者Firok

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 14:20:27