相关内容
0. 概述
Exposed
Exposed 是 JetBrains 推出的 Kotlin SQL 框架。它提供类型安全的 SQL DSL,也提供 DAO API。项目可以根据控制力、抽象程度和团队习惯选择不同用法。
Exposed的主要能力:
- 使用Kotlin DSL构造SQL
- 使用DAO方式映射数据库表和实体
- 支持事务管理
- 支持JDBC与R2DBC访问模型
- 支持多种常见关系型数据库
注意
Exposed不是MyBatis的Kotlin语法糖。它更强调以Kotlin类型系统构造查询;复杂SQL、索引设计和事务边界仍然需要开发者理解数据库本身。
Exposed 1.x之后包名带有
org.jetbrains.exposed.v1命名空间。旧版本教程中常见的org.jetbrains.exposed.sql.*需要按当前版本迁移。
1. Exposed使用
1.1 依赖导入
JDBC方式常见依赖如下:
dependencies {
implementation("org.jetbrains.exposed:exposed-core:$exposed_version")
implementation("org.jetbrains.exposed:exposed-jdbc:$exposed_version")
implementation("org.jetbrains.exposed:exposed-dao:$exposed_version")
implementation("org.jetbrains.exposed:exposed-java-time:$exposed_version")
implementation("com.mysql:mysql-connector-j:$mysql_version")
}R2DBC方式需要选择对应模块和驱动:
dependencies {
implementation("org.jetbrains.exposed:exposed-core:$exposed_version")
implementation("org.jetbrains.exposed:exposed-r2dbc:$exposed_version")
}1.2 表结构声明
import org.jetbrains.exposed.v1.core.Table
object Users : Table("users") {
val id = long("id").autoIncrement()
val username = varchar("username", 64)
val email = varchar("email", 128)
override val primaryKey = PrimaryKey(id)
}说明
object表示表结构在应用中是单例。字段使用Kotlin属性声明后,后续查询可以直接引用这些属性,避免大量字符串字段名。
1.3 连接数据库
import org.jetbrains.exposed.v1.jdbc.Database
Database.connect(
url = "jdbc:mysql://localhost:3306/demo",
driver = "com.mysql.cj.jdbc.Driver",
user = "root",
password = "password"
)注意
示例中的账号密码只用于说明。真实项目应通过配置文件、环境变量或密钥管理系统注入。
1.4 事务
Exposed DSL和DAO操作通常运行在事务中:
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
transaction {
// DSL或DAO操作
}事务原则
- 事务范围要短
- 不要在事务中执行耗时网络调用
- 服务端异步框架中不要在事件循环线程执行阻塞式JDBC事务
- R2DBC场景使用对应的
suspendTransaction
2. DSL API
2.1 新增数据
transaction {
Users.insert {
it[username] = "tom"
it[email] = "tom@example.com"
}
}2.2 查询数据
transaction {
Users
.select(Users.id, Users.username, Users.email)
.where { Users.username eq "tom" }
.forEach { row ->
println(row[Users.email])
}
}2.3 修改数据
transaction {
Users.update({ Users.username eq "tom" }) {
it[email] = "tom.new@example.com"
}
}2.4 删除数据
transaction {
Users.deleteWhere { username eq "tom" }
}3. DAO API
概述
DAO API 更接近传统ORM写法,适合围绕实体对象进行增删改查。它抽象程度更高,但对复杂查询的控制力弱于DSL。
import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.dao.id.LongIdTable
import org.jetbrains.exposed.v1.dao.LongEntity
import org.jetbrains.exposed.v1.dao.LongEntityClass
object UserTable : LongIdTable("users") {
val username = varchar("username", 64)
val email = varchar("email", 128)
}
class UserEntity(id: EntityID<Long>) : LongEntity(id) {
companion object : LongEntityClass<UserEntity>(UserTable)
var username by UserTable.username
var email by UserTable.email
}transaction {
val user = UserEntity.new {
username = "tom"
email = "tom@example.com"
}
println(user.id.value)
}4. 使用建议
选型建议
- 查询复杂、需要贴近SQL:优先DSL API
- 简单实体增删改查:可以使用DAO API
- 使用Ktor/Vert.x等非阻塞框架:谨慎使用JDBC版Exposed,必要时放到专门线程池或选择R2DBC
- 与Spring事务集成:明确事务边界,不要在多个事务体系之间混用而不加控制
常见误区
- 把Exposed当作“自动优化SQL”的工具
- 在事件循环线程中执行JDBC事务
- 在事务内做HTTP调用或长时间计算
- 忽略数据库迁移工具,直接依赖代码自动建表