名詞介紹
Exposed Framework
Exposed Framework 是一個用 Kotlin 所開發的 ORM (Object-Relation Mapping — 物件關係對映) 框架。
在 Exposed 中,可以使用兩種方式存取資料庫:1. 包裝在 DSL (Domain Specific Language — 領域特定語言) 中,具有型別安全的 SQL 指令,2. 輕量的DAO (Data Access Object — 資料存取物件)
目前支援的資料庫如下:
- H2
- MySQL
- MariaDB
- Oracle
- PostgreSQL
- PostgreSQL using the pgjdbc-ng JDBC driver
- SQL Server
- SQLite
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"
: 設定Driveruser
,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
參考
Kotlinlang.org: Object