JPA oneToMany entity Collection item remove or add

공부는 혼자하는 거·2022년 11월 7일
0

Spring Tip

목록 보기
29/52

요렇게 2개의 Entity가 있다고 생각했을 때..

@Entity
@Table(name = "album")
class Album(
    id: Long = 0,
    name: String,
) : AuditingEntity(id) {

    @Column(name = "name")
    var name: String = name
        protected set


    @JsonBackReference
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "album", orphanRemoval = true, cascade = [CascadeType.ALL])
    var artistAlbums: MutableList<ArtistAlbum> = mutableListOf()
}

@Entity
@Table(name = "artist_album")
class ArtistAlbum(
    id:Long = 0,
    artist: Artist? = null,
    album: Album? = null
) : AuditingEntity(id) {

    @ManyToOne(fetch = FetchType.LAZY)
    @JsonManagedReference
    @JoinColumn(name = "artist_id")
    var artist: Artist? = artist
        protected set

    @ManyToOne(fetch = FetchType.LAZY)
    @JsonManagedReference
    @JoinColumn(name = "album_id")
    var album: Album? = album
        protected set

}

수정 API 작업 중.. ArtistAlbum collection 일괄업데이트 로직 상 기존 item 중 수정할 부분만 수정하고, 추가된 item이나 삭제된 item은 별도로 다루는 로직을 만들수 있는 방법이 없나.. 고민했지만.. 잘 떠오르지 않았다.
그래서 걍 속 편하게 싹 다 지우고 다시 추가하는 방향으로 잡았다.

data class AlbumUpdateReq(
    @field:Positive(message = "require albumId positive")
    val id: Long,
    val name: String?,
    val artistIds: MutableList<Long> = mutableListOf(),
){

    fun createNewArtistAlbums(artists: List<Artist>): List<ArtistAlbum> {
        return artists.map {
            ArtistAlbum(
                artist = it,
            )
        }
    }

}

Dto를 위와 같이 만들고,



@Entity
@Table(name = "album")
class Album(
    id: Long = 0,
    name: String,
) : AuditingEntity(id) {

    @Column(name = "name")
    var name: String = name
        protected set


    @JsonBackReference
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "album", orphanRemoval = true, cascade = [CascadeType.ALL])
    var artistAlbums: MutableList<ArtistAlbum> = mutableListOf()
    
    fun update(dto: AlbumUpdateReq, newArtistAlbums: List<ArtistAlbum>): Album {

        this.name = dto.name ?: this.name       
        //if (StringUtils.hasLength(newImgUrl)) {
        //    this.imgUrl = newImgUrl
        //}
        // 그냥 속 편하게 다 지우고, 다시 집어넣겠다.. 그냥 for문을 돌리면 ConcurrentModificationException..
        for (artistAlbum in this.artistAlbums.stream().collect(Collectors.toList())) {
            artistAlbum.remove()
        }
        
        for (newArtistAlbum in newArtistAlbums) {
            addArtistAlbum(newArtistAlbum)
        }
        return this
    }
    
    private fun addArtistAlbum(artistAlbum: ArtistAlbum){
        artistAlbum.addAlbum(this)
        this.artistAlbums.add(artistAlbum)
    }
        
}




@Entity
@Table(name = "artist_album")
class ArtistAlbum(
    id:Long = 0,
    artist: Artist? = null,
    album: Album? = null
) : AuditingEntity(id) {

    @ManyToOne(fetch = FetchType.LAZY)
    @JsonManagedReference
    @JoinColumn(name = "artist_id")
    var artist: Artist? = artist
        protected set

    @ManyToOne(fetch = FetchType.LAZY)
    @JsonManagedReference
    @JoinColumn(name = "album_id")
    var album: Album? = album
        protected set
        
        
    fun remove(){
        this.album?.artistAlbums?.remove(this)
        this.artist?.artistAlbums?.remove(this)
        this.artist = null
        this.album = null
    }

    fun addAlbum(album: Album){
        this.album = album
    }

}

서비스를 만들어줬다..

    @Transactional
    fun updateAlbum(dto: AlbumUpdateReq ): AlbumRes {

        val artists = musicDataRepository.findArtistByIds(dto.artistIds)
        val newArtistAlbums = dto.createNewArtistAlbums(artists)
        val album = musicDataRepository.findAlbumById(dto.id) ?: throw AlbumNotFoundException(dto.id)

        return album.update(dto, newArtistAlbums, albumImgUrl).toDto()
    }

내가 원하는 방식은 아니지만.. 돌아가긴 돌아간다..

profile
시간대비효율

0개의 댓글