Skip to content

使用 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 語言地圖