6.1.7.5 使用 Spring Data CrudRepository 访问数据库
原文链接: https://kotlinlang.org/docs/jvm-spring-boot-using-crudrepository.html
6.1.7.5 使用 Spring Data CrudRepository 访问数据库
在用 Kotlin 编写的 Spring Boot 项目中使用 Spring Data 接口。
在这一部分,你将把服务层从使用 JdbcTemplate 迁移到使用 Spring Data 的 CrudRepository 来访问数据库。CrudRepository 是 Spring Data 的一个接口,用于对特定类型的仓库进行通用的 CRUD 操作。它开箱即用地提供了多个与数据库交互的方法。
更新你的应用
首先,你需要调整 Message 类,使其适用于 CrudRepository API:
- 为
Message 类添加 @Table 注解,以声明到数据库表的映射。在 id 字段之前添加 @Id 注解。
注意: 这些注解还需要额外的导入。
1
2
3
4
5
6
7
8
| // 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,并调换数据类属性的顺序:
1
2
| @Table("MESSAGES")
data class Message(val text: String, @Id val id: String? = null)
|
现在,如果你需要创建 Message 类的新实例,只需把 text 属性作为参数指定即可:
1
| val message = Message("Hello") // id 为 null
|
- 为将要处理
Message 数据类的 CrudRepository 声明一个接口。创建 MessageRepository.kt 文件并添加以下代码:
1
2
3
4
5
6
| // MessageRepository.kt
package com.example.demo
import org.springframework.data.repository.CrudRepository
interface MessageRepository : CrudRepository<Message, String>
|
- 更新
MessageService 类。现在它将使用 MessageRepository,而不是执行 SQL 查询:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // 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 实例上。
- 更新 messages 表的定义,为插入的对象生成 id。由于
id 是字符串,你可以使用 RANDOM_UUID() 函数默认生成 id 值:
1
2
3
4
5
| -- schema.sql
CREATE TABLE IF NOT EXISTS messages (
id VARCHAR(60) DEFAULT RANDOM_UUID() PRIMARY KEY,
text VARCHAR NOT NULL
);
|
- 更新位于
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
以下是应用的完整代码:
1
2
3
4
5
6
7
8
9
10
11
12
| // 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)
}
|
1
2
3
4
5
6
7
8
| // 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)
|
1
2
3
4
5
6
| // MessageRepository.kt
package com.example.demo
import org.springframework.data.repository.CrudRepository
interface MessageRepository : CrudRepository<Message, String>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // 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)
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| // 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()
}
|
运行应用
恭喜!应用又可以运行了。用 CrudRepository 替换 JdbcTemplate 之后,功能保持不变,因此应用的工作方式与之前完全一样。
现在你可以从 requests.http 文件运行 POST 和 GET HTTP 请求,并得到相同的结果。
上一步
接下来学什么
获取你的专属语言地图,帮助你了解 Kotlin 特性并跟踪语言学习进度:
获取 Kotlin 语言地图