
ORM后端数据存储【免费下载链接】ExposedKotlin SQL Framework项目地址https://gitcode.com/gh_mirrors/ex/Exposed点击查看免费下载本文是一份以 JetBrains ExposedKotlin SQL FrameworkDAO API 为核心的上手指南全程围绕一个可运行的简单控制台应用展开你将学会配置内存数据库连接、用IntIdTable定义表、用IntEntity定义实体并在事务中完成 Create、Read、Update、Delete 全流程操作。读完本文你将掌握 Exposed DAO 的核心开发模式并能在自己的 Kotlin 项目中直接迁移这套写法。文中所涉及的完整示例代码位于 documentation-website/Writerside/snippets/get-started-with-exposed-dao可在仓库中对照阅读。前置准备开始之前请确保你的机器上具备以下环境Gradle最新发行版用于初始化与管理项目构建JDK版本 8 或更高IDE如 IntelliJ IDEA。官方推荐 IntelliJ IDEA Ultimate其内置数据库工具并支持 Exposed 插件可提供代码补全、检查以及根据已有 schema 生成表与实体的能力。创建新项目在终端中创建一个新目录并进入mkdir exposed-dao-kotlin-app cd exposed-dao-kotlin-app运行gradle init初始化 Gradle 项目gradle init按提示选择以下选项项目类型选择1: Application实现语言选择2: Kotlin其余问题直接回车使用默认值即可。初始化完成后在 IDE 中打开项目IntelliJ IDEA 可直接执行idea .。添加依赖使用 Exposed 前需要为项目添加依赖。首先打开gradle/libs.versions.toml定义 Exposed 与 H2 的版本和构件当前仓库自身的版本声明可参考 gradle/libs.versions.toml[versions] //... exposed %exposed_version% h2 %h2_db_version% [libraries] //... exposed-core { module org.jetbrains.exposed:exposed-core, version.ref exposed } exposed-dao { module org.jetbrains.exposed:exposed-dao, version.ref exposed } exposed-jdbc { module org.jetbrains.exposed:exposed-jdbc, version.ref exposed } h2 { module com.h2database:h2, version.ref h2 }三个 Exposed 构件各有分工exposed-core提供以类型安全方式操作数据库所需的基础组件与抽象包含 DSL APIexposed-dao提供 Data Access ObjectDAOAPI即本文主角exposed-jdbcexposed-core的扩展增加 Java Database ConnectivityJDBC支持。然后打开app/build.gradle.kts在dependencies块中加入这些模块dependencies { //... implementation(libs.exposed.core) implementation(libs.exposed.dao) implementation(libs.exposed.jdbc) implementation(libs.h2) //... }修改完构建脚本后在 IntelliJ IDEA 中点击编辑器右侧的 Gradle 通知图标加载 Gradle 变更。配置数据库连接使用 Exposed 访问数据库时第一步是获取连接并创建事务。连接配置通过Database.connect()完成。打开app/src/main/kotlin/org/example/App.kt写入如下实现package org.example import org.jetbrains.exposed.v1.jdbc.Database fun main() { Database.connect(jdbc:h2:mem:test, driver org.h2.Driver) // ... }Database.connect()创建了一个代表数据库的实例这里传入了连接 URL 与驱动两个参数。逐段拆解 URLjdbc:h2:mem:testjdbc表示这是一个 JDBC 连接h2表示目标数据库是 H2mem表示数据库运行在内存中数据只存在于内存应用停止即丢失test数据库名称。org.h2.Driver指定用于建立连接的 H2 JDBC 驱动。两个值得注意的行为惰性连接调用Database.connect()只配置连接设置并不会立即建立数据库连接真正的连接在首次执行数据库操作时才建立自动注册默认情况下 Exposed 会自动注册该数据库连接如需改变这一行为可在调用Database.connect()时设置connectionAutoRegistration参数。从源码结构看Database.connect()在 Database.kt 中提供了多个重载支持传入 URL、驱动、数据源DataSource等不同形式的连接配置便于在真实项目中接入连接池。至此你已为 Kotlin 项目添加了 Exposed 并配置好数据库连接接下来可以定义数据模型并使用 DAO API 与数据库交互。定义表对象Exposed 的 DAO API 提供了基类IdTable及其子类用于定义以标准id列作为主键的表。在app/src/main/kotlin/org/example/目录下新建Task.kt文件添加表定义package org.example import org.jetbrains.exposed.v1.core.dao.id.IntIdTable object Tasks : IntIdTable(tasks) { val title varchar(name, 128) val description varchar(description, 128) val isCompleted bool(completed).default(false) }在IntIdTable构造函数中传入名称tasks即为表配置了自定义名称。如果不提供名称Exposed 会根据对象名推导表名这可能因命名规范不同而产生意外结果。Tasks对象定义了以下列title与descriptionString类型列使用varchar()创建每列最大长度 128 字符isCompletedBoolean类型列使用bool()定义并通过default(false)将默认值配置为false。IntIdTable会自动为表添加一个自增整数id列作为主键。这一点在源码中有直接体现见 IdTable.ktIntIdTable的内部实现即为final override val id: ColumnEntityIDInt integer(columnName).autoIncrement(sequenceName).entityId() final override val primaryKey PrimaryKey(id)也就是说integer()定义 4 字节整型列、autoIncrement()声明自增可传sequenceName指定序列名、entityId()将其包装为EntityIDInt并最终声明为表的主键。到这里你已经定义了一个带列的表相当于为tasks表创建了蓝图。定义实体使用 DAO 方式时每个基于IntIdTable定义的表都必须关联一个对应的实体类实体类详细说明见 DAO-Entity-definition.topic。实体类代表表中的单条记录由主键唯一标识。继续完善Task.ktpackage org.example import org.jetbrains.exposed.v1.core.dao.id.EntityID import org.jetbrains.exposed.v1.core.dao.id.IntIdTable import org.jetbrains.exposed.v1.dao.IntEntity import org.jetbrains.exposed.v1.dao.IntEntityClass object Tasks : IntIdTable(tasks) { val title varchar(name, 128) val description varchar(description, 128) val isCompleted bool(completed).default(false) } class Task(id: EntityIDInt) : IntEntity(id) { companion object : IntEntityClassTask(Tasks) var title by Tasks.title var description by Tasks.description var isCompleted by Tasks.isCompleted override fun toString(): String { return Task(id$id, title$title, completed$isCompleted) } }逐项理解这段代码Task继承IntEntity它是主键类型为Int的实体基类。源码定义见 IntEntity.ktabstract class IntEntity(id: EntityIDInt) : EntityInt(id)构造函数中的EntityIDInt参数代表该实体所映射的数据库行的主键companion object继承IntEntityClassTask将实体类与Tasks表绑定同时承担查询入口的职责源码见 IntEntity.kttitle、description、isCompleted三个属性通过 Kotlin 的by关键字委托给Tasks表中的对应列toString()自定义了Task实例的字符串表示便于调试与日志输出打印结果将包含实体 ID、标题与完成状态。创建和查询表借助 DAO API你可以用类型安全、面向对象的方式操作数据库就像操作普通 Kotlin 类一样。所有数据库操作都必须运行在事务内事务由Transaction类的实例表示你可以在其 lambda 中定义和操作数据Exposed 会在后台自动管理事务的打开与关闭事务的完整说明见 Transactions.md。打开App.kt加入事务函数package org.example import org.jetbrains.exposed.v1.core.StdOutSqlLogger import org.jetbrains.exposed.v1.jdbc.Database import org.jetbrains.exposed.v1.jdbc.SchemaUtils import org.jetbrains.exposed.v1.jdbc.transactions.transaction fun main() { Database.connect(jdbc:h2:mem:test, driver org.h2.Driver) transaction { addLogger(StdOutSqlLogger) SchemaUtils.create(Tasks) val task1 Task.new { title Learn Exposed DAO description Follow the DAO tutorial } val task2 Task.new { title Read The Hobbit description Read chapter one isCompleted true } println(Created new tasks with ids ${task1.id} and ${task2.id}) val completed Task.find { Tasks.isCompleted eq true }.toList() println(Completed tasks: ${completed.count()}) } }首先使用SchemaUtils.create()创建tasks表。SchemaUtils对象持有创建、修改和删除数据库对象的工具方法。表创建完成后使用IntEntityClass的扩展方法.new()添加两条新的Task记录val task1 Task.new { title Learn Exposed DAO description Follow the DAO tutorial } val task2 Task.new { title Read The Hobbit description Read chapter one isCompleted true }从源码结构看new()在 EntityClass.kt 中定义接受一个以实体为接收者的初始化 lambdaExposed 会在插入后把生成的主键回填到实体的id属性上。示例中task1、task2都是Task实体的实例各自代表Tasks表中的新行在new块内为各列赋值后Exposed 会翻译为如下 SQLINSERT INTO TASKS (name, DESCRIPTION, COMPLETED) VALUES (Learn Exposed DAO, Follow the DAO tutorial, FALSE) INSERT INTO TASKS (name, DESCRIPTION, COMPLETED) VALUES (Read The Hobbit, Read chapter one, TRUE)随后用.find()执行带条件的查询检索所有isCompleted为true的任务val completed Task.find { Tasks.isCompleted eq true }.toList() println(Completed tasks: ${completed.count()})find {}接收一个类型安全的条件 lambdaTasks.isCompleted eq true返回可迭代的结果集toList()触发实际查询执行。注意 DAO 查询是惰性的——查询不会立即执行直到你调用遍历结果的方法如toList()、forEach()时才真正发起 SQL。在测试代码之前最好能看到 Exposed 发送给数据库的 SQL 语句为此需要添加日志。启用日志在transaction块的开头加入如下代码以启用 SQL 查询日志transaction { addLogger(StdOutSqlLogger) SchemaUtils.create(Tasks) // ... }addLogger(StdOutSqlLogger)将 SQL 语句输出到标准输出stdout方便观察 Exposed 实际生成的 DDL 与 DML。运行应用在 IntelliJ IDEA 中点击 gutter 区域的运行按钮启动应用应用将在底部Run工具窗口启动你可以在那里看到 SQL 日志与打印结果SQL: SELECT SETTING_VALUE FROM INFORMATION_SCHEMA.SETTINGS WHERE SETTING_NAME MODE SQL: CREATE TABLE IF NOT EXISTS TASKS (ID INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(128) NOT NULL, DESCRIPTION VARCHAR(128) NOT NULL, COMPLETED BOOLEAN DEFAULT FALSE NOT NULL) SQL: INSERT INTO TASKS (name, DESCRIPTION, COMPLETED) VALUES (Learn Exposed DAO, Follow the DAO tutorial, FALSE) SQL: INSERT INTO TASKS (name, DESCRIPTION, COMPLETED) VALUES (Read The Hobbit, Read chapter one, TRUE) Created new tasks with ids 1 and 2 SQL: SELECT TASKS.ID, TASKS.name, TASKS.DESCRIPTION, TASKS.COMPLETED FROM TASKS WHERE TASKS.COMPLETED TRUE Completed tasks: 1对照输出可以看出SchemaUtils.create(Tasks)生成了CREATE TABLE IF NOT EXISTS两次Task.new分别产生 INSERTTask.find生成了带WHERE TASKS.COMPLETED TRUE的 SELECT。这也验证了 DAO 实体到 SQL 的映射关系。更新和删除任务下面扩展应用功能更新并删除任务。在同一个transaction()函数中追加代码对应 snippets 中的 App.kt// Update task1.title Try Exposed DAO task1.isCompleted true println(Updated task1: $task1) // Delete task2.delete() println(Remaining tasks: ${Task.all().toList()})更新实体属性就像修改普通 Kotlin 类的属性一样简单直接对task1.title和task1.isCompleted赋值即可。删除则调用实体的.delete()方法task2.delete()会将该行从数据库中移除。关键机制延迟刷新lazy flush。Exposed 在修改实体属性如task1.title时并不会立即发出UPDATE语句而是将改动缓存在内存中在下一次读操作之前或事务结束时统一刷新到数据库。因此你会在日志中看到这样的输出顺序SQL: UPDATE TASKS SET COMPLETEDTRUE, nameTry Exposed DAO WHERE ID 1重启应用IntelliJ IDEA 中点击 rerun 按钮后应看到以下完整结果SQL: SELECT SETTING_VALUE FROM INFORMATION_SCHEMA.SETTINGS WHERE SETTING_NAME MODE SQL: CREATE TABLE IF NOT EXISTS TASKS (ID INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(128) NOT NULL, DESCRIPTION VARCHAR(128) NOT NULL, COMPLETED BOOLEAN DEFAULT FALSE NOT NULL) SQL: INSERT INTO TASKS (name, DESCRIPTION, COMPLETED) VALUES (Learn Exposed DAO, Follow the DAO tutorial, FALSE) SQL: INSERT INTO TASKS (name, DESCRIPTION, COMPLETED) VALUES (Read The Hobbit, Read chapter one, TRUE) Created new tasks with ids 1 and 2 SQL: SELECT TASKS.ID, TASKS.name, TASKS.DESCRIPTION, TASKS.COMPLETED FROM TASKS WHERE TASKS.COMPLETED TRUE Completed tasks: 1 Updated task1: Task(id1, titleTry Exposed DAO, completedtrue) SQL: UPDATE TASKS SET COMPLETEDTRUE, nameTry Exposed DAO WHERE ID 1 SQL: DELETE FROM TASKS WHERE TASKS.ID 2 SQL: SELECT TASKS.ID, TASKS.name, TASKS.DESCRIPTION, TASKS.COMPLETED FROM TASKS Remaining tasks: [Task(id1, titleTry Exposed DAO, completedtrue)]注意日志中Updated task1: Task(id1, ...)打印在 UPDATE 之前这正是因为toString()读取的是内存中尚未刷新的属性值随后 Exposed 才在下次读取Task.all()前把改动 flush 到数据库。最后Task.all()列出表中全部剩余任务确认task2已被删除。关于内存数据库的一个提醒如果在此之后打开第二个事务你可能会发现表和数据都消失了——即使应用还没有停止。这是 H2 内存数据库在管理连接与事务时的预期行为。若希望数据库保持打开可在 URL 中追加DB_CLOSE_DELAY-1Database.connect(jdbc:h2:mem:test;DB_CLOSE_DELAY-1, driver org.h2.Driver)下一步继续深入 DAO API你已经用 Exposed 的 DAO API 构建了一个简单的控制台应用完成了内存数据库中的建表、插入、查询、更新与删除。接下来可以从两个方向继续深入学习完整的 CRUD 操作掌握批量操作、复杂查询等进阶用法学习如何在实体间定义关系一对一、一对多、多对多等。这些章节将帮助你用 Exposed 类型安全、面向对象的方式构建更复杂、更贴近真实业务的数据模型。赞分享ORM后端数据存储【免费下载链接】ExposedKotlin SQL Framework项目地址https://gitcode.com/gh_mirrors/ex/Exposed点击查看免费下载相关推荐数据科学走进现实世界Data-Science-For-Beginners 课程第 20 课“真实世界应用”全景解读数据科学走进现实世界Data Science For Beginners 课程第 20 课“真实世界应用”全景解读 本篇以 Data Science ForORM后端数据存储如何快速上手WeUI5分钟搭建微信风格界面的完整教程如何快速上手WeUI5分钟搭建微信风格界面的完整教程 WeUI是微信官方设计团队专门为微信网页开发量身打造的UI框架能够帮助开发者快速构建与微信原生体验一致UI组件前端如何快速上手TiDB面向新手的完整使用教程如何快速上手TiDB面向新手的完整使用教程 TiDB是一个开源的分布式NewSQL数据库它结合了传统关系型数据库的最佳特性和NoSQL系统的可扩展性。作为一上一篇为什么选择Toga5大理由让你爱上这个Python GUI工具包下一篇PingFangSC字体跨平台解决方案实现苹果原生字体体验的统一技术架构创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考