看完 src/main/kotlin/Routing.kt 裡面所定義的幾個路由之後,接著我們來看看跟資料庫操作相關的程式
我們先看 src/main/kotlin/UsersSchema.kt 裡面
@Serializable
data class ExposedUser(val name: String, val age: Int)
這邊定義了一個 data class,包含了兩個屬性 name
和 age
。注意這邊和之前要序列化的物件一樣
,要加上 @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
資料表的內容,定義了 name
和 age
在資料庫內的資料型態。
接著下面定義了資料表初始化,以及 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)
}
這個寫法是不是很直觀呢?
今天的部分就到這邊,我們明天見!