Skip to content

使用 Spring Data CrudRepository 進行資料庫存取

這是開始使用 Spring Boot 與 Kotlin 教學的最後一部分。在繼續之前,請確保您已完成先前的步驟:


第一步 使用 Kotlin 建立 Spring Boot 專案
第二步 為 Spring Boot 專案加入資料類別
第三步 為 Spring Boot 專案加入資料庫支援
第四步 使用 Spring Data CrudRepository 進行資料庫存取

在這一部分中,您將遷移服務層,改為使用 Spring Data CrudRepository 而非 JdbcTemplate 進行資料庫存取。 CrudRepository 是一個 Spring Data 介面,用於對特定類型的存儲庫進行通用的 CRUD 操作。 它隨附提供了多個用於與資料庫互動的現成方法。

更新您的應用程式

首先,您需要調整 Message 類別以搭配 CrudRepository API 使用:

  1. Message 類別加入 @Table 註解,以宣告與資料庫資料表的對應。
    id 欄位前加入 @Id 註解。

    這些註解還需要額外的匯入。

    kotlin
    // Message.kt
    package com.example.demo
    
    import org.springframework.data.annotation.Id
    import org.springframework.data.relational.core.mapping.Table
    
    @Table("MESSAGES")
    data class Message(@Id val id: String?, val text: String)

    此外,為了讓 Message 類別的使用更符合慣例(idiomatic), 您可以將 id 屬性的預設值設定為 null,並翻轉資料類別屬性的順序:

    kotlin
    @Table("MESSAGES")
    data class Message(val text: String, @Id val id: String? = null)

    現在,如果您需要建立 Message 類別的新執行個體,可以僅指定 text 屬性作為參數:

    kotlin
    val message = Message("Hello") // id 為 null
  2. 為將與 Message 資料類別配合使用的 CrudRepository 宣告一個介面。建立 MessageRepository.kt 檔案並加入以下程式碼:

    kotlin
    // MessageRepository.kt
    package com.example.demo
    
    import org.springframework.data.repository.CrudRepository
    
    interface MessageRepository : CrudRepository<Message, String>
  3. 更新 MessageService 類別。它現在將使用 MessageRepository 而非執行 SQL 查詢:

    kotlin
    // MessageService.kt
    package com.example.demo
    
    import org.springframework.data.repository.findByIdOrNull
    import org.springframework.stereotype.Service
    
    @Service
    class MessageService(private val db: MessageRepository) {
        fun findMessages(): List<Message> = db.findAll().toList()
    
        fun findMessageById(id: String): Message? = db.findByIdOrNull(id)
    
        fun save(message: Message): Message = db.save(message)
    }
    擴充函式

    findByIdOrNull() 函式是 Spring Data JDBC 中 CrudRepository 介面的擴充函式

    CrudRepository save() 函式

    此函式的工作前提是新物件在資料庫中沒有 id。因此,對於插入操作,id 應為 null

    如果 id 不是 nullCrudRepository 會假設該物件已存在於資料庫中,並將其視為更新(update)操作而非插入(insert)操作。在插入操作之後,id 將由資料存儲產生並回填至 Message 執行個體中。

  4. 更新 messages 資料表定義,以便為插入的物件產生 id。由於 id 是字串,您可以使用 RANDOM_UUID() 函式作為預設值來產生 id 值:

    sql
    -- schema.sql 
    CREATE TABLE IF NOT EXISTS messages (
        id      VARCHAR(60)  DEFAULT RANDOM_UUID() PRIMARY KEY,
        text    VARCHAR      NOT NULL
    );
  5. 更新位於 src/main/resources 資料夾中 application.properties 檔案內的資料庫名稱:

    none
    spring.application.name=demo
    spring.datasource.driver-class-name=org.h2.Driver
    spring.datasource.url=jdbc:h2:file:./data/testdb2
    spring.datasource.username=name
    spring.datasource.password=password
    spring.sql.init.schema-locations=classpath:schema.sql
    spring.sql.init.mode=always

以下是應用程式的完整程式碼:

kotlin
// DemoApplication.kt
package com.example.demo

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class DemoApplication

fun main(args: Array<String>) {
    runApplication<DemoApplication>(*args)
}
kotlin
// Message.kt
package com.example.demo

import org.springframework.data.annotation.Id
import org.springframework.data.relational.core.mapping.Table

@Table("MESSAGES")
data class Message(val text: String, @Id val id: String? = null)
kotlin
// MessageRepository.kt
package com.example.demo

import org.springframework.data.repository.CrudRepository

interface MessageRepository : CrudRepository<Message, String>
kotlin
// MessageService.kt
package com.example.demo

import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service

@Service
class MessageService(private val db: MessageRepository) {
    fun findMessages(): List<Message> = db.findAll().toList()

    fun findMessageById(id: String): Message? = db.findByIdOrNull(id)

    fun save(message: Message): Message = db.save(message)
}
kotlin
// MessageController.kt
package com.example.demo

import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import java.net.URI

@RestController
@RequestMapping("/")
class MessageController(private val service: MessageService) {
    @GetMapping
    fun listMessages() = ResponseEntity.ok(service.findMessages())

    @PostMapping
    fun post(@RequestBody message: Message): ResponseEntity<Message> {
        val savedMessage = service.save(message)
        return ResponseEntity.created(URI("/${savedMessage.id}")).body(savedMessage)
    }

    @GetMapping("/{id}")
    fun getMessage(@PathVariable id: String): ResponseEntity<Message> =
        service.findMessageById(id).toResponseEntity()

    private fun Message?.toResponseEntity(): ResponseEntity<Message> =
        // 如果訊息為 null(找不到),將回應代碼設為 404
        this?.let { ResponseEntity.ok(it) } ?: ResponseEntity.notFound().build()
}

執行應用程式

恭喜!應用程式已準備好再次執行。 在將 JdbcTemplate 替換為 CrudRepository 之後,功能性保持不變,因此應用程式的運作方式與之前完全相同。

您現在可以從 requests.http 檔案中執行 POST 與 GET HTTP 請求,並獲得相同的結果。

下一步

獲取您的個人語言地圖,協助您探索 Kotlin 特性並追蹤學習進度:

獲取 Kotlin 語言地圖