最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Spring Boot - HowWhere to convert foreign key of a DTO to an Entity - Stack Overflow

programmeradmin3浏览0评论

I am making a REST service with Spring Boot, and I am having trouble at the point where I convert JSON requests/DTOs to the corresponding entities, specifically when an entity contains a reference to another entity. Say we have these objects for example:

data class BookEntity(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Int?,

    val name: String,

    @ManyToOne
    @JoinColumn(name = "author_id")
    val author: AuthorEntity,
)
data class AuthorEntity(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Int?,

    val name: String,
)
data class BookRequestDto(
    val name: String,
    val authorId: Int,
)

The application has controller-service-repository layers, and as far as I know I am supposed to perform the DTO -> entity conversion in the controller layer, and then pass the resulting entity to the service layer. However, in order to convert the book request DTO to an entity, I first have to fetch the appropriate Author entity from the repository based on the given authorId. My question is: where exactly should I do that?

Given that the service layer should only accept entities, it looks like I should do the author fetching in the controller layer. But that would mean that the Book controller would need to have access to the Author service layer, and that's what I'm not sure is good practice.

Here is what I mean (using obvious extension functions to do the DTO/entity conversions)

@RestController
@RequestMapping(path = ["/books"])
class BookController(
    private val bookService: BookService,
    private val authorService: AuthorService,
) {
    @PostMapping
    fun createBook(@RequestBody bookDto: BookRequestDto): ResponseEntity<BookResponseDto> {
        val author = authorService.get(bookDto.authorId)
        val bookEntity = bookDto.toBookEntity(author = author)
        val createdBook = bookService.create(bookEntity)
        return ResponseEntity(createdBook.toBookResponseDto(), HttpStatus.CREATED)
    }
}

Is this the usual way of doing this, or is mixing multiple services in a controller a bad idea? I clearly have to access the author repository somewhere, I just don't know where the best place would be. Is there a better way?

I am making a REST service with Spring Boot, and I am having trouble at the point where I convert JSON requests/DTOs to the corresponding entities, specifically when an entity contains a reference to another entity. Say we have these objects for example:

data class BookEntity(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Int?,

    val name: String,

    @ManyToOne
    @JoinColumn(name = "author_id")
    val author: AuthorEntity,
)
data class AuthorEntity(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Int?,

    val name: String,
)
data class BookRequestDto(
    val name: String,
    val authorId: Int,
)

The application has controller-service-repository layers, and as far as I know I am supposed to perform the DTO -> entity conversion in the controller layer, and then pass the resulting entity to the service layer. However, in order to convert the book request DTO to an entity, I first have to fetch the appropriate Author entity from the repository based on the given authorId. My question is: where exactly should I do that?

Given that the service layer should only accept entities, it looks like I should do the author fetching in the controller layer. But that would mean that the Book controller would need to have access to the Author service layer, and that's what I'm not sure is good practice.

Here is what I mean (using obvious extension functions to do the DTO/entity conversions)

@RestController
@RequestMapping(path = ["/books"])
class BookController(
    private val bookService: BookService,
    private val authorService: AuthorService,
) {
    @PostMapping
    fun createBook(@RequestBody bookDto: BookRequestDto): ResponseEntity<BookResponseDto> {
        val author = authorService.get(bookDto.authorId)
        val bookEntity = bookDto.toBookEntity(author = author)
        val createdBook = bookService.create(bookEntity)
        return ResponseEntity(createdBook.toBookResponseDto(), HttpStatus.CREATED)
    }
}

Is this the usual way of doing this, or is mixing multiple services in a controller a bad idea? I clearly have to access the author repository somewhere, I just don't know where the best place would be. Is there a better way?

Share Improve this question asked 22 hours ago FinnCoalFinnCoal 631 silver badge6 bronze badges 1
  • I believe the best practice is to keep controllers as minimal as possible, delegating tasks to service classes. The methods in the service classes should then be annotated with the appropriate @Transactional annotations. – Roar S. Commented 22 hours ago
Add a comment  | 

1 Answer 1

Reset to default 0

This might get closed as an opinion based question :), but ...

  • controllers can take all the services you want, there is no relationship
  • controllers should only speak request/response DTOs annotated with hibernate validator annotations on the request
  • repositories should only speak database DTOs annotated with hibernate annotations
  • service layer is the middle man that does request/response objects <-> database objects conversion
  • you shouldn't have .toBookResponseDto() type methods, request/response should not know about each other or the database objects at all, that is strictly a service layer thing
  • you didn't show your conversion code, but you shouldn't manage that yourself either as its error prone, boiler plate and a maintenance headache
  • for conversion between different objects, use a singleton bean of either ModelMapper or MapStruct. NOTE: MapStruct pre-generates the conversion code at compile time, so its faster in general and keeping it a singleton is not critical although recommended. For ModelMapper, you should definitely use a singleton instance since type conversions are done at runtime and ModelMapper will cache them.
  • for the example you showed, you shouldn't have an AuthorRepository or an AuthorService. You only need those for the root object (in this case Book) and then your service and repository would have a findBooksByAuthor method. You only need the Author service/repository when the Author is the root. I.e. listAllAuthors()
发布评论

评论列表(0)

  1. 暂无评论