Skip to content

將資料庫與 Kotlin、Ktor 和 Exposed 整合

程式碼範例: tutorial-server-db-integration

使用的外掛程式:

Routing
路由是處理伺服器應用程式中傳入請求的核心外掛程式。
Static Content
瞭解如何提供靜態內容,例如樣式表、腳本、圖片等。
Content Negotiation
ContentNegotiation 外掛程式主要有兩個目的:協商客戶端與伺服器之間的媒體類型,以及以特定格式序列化/反序列化內容。
Status pages
%plugin_name% 允許 Ktor 應用程式根據拋出的異常或狀態碼對任何故障狀態作出適當的回應。
kotlinx.serialization
ContentNegotiation 外掛程式主要有兩個目的:協商客戶端與伺服器之間的媒體類型,以及以特定格式序列化/反序列化內容。
ExposedPostgres

在本文中,您將學習如何使用 Kotlin 的 SQL 函式庫 Exposed,將您的 Ktor 服務與關聯式資料庫整合。

透過本教程,您將學習如何執行以下操作:

  • 建立使用 JSON 序列化的 RESTful 服務。
  • 將不同的儲存庫注入到這些服務中。
  • 使用模擬儲存庫為您的服務建立單元測試。
  • 使用 Exposed 和依賴注入 (DI) 建立可運作的儲存庫。
  • 部署存取外部資料庫的服務。

在先前的教程中,我們使用「任務管理器」範例涵蓋了基礎知識,例如

處理請求
學習在 Kotlin 中使用 Ktor 建立任務管理器應用程式時,關於路由、處理請求和參數的基礎知識。
建立 RESTful API
學習如何使用 Kotlin 和 Ktor 建立後端服務,其中包含一個產生 JSON 檔案的 RESTful API 範例。
使用 Thymeleaf 模板建立 Web 應用程式
學習如何使用 Kotlin 和 Ktor 以及 Thymeleaf 模板建立網站。
。 雖然這些教程側重於使用簡單的記憶體內部 TaskRepository 的前端功能, 本指南則將重點轉移到如何透過 Exposed SQL 函式庫讓您的 Ktor 服務與關聯式資料庫互動。

儘管本指南較長且更複雜,您仍然能快速產生可運作的程式碼並逐步引入新功能。

本指南將分為兩個部分:

  1. 使用記憶體內部儲存庫建立您的應用程式。
  2. 將記憶體內部儲存庫替換為使用 PostgreSQL 的儲存庫。

先決條件

您可以獨立完成本教程,但是,我們建議您完成

建立 RESTful API
學習如何使用 Kotlin 和 Ktor 建立後端服務,其中包含一個產生 JSON 檔案的 RESTful API 範例。
教程,以便熟悉內容協商 (Content Negotiation) 和 REST。

我們建議您安裝 IntelliJ IDEA,但您也可以使用您選擇的其他 IDE。

建立 RESTful 服務和記憶體內部儲存庫

首先,您將重新建立您的任務管理器 RESTful 服務。最初,這將使用記憶體內部儲存庫,但您將設計其結構,使其能夠以最小的努力進行替換。

您將分六個階段進行:

  1. 建立初始專案。
  2. 新增入門程式碼。
  3. 新增 CRUD 路由。
  4. 新增單頁應用程式 (SPA)。
  5. 手動測試應用程式。
  6. 新增自動化測試。

使用外掛程式建立新專案

若要使用 Ktor 專案生成器建立新專案,請按照以下步驟操作:

  1. 導航至 Ktor 專案生成器

  2. 專案構件 欄位中,輸入 com.example.ktor-exposed-task-app 作為您的專案構件名稱。 在 Ktor 專案生成器中命名專案構件

  3. 在外掛程式區段中,透過點擊 新增 按鈕來搜尋並新增以下外掛程式:

    1. Routing
    2. Content Negotiation
    3. Kotlinx.serialization
    4. Static Content
    5. Status Pages
    6. Exposed
    7. Postgres

    在 Ktor 專案生成器中新增外掛程式

  4. 新增外掛程式後,點擊外掛程式區段右上角的 7 plugins 按鈕,以查看已新增的外掛程式。

    然後您將看到所有將新增到您專案中的外掛程式列表: Ktor 專案生成器中的外掛程式下拉選單

  5. 點擊 下載 按鈕以生成並下載您的 Ktor 專案。

  6. IntelliJ IDEA 或您選擇的其他 IDE 中開啟生成的專案。

  7. 導航至 src/main/kotlin/com/example 並刪除檔案 CitySchema.ktUsersSchema.kt

  8. 開啟 Databases.kt 檔案並移除 configureDatabases() 函數的內容。

    kotlin

    移除此功能的理由是 Ktor 專案生成器已新增範例程式碼,以展示如何將使用者和城市資料持久化到 HSQLDB 或 PostgreSQL。本教程中不需要該範例程式碼。

新增入門程式碼

  1. 導航至 src/main/kotlin/com/example 並建立一個名為 model 的子套件。
  2. model 套件內,建立一個新的 Task.kt 檔案。
  3. 開啟 Task.kt 並新增一個 enum 來表示優先級,以及一個 class 來表示任務。

    kotlin

    Task 類別使用

    kotlinx.serialization
    ContentNegotiation 外掛程式主要有兩個目的:協商客戶端與伺服器之間的媒體類型,以及以特定格式序列化/反序列化內容。
    函式庫中的 Serializable 類型進行註解。

    與先前的教程一樣,您將建立一個記憶體內部儲存庫。然而,這次儲存庫將實作一個 interface,以便您以後可以輕鬆替換它。

  4. model 子套件中,建立一個新的 TaskRepository.kt 檔案。
  5. 開啟 TaskRepository.kt 並新增以下 interface

    kotlin
  6. 在同一個目錄中建立一個新的 FakeTaskRepository.kt 檔案。
  7. 開啟 FakeTaskRepository.kt 並新增以下 class

    kotlin

新增路由

  1. 開啟 src/main/kotlin/com/example 中的 Serialization.kt 檔案。
  2. 將現有的 Application.configureSerialization() 函數替換為以下實作:

    kotlin

    這是您在

    建立 RESTful API
    學習如何使用 Kotlin 和 Ktor 建立後端服務,其中包含一個產生 JSON 檔案的 RESTful API 範例。
    教程中實作的相同路由,只是現在您將儲存庫作為參數傳遞到 routing() 函數中。由於參數的類型是一個 interface,因此可以注入許多不同的實作。

    現在您已將參數新增到 configureSerialization(),現有的呼叫將不再編譯。幸運的是,此函數僅被呼叫一次。

  3. 開啟 src/main/kotlin/com/example 內的 Application.kt 檔案。
  4. module() 函數替換為以下實作:

    kotlin

    您現在正將 FakeTaskRepository 的實例注入到 configureSerialization() 中。 長期目標是能夠將其替換為 PostgresTaskRepository

新增客戶端頁面

  1. 開啟 src/main/resources/static 中的 index.html 檔案。
  2. 將目前的內容替換為以下實作:

    html

    這與先前教程中使用的 SPA 相同。因為它是用 JavaScript 編寫的,並且只使用了瀏覽器中可用的函式庫,所以您不必擔心客戶端依賴項。

手動測試應用程式

    由於這次迭代使用的是記憶體內部儲存庫,而不是連接到資料庫,您需要確保應用程式已正確配置。

  1. 導航至 src/main/resources/application.yaml 並移除 postgres 配置。

    yaml
  2. 在 IntelliJ IDEA 中,點擊執行按鈕 (IntelliJ IDEA 執行圖示) 來啟動應用程式。

  3. 在瀏覽器中導航至 http://0.0.0.0:8080/static/index.html。您應該會看到客戶端頁面,其中包含三個表單和一個顯示篩選結果的表格。

    顯示任務管理器客戶端的瀏覽器視窗
  4. 透過填寫並使用 前往 按鈕傳送表單來測試應用程式。使用表格項目上的 檢視刪除 按鈕。

    顯示任務管理器客戶端的瀏覽器視窗

新增自動化單元測試

  1. 開啟 src/test/kotlin/com/example 中的 ApplicationTest.kt 並新增以下測試:

    kotlin

    為使這些測試能夠編譯和執行,您需要為 Ktor 客戶端新增對 Content Negotiation 外掛程式的依賴項。

  2. 開啟 gradle/libs.versions.toml 檔案並指定以下函式庫:

    kotlin
  3. 開啟 build.gradle.kts 並新增以下依賴項:

    kotlin
  4. 在 IntelliJ IDEA 中,點擊通知 Gradle 圖示 (IntelliJ IDEA Gradle 圖示) 在編輯器右側以載入 Gradle 變更。

  5. 在 IntelliJ IDEA 中,點擊測試類別定義旁邊的執行按鈕 (IntelliJ IDEA 執行圖示) 來執行測試。

    然後您應該會在 執行 窗格中看到測試成功執行。

    IntelliJ IDEA 執行窗格中顯示成功的測試結果

新增 PostgreSQL 儲存庫

現在您已經有一個使用記憶體內部資料的運作中應用程式,下一步是將資料儲存外部化到 PostgreSQL 資料庫。

您將透過執行以下操作來實現這一點:

  1. 在 PostgreSQL 中建立資料庫綱要。
  2. 使 TaskRepository 適應非同步存取。
  3. 在應用程式中配置資料庫連線。
  4. Task 類型映射到相關的資料庫表格。
  5. 基於此映射建立一個新的儲存庫。
  6. 在啟動程式碼中切換到這個新儲存庫。

建立資料庫綱要

  1. 使用您選擇的資料庫管理工具,在 PostgreSQL 中建立一個新資料庫。 名稱無關緊要,只要您記得它即可。在此範例中,我們將使用 ktor_tutorial_db

    TIP

    有關 PostgreSQL 的更多資訊,請參閱 官方文件

    在 IntelliJ IDEA 中,您可以使用資料庫工具來連接和管理您的 PostgreSQL 資料庫。

  2. 對您的資料庫執行以下 SQL 命令。這些命令將建立並填充資料庫綱要:

    sql

    請注意以下事項:

    • 您正在建立一個名為 task 的單一表格,其中包含用於 namedescriptionpriority 的欄位。這些欄位需要映射到 Task 類別的屬性。
    • 如果表格已存在,您將重新建立它,因此您可以重複執行腳本。
    • 還有一個名為 id 的額外欄位,其類型為 SERIAL。這將是一個整數值,用於為每一列提供其主鍵。這些值將由資料庫為您自動分配。

調整現有儲存庫

    當對資料庫執行查詢時,最好讓它們非同步執行以避免阻塞處理 HTTP 請求的執行緒。在 Kotlin 中,這最好透過 協程 (coroutines) 來管理。

  1. 開啟 src/main/kotlin/com/example/model 中的 TaskRepository.kt 檔案。

  2. suspend 關鍵字新增到所有介面方法:

    kotlin

    這將允許介面方法的實作在不同的協程調度器 (Coroutine Dispatcher) 上啟動工作。

    您現在需要調整 FakeTaskRepository 的方法以匹配,儘管您不需要在該實作中切換調度器。

  3. 開啟 FakeTaskRepository.kt 檔案並將 suspend 關鍵字新增到所有方法:

    kotlin

    到目前為止,您沒有引入任何新功能。相反,您為建立一個將非同步執行資料庫查詢的 PostgresTaskRepository 奠定了基礎。

配置資料庫連線

    本教程的第一部分 中,您刪除了在 Databases.kt 中找到的 configureDatabases() 方法中的範例程式碼。您現在已準備好新增您自己的實作。

  1. 開啟 src/main/kotlin/com/example 中的 Databases.kt 檔案。
  2. 使用 Database.connect() 函數連接到您的資料庫,調整設定值以符合您的環境:

    kotlin

    請注意,url 包含以下組件:

    • localhost:5432 是 PostgreSQL 資料庫運行的主機和埠。
    • ktor_tutorial_db 是執行服務時建立的資料庫名稱。

建立物件/關聯式對應

  1. 導航至 src/main/kotlin/com/example 並建立一個名為 db 的新套件。
  2. db 套件內,建立一個新的 mapping.kt 檔案。
  3. 開啟 mapping.kt 並新增類型 TaskTableTaskDAO

    kotlin

    這些類型使用 Exposed 函式庫將 Task 類型中的屬性映射到資料庫中 task 表格中的欄位。TaskTable 類型定義了基本映射,而 TaskDAO 類型則新增了輔助方法來建立、尋找、更新和刪除任務。

    Ktor 專案生成器尚未新增對 DAO 類型的支援,因此您需要在 Gradle 建置檔案中新增相關依賴項。

  4. 開啟 gradle/libs.versions.toml 檔案並指定以下函式庫:

    kotlin
  5. 開啟 build.gradle.kts 檔案並新增以下依賴項:

    kotlin
  6. 在 IntelliJ IDEA 中,點擊通知 Gradle 圖示 (IntelliJ IDEA Gradle 圖示) 在編輯器右側以載入 Gradle 變更。

  7. 導航回 mapping.kt 檔案並新增以下兩個輔助函數:

    kotlin

    suspendTransaction() 接受一個程式碼區塊,並在資料庫交易中,透過 IO 調度器 (IO Dispatcher) 執行它。這是為了將阻塞型工作卸載到執行緒池 (thread pool)。

    daoToModel()TaskDAO 類型的一個實例轉換為 Task 物件。

  8. 新增以下缺少的匯入:

    kotlin

編寫新儲存庫

    您現在擁有建立資料庫特定儲存庫所需的所有資源。

  1. 導航至 src/main/kotlin/com/example/model 並建立一個新的 PostgresTaskRepository.kt 檔案。
  2. 開啟 PostgresTaskRepository.kt 檔案並使用以下實作建立一個新類型:

    kotlin

    在此實作中,您使用 TaskDAOTaskTable 類型的輔助方法與資料庫互動。建立此新儲存庫後,剩下的唯一任務是在您的路由中切換到使用它。

切換到新儲存庫

    要切換到外部資料庫,您只需要更改儲存庫類型。

  1. 開啟 src/main/kotlin/com/example 中的 Application.kt 檔案。
  2. Application.module() 函數中,將 FakeTaskRepository 替換為 PostgresTaskRepository

    kotlin

    由於您透過介面注入依賴項,因此實作中的切換對於管理路由的程式碼是透明的。

  3. 在 IntelliJ IDEA 中,點擊重新執行按鈕 (IntelliJ IDEA 重新執行圖示) 以重新啟動應用程式。

  4. 導航至 http://0.0.0.0:8080/static/index.html。 UI 保持不變,但它現在從資料庫中獲取資料。
  5. 為了驗證這一點,請使用表單新增一個新任務,並查詢 PostgreSQL 中任務表格中儲存的資料。

    TIP

    在 IntelliJ IDEA 中,您可以使用 查詢主控台 (Query Console)SELECT SQL 陳述式來查詢表格資料:

    SQL

    查詢後,資料應顯示在 服務 窗格中,包括新任務:

    IntelliJ IDEA 服務窗格中顯示的任務表格

至此,您已成功將資料庫整合到您的應用程式中。

由於 FakeTaskRepository 類型在生產程式碼中不再需要,您可以將其移至測試原始碼集,即 src/test/com/example

最終的專案結構應如下所示:

IntelliJ IDEA 專案視圖中顯示的 src 資料夾

下一步

您現在擁有一個與 Ktor RESTful 服務通信的應用程式。該服務又使用 Exposed 編寫的儲存庫來存取 PostgreSQL。您還擁有一套測試,可以在不需要網路伺服器或資料庫的情況下驗證核心功能。

此結構可以根據需要擴展以支援任意功能,但是,您可能需要首先考慮設計的非功能性方面,例如容錯、安全性與可擴展性。您可以從將資料庫設定提取

設定檔
瞭解如何在設定檔中配置各種伺服器參數。
開始。