Добрый день!
Начал вникать в котлин, чтобы было не скучно решил сделать простой туду-лист на Spring и столкнулся с неочевидной для меня проблемой передачи id связанных объектов в RestController.
Есть две модели:
@Entity
@Table(name = "lists")
data class List(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long,
@Column(name = "name", nullable = false, unique = true)
val name: String
)
@Entity
@Table(name = "todos")
data class Todo(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long,
@Column(name = "name", nullable = false, unique = true)
val name: String
@ManyToOne
@JoinColumn(name = "list_id")
@field:NotNull
val list: List,
)
Их репозитории идентичны:
interface ListRepository: CrudRepository<List, Long>
interface TodoRepository: CrudRepository<Todo, Long>
И их контроллеры:
@RestController
@RequestMapping("/api/lists")
class ListController(@Autowired private val listRepository: ListRepository) {
@GetMapping("")
fun getAllLists(): List<List> {
return listRepository.findAll().toList()
}
@GetMapping("/{id}")
fun getListById(@PathVariable("id") listId: Long): ResponseEntity<List> {
val list = listRepository.findById(listId).orElse(null)
return if (list != null) ResponseEntity(list, HttpStatus.OK)
else ResponseEntity(HttpStatus.NOT_FOUND)
}
@PostMapping("")
fun createList(@RequestBody list: List): ResponseEntity<List> {
val createdList = listRepository.save(list)
return ResponseEntity(createdList, HttpStatus.CREATED)
}
@PutMapping("/{id}")
fun updateListById(@PathVariable("id") listId: Long, @RequestBody list: List): ResponseEntity<List> {
val existingList = listRepository.findById(listId).orElse(null)
if (existingList == null) {
return ResponseEntity(HttpStatus.NOT_FOUND)
}
val updatedList = existingList.copy(
name = list.name,
description = list.description
)
listRepository.save(updatedList)
return ResponseEntity(updatedList, HttpStatus.OK)
}
@DeleteMapping("/{id}")
fun deleteListById(@PathVariable("id") listId: Long): ResponseEntity<List> {
if (!listRepository.existsById(listId)) {
return ResponseEntity(HttpStatus.NOT_FOUND)
}
listRepository.deleteById(listId)
return ResponseEntity(HttpStatus.NO_CONTENT)
}
}
@RestController
@RequestMapping("/api/todos")
class TodoController(@Autowired private val todoRepository: TodoRepository) {
@GetMapping("")
fun getAllTodos(): Todo<Todo> {
return todoRepository.findAll().toTodo()
}
@GetMapping("/{id}")
fun getTodoById(@PathVariable("id") todoId: Long): ResponseEntity<Todo> {
val todo = todoRepository.findById(todoId).orElse(null)
return if (todo != null) ResponseEntity(todo, HttpStatus.OK)
else ResponseEntity(HttpStatus.NOT_FOUND)
}
@PostMapping("")
fun createTodo(@RequestBody todo: Todo): ResponseEntity<Todo> {
val createdTodo = todoRepository.save(todo)
return ResponseEntity(createdTodo, HttpStatus.CREATED)
}
@PutMapping("/{id}")
fun updateTodoById(@PathVariable("id") todoId: Long, @RequestBody todo: Todo): ResponseEntity<Todo> {
val existingTodo = todoRepository.findById(todoId).orElse(null)
if (existingTodo == null) {
return ResponseEntity(HttpStatus.NOT_FOUND)
}
val updatedTodo = existingTodo.copy(
name = todo.name,
description = todo.description
)
todoRepository.save(updatedTodo)
return ResponseEntity(updatedTodo, HttpStatus.OK)
}
@DeleteMapping("/{id}")
fun deleteTodoById(@PathVariable("id") todoId: Long): ResponseEntity<Todo> {
if (!todoRepository.existsById(todoId)) {
return ResponseEntity(HttpStatus.NOT_FOUND)
}
todoRepository.deleteById(todoId)
return ResponseEntity(HttpStatus.NO_CONTENT)
}
}
Я полагал, что запрос на создание Todo подобного типа автоматически обрабатывается классом Entity и сериализация пройдет под капотом у джавы:
{
"name": "test",
"list": 1,
}
Но он вызывает ошибку отсутствия конструктора у класса List.
Я так понимаю, что аннотациями здесь не обойтись, да и контролировать сериализацию полей в json хотелось бы самому.
В интернете много статей этому посвящено, но большинство из них для джавы и не то что бы свежие. Есть какие-либо лаконичные проверенные современные практики на подобии DTO в этой области? Если есть, приведите пожалуйста ваши примеры их использования или доки, в которых они описаны, буду крайне благодарен!