Project Icon

Exposed

Kotlin语言的轻量级SQL库和ORM框架

Exposed是一个为Kotlin语言设计的轻量级SQL库和ORM框架,基于JDBC驱动。它提供类型安全的SQL DSL和轻量级DAO两种数据库访问方式。Exposed支持MySQL、PostgreSQL等多种主流数据库,便于开发不依赖特定数据库的应用,并能在不同数据库间轻松切换。该框架特点是API简洁、查询功能强大且具有良好的可扩展性。

Exposed标志


JetBrains团队项目 Slack频道 TC构建状态 Maven中央仓库 GitHub许可证

欢迎来到Exposed,一个适用于Kotlin的ORM框架。

Exposed是一个基于JDBC驱动的轻量级SQL库,专为Kotlin语言设计。 Exposed提供两种数据库访问方式:类型安全的SQL包装DSL和轻量级数据访问对象(DAO)。

使用Exposed,你可以选择包装DSL或轻量级DAO进行数据库访问。我们的官方吉祥物是墨鱼,以其出色的模仿能力而闻名,能够无缝融入任何环境。 与我们的吉祥物类似,Exposed可以用来模仿各种数据库引擎,帮助你构建不依赖于特定数据库引擎的应用程序,并且可以在它们之间切换,几乎不需要或只需很少的更改。

支持的数据库

  • H2(2.x版本;1.x版本已弃用,将在未来版本中移除)
  • MariaDB
  • MySQL
  • Oracle
  • Postgres (同时支持使用pgjdbc-ng JDBC驱动的PostgreSQL)
  • MicrosoftSQLServer
  • SQLite

依赖项

Maven中央仓库配置

Exposed的发布版本可在Maven中央仓库中获取。你可以在构建脚本中声明此仓库,如下所示:

Gradle Groovy和Kotlin DSL

警告: 你可能需要将Kotlin JVM目标设置为8,使用Spring时设置为17,以确保正常工作:

repositories {
    // 0.30.1之后的版本
    // 0.30.1之前的版本目前不可用
    mavenCentral()
}

Maven

Maven用户默认启用Maven中央仓库。

Exposed模块

Exposed包含以下模块:

  • exposed-core - 基础模块,包含DSL API和映射
  • exposed-crypt - 提供额外的列类型,用于在数据库中存储加密数据并在客户端进行编码/解码
  • exposed-dao - DAO API
  • exposed-java-time - 基于Java8 Time API的日期时间扩展
  • exposed-jdbc - 基于Java JDBC API的传输层实现
  • exposed-jodatime - 基于JodaTime库的日期时间扩展
  • exposed-json - JSON和JSONB数据类型扩展
  • exposed-kotlin-datetime - 基于kotlinx-datetime的日期时间扩展
  • exposed-money - 支持"javax.money:money-api"中MonetaryAmount的扩展
  • exposed-spring-boot-starter - Spring Boot的启动器,用于使用Exposed作为ORM,而不是Hibernate

<dependencies>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-core</artifactId>
        <version>0.53.0</version>
    </dependency>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-crypt</artifactId>
        <version>0.53.0</version>
    </dependency>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-dao</artifactId>
        <version>0.53.0</version>
    </dependency>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-java-time</artifactId>
        <version>0.53.0</version>
    </dependency>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-jdbc</artifactId>
        <version>0.53.0</version>
    </dependency>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-jodatime</artifactId>
        <version>0.53.0</version>
    </dependency>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-json</artifactId>
        <version>0.53.0</version>
    </dependency>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-kotlin-datetime</artifactId>
        <version>0.53.0</version>
    </dependency>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-money</artifactId>
        <version>0.53.0</version>
    </dependency>
    <dependency>
        <groupId>org.jetbrains.exposed</groupId>
        <artifactId>exposed-spring-boot-starter</artifactId>
        <version>0.53.0</version>
    </dependency>
</dependencies>

Gradle Groovy

dependencies {
    implementation 'org.jetbrains.exposed:exposed-core:0.53.0'
    implementation 'org.jetbrains.exposed:exposed-crypt:0.53.0'
    implementation 'org.jetbrains.exposed:exposed-dao:0.53.0'
    implementation 'org.jetbrains.exposed:exposed-jdbc:0.53.0'
    
    implementation 'org.jetbrains.exposed:exposed-jodatime:0.53.0'
    // 或
    implementation 'org.jetbrains.exposed:exposed-java-time:0.53.0'
    // 或
    implementation 'org.jetbrains.exposed:exposed-kotlin-datetime:0.53.0'
    
    implementation 'org.jetbrains.exposed:exposed-json:0.53.0'
    implementation 'org.jetbrains.exposed:exposed-money:0.53.0'
    implementation 'org.jetbrains.exposed:exposed-spring-boot-starter:0.53.0'
}

Gradle Kotlin DSL

build.gradle.kts中:

val exposedVersion: String by project
dependencies {
    implementation("org.jetbrains.exposed:exposed-core:$exposedVersion")
    implementation("org.jetbrains.exposed:exposed-crypt:$exposedVersion")
    implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion")
    implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion")
    
    implementation("org.jetbrains.exposed:exposed-jodatime:$exposedVersion")
    // 或
    implementation("org.jetbrains.exposed:exposed-java-time:$exposedVersion")
    // 或
    implementation("org.jetbrains.exposed:exposed-kotlin-datetime:$exposedVersion")
    
    implementation("org.jetbrains.exposed:exposed-json:$exposedVersion")
    implementation("org.jetbrains.exposed:exposed-money:$exposedVersion")
    implementation("org.jetbrains.exposed:exposed-spring-boot-starter:$exposedVersion")
}

gradle.properties

exposedVersion=0.53.0

示例

查看示例以快速入门。

链接

目前,Exposed可用于maven/gradle构建。查看Maven中央仓库并阅读入门指南以了解如何设置Exposed。

欲了解更多信息,请访问以下链接:

提交问题

请注意,我们正在从GitHub Issues转移到YouTrack来报告错误和功能。您必须登录才能查看和记录问题,否则将会遇到404错误。

社区

有问题吗?欢迎申请邀请加入kotlinlang slack,并在我们的#exposed频道参与项目讨论。

拉取请求

我们欢迎您的拉取请求。但是,我们更倾向于将您的工作与现有问题关联起来。

  • 从主分支创建分支并为其命名。
  • 为您的分支选择一个能描述您正在进行的工作的名称。例如:adds-new-thing。
  • 如果您添加了应该被测试的代码,请添加测试并确保测试套件通过。
  • 确保解决所有的lint警告。
  • 如果您改进了现有代码,请在PR描述中告诉我们。

更多详细信息,请参阅贡献指南

示例

SQL DSL

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

object Users : Table() {
    val id: Column<String> = varchar("id", 10)
    val name: Column<String> = varchar("name", length = 50)
    val cityId: Column<Int?> = (integer("city_id") references Cities.id).nullable()

    override val primaryKey = PrimaryKey(id, name = "PK_User_ID") // 这里的name是可选的
}

object Cities : Table() {
    val id: Column<Int> = integer("id").autoIncrement()
    val name: Column<String> = varchar("name", 50)

    override val primaryKey = PrimaryKey(id, name = "PK_Cities_ID")
}

fun main() {
    Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver", user = "root", password = "")

    transaction {
        addLogger(StdOutSqlLogger)

        SchemaUtils.create(Cities, Users)

        val saintPetersburgId = Cities.insert {
            it[name] = "圣彼得堡"
        } get Cities.id

        val munichId = Cities.insert {
            it[name] = "慕尼黑"
        } get Cities.id

        val pragueId = Cities.insert {
            it.update(name, stringLiteral("   布拉格   ").trim().substring(1, 2))
        }[Cities.id]

        val pragueName = Cities.selectAll().where { Cities.id eq pragueId }.single()[Cities.name]
        println("布拉格名称 = $pragueName")

        Users.insert {
            it[id] = "andrey"
            it[name] = "安德
Users.deleteWhere{ Users.name like "%thing" }

println("所有城市:")

for (city in Cities.selectAll()) {
    println("${city[Cities.id]}: ${city[Cities.name]}")
}

println("手动连接:")

(Users innerJoin Cities)
    .select(Users.name, Cities.name)
    .where {
        (Users.id.eq("andrey") or Users.name.eq("Sergey")) and
            Users.id.eq("sergey") and Users.cityId.eq(Cities.id)
    }.forEach { 
        println("${it[Users.name]} 住在 ${it[Cities.name]}") 
    }

println("使用外键的连接:")

(Users innerJoin Cities)
    .select(Users.name, Users.cityId, Cities.name)
    .where { Cities.name.eq("St. Petersburg") or Users.cityId.isNull() }
    .forEach { 
        if (it[Users.cityId] != null) { 
            println("${it[Users.name]} 住在 ${it[Cities.name]}") 
        } 
        else { 
            println("${it[Users.name]} 没有住处") 
        } 
    }

println("函数和分组:")

((Cities innerJoin Users)
    .select(Cities.name, Users.id.count())
    .groupBy(Cities.name)
    ).forEach {
        val cityName = it[Cities.name]
        val userCount = it[Users.id.count()]

        if (userCount > 0) {
            println("$cityName 有 $userCount 个用户")
        } else {
            println("$cityName 没有人居住")
        }
    }

SchemaUtils.drop(Users, Cities)
}
}

生成的SQL:

SQL: CREATE TABLE IF NOT EXISTS Cities (id INT AUTO_INCREMENT, name VARCHAR(50) NOT NULL, CONSTRAINT PK_Cities_ID PRIMARY KEY (id))
SQL: CREATE TABLE IF NOT EXISTS Users (id VARCHAR(10), name VARCHAR(50) NOT NULL, city_id INT NULL, CONSTRAINT PK_User_ID PRIMARY KEY (id), CONSTRAINT FK_Users_city_id__ID FOREIGN KEY (city_id) REFERENCES Cities(id) ON DELETE RESTRICT ON UPDATE RESTRICT)
SQL: INSERT INTO Cities (name) VALUES ('St. Petersburg')
SQL: INSERT INTO Cities (name) VALUES ('Munich')
SQL: INSERT INTO Cities (name) VALUES (SUBSTRING(TRIM('   Prague   '), 1, 2))
SQL: SELECT Cities.id, Cities.name FROM Cities WHERE Cities.id = 3
pragueName = Pr
SQL: INSERT INTO Users (id, name, city_id) VALUES ('andrey', 'Andrey', 1)
SQL: INSERT INTO Users (id, name, city_id) VALUES ('sergey', 'Sergey', 2)
SQL: INSERT INTO Users (id, name, city_id) VALUES ('eugene', 'Eugene', 2)
SQL: INSERT INTO Users (id, name, city_id) VALUES ('alex', 'Alex', NULL)
SQL: INSERT INTO Users (id, name, city_id) VALUES ('smth', 'Something', NULL)
SQL: UPDATE Users SET name='Alexey' WHERE Users.id = 'alex'
SQL: DELETE FROM Users WHERE Users.name LIKE '%thing'
所有城市:
SQL: SELECT Cities.id, Cities.name FROM Cities
1: St. Petersburg
2: Munich
3: Pr
手动连接:
SQL: SELECT Users.name, Cities.name FROM Users INNER JOIN Cities ON Cities.id = Users.city_id WHERE ((Users.id = 'andrey') or (Users.name = 'Sergey')) and (Users.id = 'sergey') and (Users.city_id = Cities.id)
Sergey 住在 Munich
使用外键的连接:
SQL: SELECT Users.name, Users.city_id, Cities.name FROM Users INNER JOIN Cities ON Cities.id = Users.city_id WHERE (Cities.name = 'St. Petersburg') or (Users.city_id IS NULL)
Andrey 住在 St. Petersburg
函数和分组:
SQL: SELECT Cities.name, COUNT(Users.id) FROM Cities INNER JOIN Users ON Cities.id = Users.city_id GROUP BY Cities.name
St. Petersburg 有 1 个用户
Munich 有 2 个用户
SQL: DROP TABLE IF EXISTS Users
SQL: DROP TABLE IF EXISTS Cities

DAO

import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction

object Users : IntIdTable() {
    val name = varchar("name", 50).index()
    val city = reference("city", Cities)
    val age = integer("age")
}

object Cities: IntIdTable() {
    val name = varchar("name", 50)
}

class User(id: EntityID<Int>) : IntEntity(id) {
    companion object : IntEntityClass<User>(Users)

    var name by Users.name
    var city by City referencedOn Users.city
    var age by Users.age
}

class City(id: EntityID<Int>) : IntEntity(id) {
    companion object : IntEntityClass<City>(Cities)

    var name by Cities.name
    val users by User referrersOn Users.city
}

fun main() {
    Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver", user = "root", password = "")

    transaction {
        addLogger(StdOutSqlLogger)

        SchemaUtils.create(Cities, Users)

        val stPete = City.new {
            name = "St. Petersburg"
        }

        val munich = City.new {
            name = "Munich"
        }

        User.new {
            name = "a"
            city = stPete
            age = 5
        }

        User.new {
            name = "b"
            city = stPete
            age = 27
        }

        User.new {
            name = "c"
            city = munich
            age = 42
        }

        println("城市: ${City.all().joinToString { it.name }}")
        println("${stPete.name}的用户: ${stPete.users.joinToString { it.name }}")
        println("成年人: ${User.find { Users.age greaterEq 18 }.joinToString { it.name }}")
    }
}

生成的SQL:

SQL: CREATE TABLE IF NOT EXISTS Cities (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL)
SQL: CREATE TABLE IF NOT EXISTS Users (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, city INT NOT NULL, age INT NOT NULL, CONSTRAINT FK_Users_city__ID FOREIGN KEY (city) REFERENCES Cities(id) ON DELETE RESTRICT ON UPDATE RESTRICT)
SQL: CREATE INDEX Users_name ON Users (name)
SQL: INSERT INTO Cities (name) VALUES ('St. Petersburg')
SQL: INSERT INTO Cities (name) VALUES ('Munich')
SQL: SELECT Cities.id, Cities.name FROM Cities
城市: St. Petersburg, Munich
SQL: INSERT INTO Users (name, city, age) VALUES ('a', 1, 5)
SQL: INSERT INTO Users (name, city, age) VALUES ('b', 1, 27)
SQL: INSERT INTO Users (name, city, age) VALUES ('c', 2, 42)
SQL: SELECT Users.id, Users.name, Users.city, Users.age FROM Users WHERE Users.city = 1
St. Petersburg的用户: a, b
SQL: SELECT Users.id, Users.name, Users.city, Users.age FROM Users WHERE Users.age >= 18
成年人: b, c

贡献

在贡献之前,请参阅贡献指南

通过向Exposed项目贡献,您同意您的贡献将根据Apache License, Version 2.0许可。

项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

吐司

探索Tensor.Art平台的独特AI模型,免费访问各种图像生成与AI训练工具,从Stable Diffusion等基础模型开始,轻松实现创新图像生成。体验前沿的AI技术,推动个人和企业的创新发展。

Project Cover

SubCat字幕猫

SubCat字幕猫APP是一款创新的视频播放器,它将改变您观看视频的方式!SubCat结合了先进的人工智能技术,为您提供即时视频字幕翻译,无论是本地视频还是网络流媒体,让您轻松享受各种语言的内容。

Project Cover

美间AI

美间AI创意设计平台,利用前沿AI技术,为设计师和营销人员提供一站式设计解决方案。从智能海报到3D效果图,再到文案生成,美间让创意设计更简单、更高效。

Project Cover

AIWritePaper论文写作

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号