Skip to content

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

這是 **Spring Boot 與 Kotlin 入門** 教學的最後一部分。在繼續之前,請確保您已完成以下步驟:


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

在這部分中,您將把服務層 (service layer) 遷移為使用 Spring DataCrudRepository 進行資料庫存取,而不是 JdbcTemplateCrudRepository 是一個 Spring Data 介面,用於對特定類型的儲存庫執行泛型 CRUD (建立、讀取、更新和刪除) 操作。 它提供了多種開箱即用的方法來與資料庫互動。

更新您的應用程式

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

  1. Message 類別新增 @Table 註解,以宣告其與資料庫表格的映射關係。 在 id 欄位前新增 @Id 註解。

    NOTE

    這些註解還需要額外的引用 (imports)。

    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 類別的使用方式更為慣用, 您可以將 id 屬性的預設值設定為 null,並調換資料類別屬性的順序:

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

    現在,如果您需要建立 Message 類別的新實例,您只需指定 text 屬性作為參數:

    kotlin
    val message = Message("Hello") // id is null
  2. 宣告一個 CrudRepository 介面,它將與 Message 資料類別一起使用。建立 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)
    }
    擴充函式 (Extension functions)

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

    CrudRepository save() 函式

    此函式的工作原理是基於一個假設:新物件在資料庫中沒有 ID。因此,對於插入操作,ID 應該是 null

    如果 ID 不是nullCrudRepository 會假定該物件已存在於資料庫中,並且這是一個更新操作,而不是插入操作。在插入操作之後,id 將由資料儲存區 (data store) 產生,並重新指派回 Message 實例。這就是為什麼 id 屬性應使用 var 關鍵字宣告的原因。

  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> =
        // If the message is null (not found), set response code to 404
        this?.let { ResponseEntity.ok(it) } ?: ResponseEntity.notFound().build()
}

執行應用程式

恭喜!應用程式已準備好再次執行。 在將 JdbcTemplate 替換為 CrudRepository 後,功能保持不變,因此應用程式的運作一如既往。

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

下一步

取得您的個人語言地圖,以幫助您探索 Kotlin 功能並追蹤您在學習該語言方面的進度:

取得 Kotlin 語言地圖