나예경 seminar1 과제#60
Conversation
| val playlistServiceImpl: PlaylistServiceImpl, | ||
| val playlistlikeServiceImpl: PlaylistLikeServiceImpl |
There was a problem hiding this comment.
ktlint 등을 참고하셔 indent 를 일관되게 하면 좋을 것 같습니다.
| @ManyToOne | ||
| @JoinColumn(name = "playlist_id") | ||
| val playlist: PlaylistEntity, | ||
| @ManyToOne | ||
| @JoinColumn(name = "user_id") | ||
| val user: UserEntity |
There was a problem hiding this comment.
어떠한 경우의 연관관계에도 Lazy 를 지향하는 것이 좋습니다. ManyToOne 의 default 가 EAGER 라 하더라도 이것을 변경해두는 것이 좋아요. wafflestudio/seminar-2023#169 를 참고하시면 좋습니다.
이걸 Lazy 로 해두었다면, 불필요한 N+1 쿼리가 덜 날아가서 곳곳에서 JOIN FETCH 를 덜 써도 되었을 수도 있어요.
|
|
||
| override fun get(id: Long): Playlist { | ||
| TODO() | ||
| val plEntityList = playlistsongRepository.getPlaylistSongEntity(id) |
There was a problem hiding this comment.
서비스 코드 취지상 PlaylistEntity 를 가져오는 것이 자연스러울 거 같은데, List<PlaylistSongEntity> 를 가져오고 있네요.
이런 식으로 적절한 JOIN FETCH 와 함께 PlaylistEntity 를 가져온 다음에 OneToMany 로 있는 playlist_songs에 접근해 활용하는 식이 읽기 자연스러운 코드 같습니다.
| @RestController | ||
| class SongController { | ||
| class SongController( | ||
| val songServiceImpl: SongServiceImpl |
There was a problem hiding this comment.
Spring DI 의 이점을 활용하려면, 구현체인 Impl 이 아닌 SongService 타입으로 주입 받는 것이 맞습니다. 그래야 나중에 cache 를 이용한 SongService 의 다른 구현체로 바꿔끼더라도, 코드를 많이 수정할 필요 없이 원하는 Bean 으로 착착 갈아끼기 좋으니까요.
| interface SongRepository : JpaRepository<SongEntity, Long> { | ||
| @Query(value = "SELECT s FROM songs s " | ||
| + "JOIN FETCH s.album JOIN FETCH s.song_artists sa JOIN FETCH sa.song_artist " | ||
| + "WHERE s.title LIKE %:keyword% ORDER BY length(s.title)") |
There was a problem hiding this comment.
쿼리로 DB 에서 length() 로 order 하는 것보단, 일단 application (서버) 에 가져와서 코드로 sort 하는 게 성능상 더 나은 선택일 것 같습니다.
| + "WHERE s.title LIKE %:keyword% ORDER BY length(s.title)") | ||
| fun findSongEntity(keyword: String): List<SongEntity> | ||
|
|
||
| } No newline at end of file |
There was a problem hiding this comment.
git 에서 파일의 마지막 newline 을 넣는 것이 좋은 습관입니다.
| val playlistlike = playlistlikeRepository.getPlaylistLikeEntity(playlistId, userId) | ||
| if (!playlistlike.isPresent) throw PlaylistNeverLikedException() | ||
|
|
||
| playlistlikeRepository.delete(playlistlike.get()) |
There was a problem hiding this comment.
참고로, deleteByXxx...() 같은 식으로 repository 메서드를 만들어서 삭제 쿼리를 날릴 수도 있습니다.
| @Query("SELECT pl FROM playlist_likes pl " | ||
| + "WHERE pl.playlist.id = :playlistId and pl.user.id = :userId") | ||
| fun getPlaylistLikeEntity(playlistId: Long, userId: Long): Optional<PlaylistLikeEntity> |
There was a problem hiding this comment.
이 정도 쿼리는 직접 @Query로 JPQL 을 적지 않고, findByPlaylistIdAndUserId()으로 메서드 이름을 지어주면 spring data 가 동일 쿼리를 만들어줍니다.
그리고, Kotlin 에서는 Optional 을 굳이 쓸 필요 없이 PlaylistLikeEntity?를 쓰면 됩니다. 이에 대해서는 wafflestudio/seminar-2023#160 (reply in thread) 를 참고해보세요.
| package com.wafflestudio.seminar.spring2023.playlist.repository | ||
|
|
||
| import org.springframework.data.jpa.repository.JpaRepository | ||
| import java.util.* |
No description provided.