Ktor 全栈之路 (4):SQLDelight 跨端同步 —— 前后端共用一套 SQL

0 阅读2分钟

前言

我们在本项目中一直强调“逻辑共享”。那么,作为应用数据的核心——SQL 定义,能不能也共享?

答案是:SQLDelight

SQLDelight 不是传统的 ORM,它能根据你的 SQL 语句生成对应的类型安全 Kotlin 代码。最黑科技的是,这套 SQL 可以在 Android、iOS 甚至 Ktor Server 后端完全复用


一、 为什么在后端也用 SQLDelight?

虽然 Exposed 在后端很强大,但在 KMP 全栈项目中,SQLDelight 有其不可替代的优势:

  1. 契约优先:SQL 语句就是前后端的共同契约。
  2. 绝对的模型同步:同一个 .sq 文件,生成的 Data Class 在 App 和 Server 端一模一样。
  3. 响应式支持:自带的 asFlow().mapToList() 能让你轻松实现从数据库到 UI 的全链路响应式刷新。

二、 实战:跨模块的 SQL 定义

在项目的 sharedcore 模块中创建 .sq 文件:

-- src/commonMain/sqldelight/com/yangcyzhang/database/User.sq
CREATE TABLE UserInfo (
    id INTEGER NOT NULL PRIMARY KEY,
    name TEXT NOT NULL,
    email TEXT NOT NULL
);

selectAll:
SELECT * FROM UserInfo;

insertUser:
INSERT INTO UserInfo(id, name, email)
VALUES (?, ?, ?);

三、 在 Ktor 后端接入

后端需要配置适配 JDBC 的驱动:

val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)
val database = MyDatabase(driver)

// 初始化表结构
MyDatabase.Schema.create(driver)

// 业务调用
post("/user") {
    val user = call.receive<User>()
    database.userQueries.insertUser(user.id, user.name, user.email)
    call.respond(HttpStatusCode.Created)
}

四、 “离线优先”全栈同步原型

利用 SQLDelight,你可以实现一套极其丝滑的同步逻辑:

  1. App 侧:用户操作 -> 写入本地 SQLDelight -> 标记为 "pending"。
  2. 网络侧:后台协程将 "pending" 的 SQLDelight 数据发给后端。
  3. 后端侧:接收请求 -> 写入后端同样的 SQLDelight 表结构。
  4. 一致性:由于两端代码都是根据同一个 .sq 生成的,逻辑冲突的可能性被降到了最低。

五、架构建议

  1. 驱动抽象:在 commonMain 定义一个 DatabaseDriverFactory。在 Android 传 AndroidSqliteDriver,在 iOS 传 NativeSqliteDriver,在 Server 传 JdbcSqliteDriver
  2. 性能权衡:对于极大规模的后端数据(千万级以上),Exposed 配合传统数据库(PostgreSQL/MySQL)依然是首选;但对于中小型闭环 App 或边缘计算场景,SQLDelight 能极大地提升开发效率。

结语

SQLDelight 是 KMP 全栈开发的最后一块拼图。它让数据层也实现了“一处编写,全端同步”。

下一篇,我们将进入安全核心 —— 认证与授权 (Auth):JWT 登录全流程实战