Ktor 練功場筆記(3) — 資料庫串接

如何使用 Exposed Framework 操控 H2 Database

Andy Lu
8 min readAug 24, 2020

本次練功場將學習,如何使用 Exposed Framework 操控 H2 Database。

名詞介紹

Exposed Framework

Exposed Framework 是一個用 Kotlin 所開發的 ORM (Object-Relation Mapping — 物件關係對映) 框架。

在 Exposed 中,可以使用兩種方式存取資料庫:1. 包裝在 DSL (Domain Specific Language — 領域特定語言) 中,具有型別安全的 SQL 指令,2. 輕量的DAO (Data Access Object — 資料存取物件)

目前支援的資料庫如下:

H2 Database

H2 Database 是一種記憶體資料庫 (in-memory database)。

因存放在記憶體中,所以存取快速。用 JDBC (Java Database Connective — Java資料庫連接) API 連接。因 Kotlin 與 Java 的高相容性,所以在 Kotlin 專案中,也可以輕鬆地使用。

Step by step

1. 安裝 Exposed Framework

  • 以 Gradle 安裝:在專案中的 build.gradle 的 dependencies 中,加入
dependencies {
implementation 'org.jetbrains.exposed:exposed:0.17.7'
}

2. 安裝 H2 Database

  • 以 Gradle 安裝:在專案中的 build.gradle 的 dependencies 中,加入
dependencies {
implementation "com.h2database:h2:1.4.200"
}

3. 定義資料表

  • object 用來宣告此類為單例 (Singleton)。
object Users : IntIdTable() {
val name = varchar("name", 50).index()
val city = reference("city", Cities)
val age = integer("age")
}

object Cities: IntIdTable() {
val name = varchar("name", 50)
}

4. 定義 DAO


class User(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<User>(Users)

var name by Users.name
var city by City referencedOn Users.city
var age by Users.age
}

class City(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<City>(Cities)

var name by Cities.name
val users by User referrersOn Users.city
}

5. 連線資料庫

Database.connect(
url= "jdbc:h2:mem:test; DB_CLOSE_DELAY=-1;",
driver = "org.h2.Driver",
user = "root",
password = ""
)
  • url=jdbc:h2:mem:test; :連接 H2 資料庫。
  • DB_CLOSE_DELAY=-1; :因 H2 Database 是 in-memory 的資料庫,所以在每次關閉連線的時候,資料都會不見。使用 DB_CLOSE_DELAY=-1; 可以在每次虛擬機器還在時,資料都會存在。
  • driver="org.h2.Driver" : 設定Driver
  • user , password :設定連線帳號密碼

6. 建立資料庫 Table (Cities, Users)

  • 因H2 Database 是存放在記憶體中,所以我們可以在 routing之外建立資料庫 Table,當每次 Ktor 跑起來時,就會把資料庫的 Table 建立起來。
  • 每一次資料庫的動作,都需要把相關程式碼放在 transaction{} 區塊中。
transaction{
SchemaUtils.create(Cities, Users)
}
routing{...}

7. 新建資料 (Users, Cities)

  • 因應測試需求,所以我們將建立資料的部分跟上方建立資料庫 Table 的動作一起做,這樣在 Ktor 建立連線之後,資料庫 Table 也建立好,同時,裡面也有資料了。
transaction{
SchemaUtils.create(Cities, Users)

val taipei = City.new {
name = "Taipei"
}
User.new {
name = "Leo"
city = taipei
age = 40
}
}
  • City.new :建立 City 的內容 name="Taipei"。因為 User 與 City 有一個欄位有關聯,所以將建立 City 的結果暫存在 val taipei 中。
  • User.new :建立 User 的內容,其中 city 的欄位是由前面建立的 city 所帶入。

8. Get 資料

  • 那麼,現在已經有資料了,我們來測試資料有沒有正確的寫入。
  • routing{} 中,設定一個路徑 \get-user ,並將先前寫入的資料讀出。
routing{
...
get("/get-user"){
val users = transaction{
User.all().joinToString { "${it.name} live in ${it.city.name} is ${it.age} years old." }
}
call.respondText(users, contentType = ContentType.Text.Plain)
}
...
}
  • 同樣的,需要操作資料庫時,需要將指令用 transaction 包起來。這邊我希望能夠列印出 全部 User 的資訊 (雖然現在只有一個)。
  • User.all() 的型態是 SizedIterable<T> 並且繼承於 Iterable<T> ,所以我們這邊可以使用 .joinToString()User.all() 得到的結果以字串傳出。
  • 最後,我們將得到的內容用 call.respondText() 顯示在畫面上。

9. Run application

好der,我們已經完成建立資料庫、寫入資料、Get 資料了,我們來測試一下。

我們在 IntelliJ IDEA 的 Gradle Tab 中,點擊 application->run

  • 如果沒有問題的話,將在 Run 的視窗中,看到資料庫建立以及寫入的訊息。
  • 最後,開啟瀏覽器,我們就可以看到寫入的資料庫內容了。

小記

在練功場時,一直無法看到資料,以為是電腦沒有安裝 H2 Database 的關係,原來是因為資料庫的 Table 沒有建立,所以在寫入資料的時候就沒辦法寫入。講師將建立資料庫的步驟搬到 routing 之前,建立資料就沒有問題了。

Github: https://github.com/andyludeveloper/ktor_workshop

參考

Exposed Framework Github

Exposed Framework 安裝

H2 Database 官網

Kotlinlang.org: Object

運用 Exposed 管理及操作資料庫

--

--

Andy Lu
Andy Lu

Written by Andy Lu

Android/Flutter developer, Kotlin Expert, like to learn and share.

No responses yet