Exposed 是 JetBrains 在数年前推出的轻量级 ORM 框架,Kotlin 编写,已经在 JetBrains 内部多个关键产品使用。

Exposed 是基于 JDBC 实现,屏蔽了底层建立数据库连接,编写 SQL,操作数据,关闭数据库连接的操作,只需要关心数据操作。

Exposed 提供了两种形式 API,面向 DSL 的 API,面向对象的 API。

特点

  • 纯 Kotlin 实现
  • 类似 SQL 的静态类型化语言,可以轻松查询数据库
  • 减少样板代码
  • 支持非常多的数据库,H2,MySQL,MariaDB,Oracle,PostgreSQL,SQL Server,SQLite 等

使用

首先需要添加依赖

<dependency>
    <groupId>org.jetbrains.exposed</groupId>
    <artifactId>exposed-core</artifactId>
    <version>0.37.3</version>
</dependency>
<dependency>
    <groupId>org.jetbrains.exposed</groupId>
    <artifactId>exposed-dao</artifactId>
    <version>0.37.3</version>
</dependency>
<dependency>
    <groupId>org.jetbrains.exposed</groupId>
    <artifactId>exposed-jdbc</artifactId>
    <version>0.37.3</version>
</dependency>

数据库连接

Database.connect("", driver = "")

DSL 和 DAO 都需要在 transaction 中执行

transaction {
  // do
  commit()
}

DSL

DSL 是领域特定语言。

import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.transactions.transaction

object Member : Table("member") {
    val id = integer("id").autoIncrement()
    val name = varchar("name", 32)
    val createdAt = timestamp("created_at")
}

fun main() {
    Database.connect(
        url = "jdbc:mysql://127.0.0.1:3306/exposed_example",
        driver = "com.mysql.cj.jdbc.Driver",
        user = "root",
        password = "mysql"
    )
    transaction {
        addLogger(StdOutSqlLogger)

        val id = Member.insert {
            it[name] = "Kotlin"
            it[createdAt] = Instant.now()
        } get Member.id
        println("Inserted id: $id")

        val member: ResultRow = Member.select(Member.id eq id).single()
        println("id: ${member[Member.id]}")
        println("name: ${member[Member.name]}")

        Member.update {
            it[createdAt] = Instant.now()
        }
        Member.deleteWhere { Member.id eq id }
    }
}

DAO

import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.StdOutSqlLogger
import org.jetbrains.exposed.sql.addLogger
import org.jetbrains.exposed.sql.transactions.transaction

// 创建名为 member 的表,添加 name 字段
object MemberTable : IntIdTable("member") {
    val name = varchar("name", 32)
    val createdAt = timestamp("created_at)
}

// 创建实体类
class MemberEntity(id: EntityID<Int>) : IntEntity(id) {
    companion object : IntEntityClass<MemberEntity>(MemberTable)

    var name by MemberTable.name
    var createdAt by MemberTable.createdAt
}

fun main() {
    Database.connect(
        url = "jdbc:mysql://127.0.0.1:3306/exposed_example",
        driver = "com.mysql.cj.jdbc.Driver",
        user = "root",
        password = "mysql"
    )
    transaction {
        addLogger(StdOutSqlLogger)

        val member = MemberEntity.new {
            name = "Kotlin"
            createdAt = Instant.now()
        }
        println("Inserted id: ${member.id}")

        MemberEntity.findById(member.id)?.let {
            println("id: ${it.id}")
            println("name: ${it.name}")
        }

        val result = MemberEntity.findById(member.id)
        
        result?.createAt = Instant.now()
        result?.delete()
    }
}

索引定义

单列或者多列索引

object Members : IntIdTable("members") {

    val index = index("idx_name_other", true, name, other)
}

Spring Boot 集成

<dependencies>
  <dependency>
    <groupId>org.jetbrains.exposed</groupId>
    <artifactId>exposed-spring-boot-starter</artifactId>
    <version>0.37.3</version>
  </dependency>
</dependencies>