1. 简介

在本文中,我们将使用 Spring Boot 和 Kotlin 构建一个简单的 CRUD(创建、读取、更新、删除)API。我们将以一个任务管理系统为例,展示如何使用 Kotlin 语言结合 Spring Boot 快速搭建 RESTful 接口。

2. 项目搭建

要完成本项目,需要先搭建一个支持 Kotlin 的 Spring Boot 项目。建议使用 Spring Initializr 创建项目,选择以下依赖:

  • Spring Web
  • Spring Data JPA
  • H2 Database(用于内存数据库)

Kotlin 与 Spring Boot 集成非常方便,只需在 build.gradle.kts 中引入 Kotlin 插件和 Spring Boot 插件即可。

3. 定义实体与仓库

我们首先定义一个 TaskEntity 数据类,用于映射数据库表:

@Entity(name = "task")
data class TaskEntity(
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  var id: Long?,
  var name: String,
  var description: String,
  var done: Boolean
)

⚠️ 注意:id 被定义为 Long? 是为了支持 JPA 的自动生成 ID 机制。

接下来,定义一个 JPA Repository:

@Repository
interface TaskRepository : JpaRepository<TaskEntity, Long>

至此,我们已经完成了数据模型和持久层的搭建。

4. 实现 CRUD 操作

4.1 控制器与服务类

我们创建一个 TaskController 作为 REST API 的入口点:

@RestController
@RequestMapping("/tasks")
class TaskController(var taskService: TaskService)

同时,创建 TaskService 作为业务逻辑层:

@Service
class TaskService(var repository: TaskRepository)

4.2 创建任务

定义用于请求和响应的数据传输对象(DTO):

data class TaskDTORequest(
  var name: String,
  var description: String,
  var done: Boolean
)

data class TaskDTOResponse(
  var id: Long,
  var name: String,
  var description: String,
  var done: Boolean
)

创建任务的接口如下:

@PostMapping("/create")
fun createTask(@RequestBody newTask: TaskDTORequest): TaskDTOResponse {
    return taskService.createTask(newTask)
}

对应的服务方法:

fun createTask(newTask: TaskDTORequest): TaskDTOResponse {
    val saved = repository.save(TaskEntity(id = null, newTask.name, newTask.description, newTask.done))
    return TaskDTOResponse(saved.id!!, saved.name, saved.description, saved.done)
}

✅ 提示:这里使用了 !! 操作符,表示我们确信 id 不为 null,否则会抛出 NullPointerException

4.3 查询任务

接口定义:

@GetMapping("/{id}")
fun getTask(@PathVariable id: Long): TaskDTOResponse {
    return taskService.getTask(id)
      ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Task not found")
}

服务层实现:

fun getTask(id: Long): TaskDTOResponse? {
    return repository.findById(id)
      .map { TaskDTOResponse(it.id!!, it.name, it.description, it.done) }
      .getOrNull()
}

4.4 更新任务

使用 @PutMapping 实现任务更新:

@PutMapping("/{id}")
fun updateTask(@PathVariable id: Long, @RequestBody updatedTask: TaskDTORequest): TaskDTOResponse {
    return taskService.updateTask(id, updatedTask)
      ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Task not found")
}

服务层:

fun updateTask(id: Long, updatedTask: TaskDTORequest): TaskDTOResponse? {
    return repository.findById(id).map {
        val updated = repository.save(TaskEntity(it.id, updatedTask.name, updatedTask.description, updatedTask.done))
        TaskDTOResponse(updated.id!!, updated.name, updated.description, updated.done)
    }.orElseGet { null }
}

4.5 删除任务

接口定义:

@DeleteMapping("/{id}")
fun deleteTask(@PathVariable id: Long) {
    taskService.deleteTask(id)
}

服务层:

fun deleteTask(id: Long) {
    repository.deleteById(id)
}

5. 接口测试

我们使用 @SpringBootTestTestRestTemplate 进行集成测试:

@SpringBootTest(
  classes = [SpringBootCrudApplication::class],
  webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
)
class TaskDTOResponseCRUDIntegrationTest(@Autowired var restTemplate: TestRestTemplate) {
    // 测试逻辑...
}

测试创建任务:

val taskDTORequest = TaskDTORequest("Task", "description", false)
val result = this.restTemplate.postForEntity("/tasks/create", taskDTORequest, TaskDTOResponse::class.java)
taskId = result.body?.id!!
assertTrue { result.body?.name == "Task" }

测试获取任务:

val result = this.restTemplate.getForEntity("/tasks/{id}", TaskDTOResponse::class.java, taskId)
assertTrue { result.body?.name == "Task" }

测试更新任务:

this.restTemplate.put("/tasks/{id}", taskDTORequest, taskId)
val result = this.restTemplate.getForEntity("/tasks/{id}", TaskDTOResponse::class.java, taskId)
assertTrue { result.body?.done!! }

测试删除任务:

this.restTemplate.delete("/tasks/{id}", taskId)
val result = this.restTemplate.getForEntity("/tasks/{id}", String::class.java, taskId)
assertTrue { result.statusCode == HttpStatus.NOT_FOUND }

6. 总结

本文我们使用 Spring Boot 和 Kotlin 实现了一个完整的 CRUD API。我们通过定义实体、仓库、服务和控制器,展示了如何使用 Kotlin 构建结构清晰、可维护的后端接口。

✅ 建议:始终使用 DTO 而不是实体类暴露给外部接口,避免耦合数据库结构与 API 模型,便于未来扩展。

如果你是 Kotlin 爱好者或正在寻找快速搭建 Spring Boot 项目的方案,本文提供的结构是一个不错的起点。


原始标题:CRUD API With Spring Boot And Kotlin