Skip to content

使用 Spring Data CrudRepository 进行数据库访问

这是Spring Boot 和 Kotlin 入门教程的最后一部分。在继续之前,请确保已完成以下步骤:


第一步 用 Kotlin 创建 Spring Boot 项目
第二步 向 Spring Boot 项目添加数据类
第三步 为 Spring Boot 项目添加数据库支持
第四步 使用 Spring Data CrudRepository 进行数据库访问

在这部分中,你将迁移服务层以使用 Spring DataCrudRepository 而不是 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 类的使用更符合习惯, 你可以将 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. 声明一个接口,用于配合 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 不是 null,则 CrudRepository 假定对象已存在于数据库中,这是一项更新操作,而不是插入操作。插入操作后,id 将由数据存储生成并重新赋值给 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 语言地图