Kotlin 语言参考文档 中文版 Help

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

在这一章中, 你将会迁移服务层, 使用 Spring Data CrudRepository 进行数据库访问, 而不是原来的 JdbcTemplate. CrudRepository 是一个 Spring Data 接口, 可以指定类型的仓库进行通常的 CRUD 操作. 它提供了一些现成的方法来操作数据库.

更新你的应用程序

首先, 你需要调整 Message 类, 来配合 CrudRepository API:

  1. Message 类添加 @Table 注解, 声明它与数据库表的映射关系. 在 id 属性之前添加 @Id 注解.

    // Message.kt package 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 类的使用更加符合 Kotlin 的编程习惯, 你可以将 id 属性的默认值设置为 null, 并翻转数据类的属性顺序:

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

    现在, 如果你需要创建的 Message 类的新实例, 你可以在参数中只指定 text 属性:

    val message = Message("Hello") // id 为 null
  2. CrudRepository 声明一个接口, 它负责操作 Message 数据类. 创建 MessageRepository.kt 文件, 添加以下代码:

    // MessageRepository.kt package demo import org.springframework.data.repository.CrudRepository interface MessageRepository : CrudRepository<Message, String>
  3. 更新 MessageService 类. 它现在使用 MessageRepository, 而不是执行 SQL 查询:

    // MessageService.kt package 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 接口的一个 扩展函数.

    在上面的代码中, Optional<out T>.toList(), .toList()Optional 的扩展函数. 使用扩展函数, 你可以向任何类添加额外的函数, 当你想要扩展某些库中的类的功能时, 这样会非常有用.

    CrudRepository save() 函数

    这个函数的工作方式 是假定新的对象在数据库中没有 id. 因此, 对 insertion 操作, id 需要为 null.

    如果 id 不是 null, CrudRepository 假定对象在数据库中已经存在, 并且这是一个 update 操作, 而不是 insert 操作. 在 insert 操作之后, id 会由数据库生成, 并反过来赋值给 Message 实例. 这就是 id 属性需要使用 var 关键字来声明的原因.

  4. 更新 messages 表定义, 对 insert 的对象生成 id. 由于 id 是一个字符串, 你可以使用 RANDOM_UUID() 函数来生成默认的 id 值:

    -- 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 文件内的数据库名称:

    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

下面是应用程序的完整代码:

// DemoApplication.kt package demo import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication @SpringBootApplication class DemoApplication fun main(args: Array<String>) { runApplication<DemoApplication>(*args) }
// Message.kt package 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)
// MessageRepository.kt package demo import org.springframework.data.repository.CrudRepository interface MessageRepository : CrudRepository<Message, String>
// MessageService.kt package 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) }
// MessageController.kt package 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> = // 如果 message 为 null (未找到), 将应答的 Status Code 设置为 404 this?.let { ResponseEntity.ok(it) } ?: ResponseEntity.notFound().build() }

运行应用程序

应用程序可以在此运行了. 通过将 JdbcTemplate 替换为 CrudRepository, 功能并没有变更, 因此应用程序应该和以前相同的方式运行.

下一步做什么

得到你个人的语言导航地图, 它可以帮助你浏览 Kotlin 的功能特性, 并追踪你学习语言的进度:

得到 Kotlin 语言导航地图

最终更新: 2024/12/17