將資料庫與 Kotlin、Ktor 和 Exposed 整合
程式碼範例: tutorial-server-db-integration
使用的外掛程式:
在本文中,您將學習如何使用 Kotlin 的 SQL 函式庫 Exposed,將您的 Ktor 服務與關聯式資料庫整合。
透過本教程,您將學習如何執行以下操作:
- 建立使用 JSON 序列化的 RESTful 服務。
- 將不同的儲存庫注入到這些服務中。
- 使用模擬儲存庫為您的服務建立單元測試。
- 使用 Exposed 和依賴注入 (DI) 建立可運作的儲存庫。
- 部署存取外部資料庫的服務。
在先前的教程中,我們使用「任務管理器」範例涵蓋了基礎知識,例如
TaskRepository
的前端功能, 本指南則將重點轉移到如何透過 Exposed SQL 函式庫讓您的 Ktor 服務與關聯式資料庫互動。 儘管本指南較長且更複雜,您仍然能快速產生可運作的程式碼並逐步引入新功能。
本指南將分為兩個部分:
先決條件
您可以獨立完成本教程,但是,我們建議您完成
我們建議您安裝 IntelliJ IDEA,但您也可以使用您選擇的其他 IDE。
建立 RESTful 服務和記憶體內部儲存庫
首先,您將重新建立您的任務管理器 RESTful 服務。最初,這將使用記憶體內部儲存庫,但您將設計其結構,使其能夠以最小的努力進行替換。
您將分六個階段進行:
使用外掛程式建立新專案
若要使用 Ktor 專案生成器建立新專案,請按照以下步驟操作:
導航至 Ktor 專案生成器 。
在 專案構件 欄位中,輸入 com.example.ktor-exposed-task-app 作為您的專案構件名稱。
在外掛程式區段中,透過點擊 新增 按鈕來搜尋並新增以下外掛程式:
- Routing
- Content Negotiation
- Kotlinx.serialization
- Static Content
- Status Pages
- Exposed
- Postgres
新增外掛程式後,點擊外掛程式區段右上角的 7 plugins 按鈕,以查看已新增的外掛程式。
然後您將看到所有將新增到您專案中的外掛程式列表:
點擊 下載 按鈕以生成並下載您的 Ktor 專案。
在 IntelliJ IDEA 或您選擇的其他 IDE 中開啟生成的專案。
導航至 src/main/kotlin/com/example 並刪除檔案 CitySchema.kt 和 UsersSchema.kt 。
開啟 Databases.kt 檔案並移除
configureDatabases()
函數的內容。kotlin移除此功能的理由是 Ktor 專案生成器已新增範例程式碼,以展示如何將使用者和城市資料持久化到 HSQLDB 或 PostgreSQL。本教程中不需要該範例程式碼。
新增入門程式碼
- 導航至 src/main/kotlin/com/example 並建立一個名為 model 的子套件。
- 在 model 套件內,建立一個新的 Task.kt 檔案。
開啟 Task.kt 並新增一個
enum
來表示優先級,以及一個class
來表示任務。kotlinTask
類別使用kotlinx.serialization函式庫中的ContentNegotiation 外掛程式主要有兩個目的:協商客戶端與伺服器之間的媒體類型,以及以特定格式序列化/反序列化內容。Serializable
類型進行註解。與先前的教程一樣,您將建立一個記憶體內部儲存庫。然而,這次儲存庫將實作一個
interface
,以便您以後可以輕鬆替換它。- 在 model 子套件中,建立一個新的 TaskRepository.kt 檔案。
開啟 TaskRepository.kt 並新增以下
interface
:kotlin- 在同一個目錄中建立一個新的 FakeTaskRepository.kt 檔案。
開啟 FakeTaskRepository.kt 並新增以下
class
:kotlin
新增路由
- 開啟 src/main/kotlin/com/example 中的 Serialization.kt 檔案。
將現有的
Application.configureSerialization()
函數替換為以下實作:kotlin這是您在
建立 RESTful API教程中實作的相同路由,只是現在您將儲存庫作為參數傳遞到學習如何使用 Kotlin 和 Ktor 建立後端服務,其中包含一個產生 JSON 檔案的 RESTful API 範例。routing()
函數中。由於參數的類型是一個interface
,因此可以注入許多不同的實作。現在您已將參數新增到
configureSerialization()
,現有的呼叫將不再編譯。幸運的是,此函數僅被呼叫一次。- 開啟 src/main/kotlin/com/example 內的 Application.kt 檔案。
將
module()
函數替換為以下實作:kotlin您現在正將
FakeTaskRepository
的實例注入到configureSerialization()
中。 長期目標是能夠將其替換為PostgresTaskRepository
。
新增客戶端頁面
- 開啟 src/main/resources/static 中的 index.html 檔案。
將目前的內容替換為以下實作:
html這與先前教程中使用的 SPA 相同。因為它是用 JavaScript 編寫的,並且只使用了瀏覽器中可用的函式庫,所以您不必擔心客戶端依賴項。
手動測試應用程式
導航至 src/main/resources/application.yaml 並移除
postgres
配置。yaml在 IntelliJ IDEA 中,點擊執行按鈕 (
) 來啟動應用程式。
在瀏覽器中導航至 http://0.0.0.0:8080/static/index.html。您應該會看到客戶端頁面,其中包含三個表單和一個顯示篩選結果的表格。
透過填寫並使用 前往 按鈕傳送表單來測試應用程式。使用表格項目上的 檢視 和 刪除 按鈕。
由於這次迭代使用的是記憶體內部儲存庫,而不是連接到資料庫,您需要確保應用程式已正確配置。
新增自動化單元測試
開啟 src/test/kotlin/com/example 中的 ApplicationTest.kt 並新增以下測試:
kotlin為使這些測試能夠編譯和執行,您需要為 Ktor 客戶端新增對 Content Negotiation 外掛程式的依賴項。
開啟 gradle/libs.versions.toml 檔案並指定以下函式庫:
kotlin開啟 build.gradle.kts 並新增以下依賴項:
kotlin在 IntelliJ IDEA 中,點擊通知 Gradle 圖示 (
) 在編輯器右側以載入 Gradle 變更。
在 IntelliJ IDEA 中,點擊測試類別定義旁邊的執行按鈕 (
) 來執行測試。
然後您應該會在 執行 窗格中看到測試成功執行。
新增 PostgreSQL 儲存庫
現在您已經有一個使用記憶體內部資料的運作中應用程式,下一步是將資料儲存外部化到 PostgreSQL 資料庫。
您將透過執行以下操作來實現這一點:
- 在 PostgreSQL 中建立資料庫綱要。
- 使
TaskRepository
適應非同步存取。 - 在應用程式中配置資料庫連線。
- 將
Task
類型映射到相關的資料庫表格。 - 基於此映射建立一個新的儲存庫。
- 在啟動程式碼中切換到這個新儲存庫。
建立資料庫綱要
使用您選擇的資料庫管理工具,在 PostgreSQL 中建立一個新資料庫。 名稱無關緊要,只要您記得它即可。在此範例中,我們將使用 ktor_tutorial_db 。
對您的資料庫執行以下 SQL 命令。這些命令將建立並填充資料庫綱要:
sql請注意以下事項:
- 您正在建立一個名為 task 的單一表格,其中包含用於 name 、 description 和 priority 的欄位。這些欄位需要映射到
Task
類別的屬性。 - 如果表格已存在,您將重新建立它,因此您可以重複執行腳本。
- 還有一個名為 id 的額外欄位,其類型為
SERIAL
。這將是一個整數值,用於為每一列提供其主鍵。這些值將由資料庫為您自動分配。
- 您正在建立一個名為 task 的單一表格,其中包含用於 name 、 description 和 priority 的欄位。這些欄位需要映射到
調整現有儲存庫
開啟 src/main/kotlin/com/example/model 中的 TaskRepository.kt 檔案。
將
suspend
關鍵字新增到所有介面方法:kotlin這將允許介面方法的實作在不同的協程調度器 (Coroutine Dispatcher) 上啟動工作。
您現在需要調整
FakeTaskRepository
的方法以匹配,儘管您不需要在該實作中切換調度器。開啟 FakeTaskRepository.kt 檔案並將
suspend
關鍵字新增到所有方法:kotlin到目前為止,您沒有引入任何新功能。相反,您為建立一個將非同步執行資料庫查詢的
PostgresTaskRepository
奠定了基礎。
當對資料庫執行查詢時,最好讓它們非同步執行以避免阻塞處理 HTTP 請求的執行緒。在 Kotlin 中,這最好透過 協程 (coroutines) 來管理。
配置資料庫連線
- 開啟 src/main/kotlin/com/example 中的 Databases.kt 檔案。
使用
Database.connect()
函數連接到您的資料庫,調整設定值以符合您的環境:kotlin請注意,
url
包含以下組件:localhost:5432
是 PostgreSQL 資料庫運行的主機和埠。ktor_tutorial_db
是執行服務時建立的資料庫名稱。
TIP
更多資訊請參閱 在 Exposed 中使用 Database 和 DataSource。
在 本教程的第一部分 中,您刪除了在 Databases.kt 中找到的 configureDatabases()
方法中的範例程式碼。您現在已準備好新增您自己的實作。
建立物件/關聯式對應
- 導航至 src/main/kotlin/com/example 並建立一個名為 db 的新套件。
- 在 db 套件內,建立一個新的 mapping.kt 檔案。
開啟 mapping.kt 並新增類型
TaskTable
和TaskDAO
:kotlin這些類型使用 Exposed 函式庫將
Task
類型中的屬性映射到資料庫中 task 表格中的欄位。TaskTable
類型定義了基本映射,而TaskDAO
類型則新增了輔助方法來建立、尋找、更新和刪除任務。Ktor 專案生成器尚未新增對 DAO 類型的支援,因此您需要在 Gradle 建置檔案中新增相關依賴項。
開啟 gradle/libs.versions.toml 檔案並指定以下函式庫:
kotlin開啟 build.gradle.kts 檔案並新增以下依賴項:
kotlin在 IntelliJ IDEA 中,點擊通知 Gradle 圖示 (
) 在編輯器右側以載入 Gradle 變更。
導航回 mapping.kt 檔案並新增以下兩個輔助函數:
kotlinsuspendTransaction()
接受一個程式碼區塊,並在資料庫交易中,透過 IO 調度器 (IO Dispatcher) 執行它。這是為了將阻塞型工作卸載到執行緒池 (thread pool)。daoToModel()
將TaskDAO
類型的一個實例轉換為Task
物件。新增以下缺少的匯入:
kotlin
編寫新儲存庫
- 導航至 src/main/kotlin/com/example/model 並建立一個新的 PostgresTaskRepository.kt 檔案。
開啟 PostgresTaskRepository.kt 檔案並使用以下實作建立一個新類型:
kotlin在此實作中,您使用
TaskDAO
和TaskTable
類型的輔助方法與資料庫互動。建立此新儲存庫後,剩下的唯一任務是在您的路由中切換到使用它。
您現在擁有建立資料庫特定儲存庫所需的所有資源。
切換到新儲存庫
- 開啟 src/main/kotlin/com/example 中的 Application.kt 檔案。
在
Application.module()
函數中,將FakeTaskRepository
替換為PostgresTaskRepository
:kotlin由於您透過介面注入依賴項,因此實作中的切換對於管理路由的程式碼是透明的。
在 IntelliJ IDEA 中,點擊重新執行按鈕 (
) 以重新啟動應用程式。
- 導航至 http://0.0.0.0:8080/static/index.html。 UI 保持不變,但它現在從資料庫中獲取資料。
為了驗證這一點,請使用表單新增一個新任務,並查詢 PostgreSQL 中任務表格中儲存的資料。
TIP
在 IntelliJ IDEA 中,您可以使用 查詢主控台 (Query Console) 和
SELECT
SQL 陳述式來查詢表格資料:SQL查詢後,資料應顯示在 服務 窗格中,包括新任務:
要切換到外部資料庫,您只需要更改儲存庫類型。
至此,您已成功將資料庫整合到您的應用程式中。
由於 FakeTaskRepository
類型在生產程式碼中不再需要,您可以將其移至測試原始碼集,即 src/test/com/example 。
最終的專案結構應如下所示:

下一步
您現在擁有一個與 Ktor RESTful 服務通信的應用程式。該服務又使用 Exposed 編寫的儲存庫來存取 PostgreSQL。您還擁有一套測試,可以在不需要網路伺服器或資料庫的情況下驗證核心功能。
此結構可以根據需要擴展以支援任意功能,但是,您可能需要首先考慮設計的非功能性方面,例如容錯、安全性與可擴展性。您可以從將資料庫設定提取到