iT邦幫忙

2025 iThome 鐵人賽

DAY 4
0

看完 src/main/kotlin/Routing.kt 裡面所定義的幾個路由之後,接著我們來看看跟資料庫操作相關的程式

我們先看 src/main/kotlin/UsersSchema.kt 裡面

@Serializable
data class ExposedUser(val name: String, val age: Int)

這邊定義了一個 data class,包含了兩個屬性 nameage。注意這邊和之前要序列化的物件一樣
,要加上 @Serializable 標記,讓 ktor 知道這個物件是可以被序列化的。

接著我們看 UserService

object Users : Table() {
    val id = integer("id").autoIncrement()
    val name = varchar("name", length = 50)
    val age = integer("age")

    override val primaryKey = PrimaryKey(id)
}

這邊定義了 Users 資料表的內容,定義了 nameage 在資料庫內的資料型態。

接著下面定義了資料表初始化,以及 CRUD 的操作

init {
    transaction(database) {
        SchemaUtils.create(Users)
    }
}

suspend fun create(user: ExposedUser): Int = dbQuery {
    Users.insert {
        it[name] = user.name
        it[age] = user.age
    }[Users.id]
}

suspend fun read(id: Int): ExposedUser? {
    return dbQuery {
        Users.selectAll()
            .where { Users.id eq id }
            .map { ExposedUser(it[Users.name], it[Users.age]) }
            .singleOrNull()
    }
}

suspend fun update(id: Int, user: ExposedUser) {
    dbQuery {
        Users.update({ Users.id eq id }) {
            it[name] = user.name
            it[age] = user.age
        }
    }
}

suspend fun delete(id: Int) {
    dbQuery {
        Users.deleteWhere { Users.id.eq(id) }
    }
}

private suspend fun <T> dbQuery(block: suspend () -> T): T =
    newSuspendedTransaction(Dispatchers.IO) { block() }

這邊使用了 Exposed 框架定義的 DSL 語法,可以用程式語法的方式撰寫 DB Query,類似其他框架的 Query Builder。

定義好了之後,我們就可以在 src/main/kotlin/Databases.kt 內操作 Users 這張資料表了!

我們看裡面的 Application.configureDatabases()

val database = Database.connect(
    url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1",
    user = "root",
    driver = "org.h2.Driver",
    password = "",
)
val userService = UserService(database)

作為範例,這邊我們定義連線的資料庫為 H2 Database。設定完連線內容之後,我們將設置好的資料庫物件輸入 UserService,之後就可以用來和資料庫做互動了。

作為範例的 CRUD API 如下

// Create user
post("/users") {
    val user = call.receive<ExposedUser>()
    val id = userService.create(user)
    call.respond(HttpStatusCode.Created, id)
}

// Read user
get("/users/{id}") {
    val id = call.parameters["id"]?.toInt() ?: throw IllegalArgumentException("Invalid ID")
    val user = userService.read(id)
    if (user != null) {
        call.respond(HttpStatusCode.OK, user)
    } else {
        call.respond(HttpStatusCode.NotFound)
    }
}

// Update user
put("/users/{id}") {
    val id = call.parameters["id"]?.toInt() ?: throw IllegalArgumentException("Invalid ID")
    val user = call.receive<ExposedUser>()
    userService.update(id, user)
    call.respond(HttpStatusCode.OK)
}

// Delete user
delete("/users/{id}") {
    val id = call.parameters["id"]?.toInt() ?: throw IllegalArgumentException("Invalid ID")
    userService.delete(id)
    call.respond(HttpStatusCode.OK)
}

這個寫法是不是很直觀呢?

今天的部分就到這邊,我們明天見!


上一篇
Day 03:StatusPages、staticResources、Serializable
下一篇
Day 05:用 DAO 改寫 UserService 並加上自動化測試
系列文
每天一點 Ktor 3.0:一個月學會 Kotlin 後端開發6
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言