You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Exposed框架的SchemaUtils维护数据库迁移历史?

针对你使用Ktor 2.3.7 + Exposed 0.45.0开发API时遇到的迁移问题,给出具体实现方案:

1. 执行表结构变更的迁移操作

Exposed的SchemaUtils提供了多种方法处理不同类型的表结构变更,按需选择即可:

自动补全缺失表/字段

如果仅需新增表或给现有表添加字段,使用createMissingTablesAndColumns即可自动完成,不会修改已有字段或数据:

transaction(database) {
    SchemaUtils.createMissingTablesAndColumns(Articles)
}

比如你给Articles表新增了author字段,执行这段代码会自动在数据库中添加该列。

手动执行复杂变更(修改字段类型、删除字段等)

对于修改现有字段类型、删除字段这类破坏性操作,需要通过alter方法手动编写变更逻辑:

transaction(database) {
    // 修改Articles表的content字段为TEXT类型
    Articles.alter {
        modifyColumn(Articles.content, TextColumnType())
    }

    // 删除Articles表的oldField字段
    Articles.alter {
        dropColumn(Articles.oldField)
    }
}

注意事项

  • 执行变更前务必备份数据库,避免数据丢失
  • 部分数据库(如MySQL)修改字段类型可能需要额外配置,需结合数据库特性调整

2. 实现迁移历史管理(类似RoR/Liquibase)

Exposed本身没有内置迁移历史表,需自行实现。核心逻辑是创建一个专门记录迁移记录的表,每次执行迁移前检查是否已执行,避免重复运行。

步骤1:定义迁移历史表

object MigrationHistory : Table() {
    val id = varchar("id", 255).primaryKey() // 迁移唯一标识,如"20240520_add_author_column"
    val executedAt = datetime("executed_at").defaultExpression(CurrentDateTime)
    val description = varchar("description", 255)
}

步骤2:创建迁移执行工具类

定义迁移接口和执行逻辑:

interface Migration {
    val id: String
    val description: String
    fun up()
}

// 示例迁移:给Articles表添加author字段
class AddAuthorColumnMigration : Migration {
    override val id = "20240520_add_author_column"
    override val description = "Add author column to Articles table"

    override fun up() {
        Articles.alter {
            addColumn(Articles.author, varchar("author", 255).nullable())
        }
    }
}

// 执行所有未运行的迁移
fun runMigrations(database: Database) {
    transaction(database) {
        // 先创建迁移历史表(如果不存在)
        SchemaUtils.createMissingTablesAndColumns(MigrationHistory)

        val executedMigrations = MigrationHistory.id.selectAll().map { it[MigrationHistory.id] }.toSet()

        // 按顺序定义所有迁移
        val migrations = listOf(
            AddAuthorColumnMigration(),
            // 后续新增迁移依次添加在这里
        )

        migrations.forEach { migration ->
            if (migration.id !in executedMigrations) {
                println("Executing migration: ${migration.id} - ${migration.description}")
                migration.up()
                // 记录到迁移历史表
                MigrationHistory.insert {
                    it[id] = migration.id
                    it[description] = migration.description
                }
            } else {
                println("Migration already executed: ${migration.id}")
            }
        }
    }
}

步骤3:在Ktor启动时执行迁移

在应用启动逻辑中调用迁移执行方法:

fun main() {
    val database = Database.connect(/* 你的数据库连接配置 */)
    runMigrations(database)

    embeddedServer(Netty, port = 8080, module = Application::module).start(wait = true)
}

这样就能通过数据库表维护迁移记录,每次启动应用时自动执行未完成的迁移。

内容的提问来源于stack exchange,提问作者vishwas-trivedi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 09:25:18