如何复用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
相关产品推荐
相关产品推荐

