DevLog ๐Ÿ˜ถ

[JPA] @OneToMany ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘ ์‹œ ๊ณ ๋ คํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ๋“ค ๋ณธ๋ฌธ

Back-end/JPA

[JPA] @OneToMany ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘ ์‹œ ๊ณ ๋ คํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ๋“ค

dolmeng2 2023. 7. 16. 20:13

๐ŸŒฑ ๋“ค์–ด๊ฐ€๊ธฐ ์ „

JPA์—์„œ @OneToMany ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘๋ณด๋‹ค๋Š” ๋‹ค๋Œ€์ผ ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์„ ๊ถŒ์žฅํ•œ๋‹ค๋Š” ๋ง์„ ๋ณด์•˜์„ ๊ฒƒ์ด๋‹ค.

ํ•˜์ง€๋งŒ, ๊ฐ์ฒด์ง€ํ–ฅ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์„ค๊ณ„ํ•˜๋‹ค ๋ณด๋ฉด ๋‹ค๋Œ€์ผ์˜ ์ƒํ™ฉ๋ณด๋‹ค๋Š” 1์ธ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ธฐ์ค€์œผ๋กœ N์ธ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋” ๋งŽ๊ณ , (์ ์–ด๋„ ๋‚ด ๊ฒฝํ—˜์—์„œ๋Š” ๊ทธ๋žฌ๋‹ค) N์ธ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๊ตณ์ด 1์ธ ์—”ํ‹ฐํ‹ฐ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๋ชฐ๋ผ๋„ ๋๋˜ ์ ์ด ๋งŽ์•˜๋‹ค. ๊ทธ๋Ÿฌ๋‹ค ๋ณด๋‹ˆ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ผ๋Œ€๋‹ค ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘์„ ๋งŽ์ด ์‚ฌ์šฉํ–ˆ์—ˆ๋Š”๋ฐ, ์™œ ๊ทธ๋žฌ๋˜ ๊ฒƒ์ธ์ง€ ๊ถ๊ธˆํ•ด์„œ ๋‚˜๋ฆ„๋Œ€๋กœ ์‹คํ—˜์„ ํ•ด๋ณด๊ณ ์ž ํ•œ๋‹ค.

(ex. ๊ฒŒ์‹œ๊ธ€๊ณผ ๊ฒŒ์‹œ๊ธ€ ์ด๋ฏธ์ง€์˜ ๊ด€๊ณ„์—์„œ, ๊ฒŒ์‹œ๊ธ€ ์ด๋ฏธ์ง€๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ฒŒ์‹œ๊ธ€ ์ •๋ณด๋ฅผ ์ฐพ์•„์˜ค๋Š” ๊ฒƒ๋ณด๋‹ค๋Š” ๊ฒŒ์‹œ๊ธ€์„ ๊ธฐ์ค€์œผ๋กœ ๊ฒŒ์‹œ๊ธ€ ์ด๋ฏธ์ง€ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒŒ ๋” ๋งŽ์•˜์Œ)

 


 

๐ŸŒฑ ๊ธฐ๋ณธ ์—”ํ‹ฐํ‹ฐ ์„ค๊ณ„ํ•˜๊ธฐ

@Entity
class Board(
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long = 0L,

    @Embedded
    val title: BoardTitle,

    @Embedded
    val content: BoardContent,

    @Embedded
    var boardTags: BoardTags = BoardTags(Collections.emptyList())
) {

    fun addTag(boardTag: BoardTag) {
        boardTags.addTag(boardTag)
        boardTags = BoardTags(boardTags.tags)
    }
}

ํ•˜๋‚˜์˜ ๊ฒŒ์‹œ๊ธ€์— ์ œ๋ชฉ๊ณผ ๋‚ด์šฉ ์ •๋ณด๊ฐ€ ์กด์žฌํ•˜๋ฉฐ, ๊ฒŒ์‹œ๊ธ€ ํƒœ๊ทธ์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ 1:N์œผ๋กœ ๊ตฌ์„ฑ๋œ ์—”ํ‹ฐํ‹ฐ๋‹ค.

BoardTitle๊ณผ BoardContent๋Š” VO์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ณ„๋„๋กœ ์ฒจ๋ถ€ํ•˜์ง€๋Š” ์•Š๊ฒ ๋‹ค.

์—ฌ๊ธฐ์„œ ๊ฒŒ์‹œ๊ธ€ ํƒœ๊ทธ ์—”ํ‹ฐํ‹ฐ์— ๋Œ€ํ•ด์„œ ์–ด๋–ค ์‹์œผ๋กœ ์„ค์ •ํ•˜๋Š”์ง€์— ๋”ฐ๋ผ์„œ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์ง€๊ฒŒ ๋œ๋‹ค.

 


 

๐ŸŒฑ ์กฐ์ธ ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•˜์—ฌ ์ผ๋Œ€๋‹ค ๊ด€๊ณ„ ๊ตฌ์„ฑํ•˜๊ธฐ

@Embeddable
class BoardTags(
    tags: MutableList<BoardTag> = Collections.emptyList()
) {

    @OneToMany(fetch = FetchType.LAZY)
    val tags: MutableList<BoardTag> = tags.toMutableList()

    fun addTag(boardTag: BoardTag) {
        tags.add(boardTag)
    }
}

๋‹จ์ˆœํžˆ @OneToMany ์–ด๋…ธํ…Œ์ด์…˜๋งŒ ๋‹ฌ์•˜์„ ๋•Œ, ์ƒ์„ฑ๋œ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ๋ฅผ ๋ณด๋ฉด board_tags๋ผ๋Š” ๋ณ„๋„์˜ ์กฐ์ธ ํ…Œ์ด๋ธ”์ด ์ƒ์„ฑ๋œ๋‹ค.

์—ฌ๊ธฐ์„œ board_tags์—๋Š” board_id์™€ tags_id๋ผ๋Š” ํ•„๋“œ๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ๋‹ค. ์—ฌ๊ธฐ์„œ board_id์˜ ๋„ค์ด๋ฐ์€ BoardTags๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์—”ํ‹ฐํ‹ฐ (board)์˜ ์ด๋ฆ„ + _id๋ฅผ ํ•ฉ์นœ ๊ฒƒ์ด๊ณ , tags_id์˜ ๋„ค์ด๋ฐ์€ @OneToMany๋กœ ์„ ์–ธํ•œ ํ•„๋“œ๋ช… (tags) + _id๋ฅผ ๋ถ™์ธ ํ˜•ํƒœ์ด๋‹ค.

 


โœ”๏ธ ์ €์žฅ ํ…Œ์ŠคํŠธ

@Test
@Rollback(false) // ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ ๋กค๋ฐฑ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ 
fun save() { 
    val ํƒœ๊ทธ1 = BoardTag(name = "ํƒœ๊ทธ1")
    val ํƒœ๊ทธ2 = BoardTag(name = "ํƒœ๊ทธ2")
    boardTagRepository.save(ํƒœ๊ทธ1)
    boardTagRepository.save(ํƒœ๊ทธ2)

    val board = Board(
        title = BoardTitle("์ œ๋ชฉ"), content = BoardContent("๋‚ด์šฉ")
    )
    board.addTag(ํƒœ๊ทธ1)
    board.addTag(ํƒœ๊ทธ2)

    boardRepository.save(board)
}

๊ฐ€์žฅ ๋จผ์ €, ๊ฒŒ์‹œ๊ธ€ ํƒœ๊ทธ์™€ ๊ฒŒ์‹œ๊ธ€์— ๋Œ€ํ•ด์„œ insert ํ•˜๋Š” ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
์ด๋•Œ, ๊ฒŒ์‹œ๊ธ€ ํƒœ๊ทธ ์ €์žฅ ์‹œ board_id์— ๋Œ€ํ•œ ์ปฌ๋Ÿผ์€ ์ฑ„์›Œ์ง€์ง€ ์•Š๊ณ  name, id์— ๋Œ€ํ•ด์„œ๋งŒ ์‚ฝ์ž…๋œ๋‹ค.
cf) ํ‚ค ์ „๋žต์ด IDENTITY์ด๊ธฐ ๋•Œ๋ฌธ์— save ์‹œ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

์ดํ›„, flush ์‹œ์ ์—์„œ ์กฐ์ธ ํ…Œ์ด๋ธ”์— ์œ„์™€ ๊ฐ™์ด insert ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
์ฆ‰, ์ฟผ๋ฆฌ๊ฐ€ ์ด 5๋ฒˆ์ด๋‚˜ ๋‚˜๊ฐ€๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด์–ด์„œ ์ƒ๋‹นํžˆ ๋น„ํšจ์œจ์ ์ด๋‹ค.

 


โœ”๏ธ ์‚ญ์ œ ํ…Œ์ŠคํŠธ

๊ฒŒ์‹œ๊ธ€์— ๋Œ€ํ•œ ํƒœ๊ทธ๋ฅผ ์‚ญ์ œํ•˜๋Š” ํ…Œ์ŠคํŠธ์ด๋‹ค.
์ด๋•Œ, ๊ฒŒ์‹œ๊ธ€์— ์กด์žฌํ•˜๋Š” ํƒœ๊ทธ ์ •๋ณด๋ฅผ ์ œ๊ฑฐํ•˜๊ณ , ๊ทธ ๋‹ค์Œ ๊ฒŒ์‹œ๊ธ€์˜ ํƒœ๊ทธ ์ •๋ณด๋ฅผ ์ œ๊ฑฐํ•œ๋‹ค.

@Test
@Rollback(false)
fun delete() {
    // ์ €์žฅ ๋กœ์ง
    val ํƒœ๊ทธ1 = BoardTag(name = "ํƒœ๊ทธ1")
    val ํƒœ๊ทธ2 = BoardTag(name = "ํƒœ๊ทธ2")
    boardTagRepository.save(ํƒœ๊ทธ1)
    boardTagRepository.save(ํƒœ๊ทธ2)

    val board = Board(title = BoardTitle("์ œ๋ชฉ"), content = BoardContent("๋‚ด์šฉ"))
    board.addTag(ํƒœ๊ทธ1)
    board.addTag(ํƒœ๊ทธ2)

    boardRepository.save(board)
    entityManager.flush()
		
		/** ์ œ๊ฑฐ ๋กœ์ง */
    val savedBoard = boardRepository.findById(1L).get()
    savedBoard.boardTags.tags.removeAt(0) // ํ•„์ˆ˜!
    boardTagRepository.deleteById(1L)
}

๊ธฐ๋ณธ์ ์œผ๋กœ ํƒœ๊ทธ ์ •๋ณด๋ฅผ ์ œ๊ฑฐํ•˜๋ ค๋ฉด deleteById๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ, ๋ฐ”๋กœ deleteById๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ์กฐ์ธ ํ…Œ์ด๋ธ”์— ๊ฑธ๋ ค ์žˆ๋Š” ์™ธ๋ž˜ํ‚ค์— ์˜ํ•ด์„œ board_tag ํ…Œ์ด๋ธ”์— ์žˆ๋Š” ํ•„๋“œ๋ฅผ ๋ฐ”๋กœ ์ œ๊ฑฐํ•  ์ˆ˜ ์—†๋‹ค.
๊ทธ๋ž˜์„œ board์— ์กด์žฌํ•˜๋Š” tag ๋ฆฌ์ŠคํŠธ์—์„œ removeAt์„ ํ†ตํ•ด 1์ฐจ์ ์œผ๋กœ ์กฐ์ธ ํ…Œ์ด๋ธ”์— ๋Œ€ํ•ด์„œ ์ œ๊ฑฐ๋ฅผ ํ•ด์•ผ ํ•œ๋‹ค. (savedBoard.boardTags.tags.removeAt(0))


์ฟผ๋ฆฌ๋ฅผ ๋ณด๋ฉด, ์šฐ์„  ์กฐ์ธ ํ…Œ์ด๋ธ”์ธ board_tags์— ๋Œ€ํ•ด์„œ delete ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ด๋•Œ, board_id๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ œ๊ฑฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— board_tags์— ์ €์žฅ๋œ 2๊ฐœ์˜ ๋ ˆ์ฝ”๋“œ ๋ชจ๋‘๊ฐ€ ์ œ๊ฑฐ๋œ๋‹ค. ์ดํ›„, ์ œ๊ฑฐ๋˜์ง€ ์•Š์€ ๋‚จ์€ ๋ ˆ์ฝ”๋“œ์— ๋Œ€ํ•ด (tags_id = 2์ธ ๋ ˆ์ฝ”๋“œ) ๋‹ค์‹œ ์‚ฝ์ž…ํ•˜๋Š” ๊ณผ์ •์ด ๋ฐœ์ƒํ•œ๋‹ค. ๋งŒ์•ฝ, board_id = 1์ธ ๋ ˆ์ฝ”๋“œ๊ฐ€ 5๊ฐœ๊ฐ€ ์žˆ์—ˆ๋‹ค๋ฉด 5๊ฐœ ๋ชจ๋‘์— ๋Œ€ํ•ด ์ œ๊ฑฐํ•˜๊ณ , 4๊ฐœ์— ๋Œ€ํ•ด์„œ ์ถ”๊ฐ€์ ์ธ insert ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ๋” ๋ถˆํ•„์š”ํ•œ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€์„ ๊ฒƒ์ด๋‹ค.


์ตœ์ข…์ ์œผ๋กœ ์œ„์˜ ๊ณผ์ •์ด ๋๋‚˜๊ณ  board_tag์— ๋Œ€ํ•ด์„œ ์ตœ์ข…์ ์ธ ํƒœ๊ทธ ์ œ๊ฑฐ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

๊ฒฐ๊ณผ์ ์œผ๋กœ insert ์ฟผ๋ฆฌ๋กœ ์ธํ•ด์„œ ์›ํ•˜์ง€ ์•Š๋Š” ์ฟผ๋ฆฌ๊ฐ€ ์—„์ฒญ ๋‚˜๊ฐˆ ์ˆ˜๋„ ์žˆ๋‹ค๋Š” ํŠน์ง•์„ ๊ฐ€์ง„๋‹ค.

 


 

๐ŸŒฑ ์กฐ์ธ ํ…Œ์ด๋ธ”์„ ์—†์• ๋ณด๊ธฐ

์กฐ์ธ ํ…Œ์ด๋ธ”์— ์˜ํ•ด์„œ insert ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์ด๋ผ๋ฉด, ์กฐ์ธ ํ…Œ์ด๋ธ” ์ž์ฒด๋ฅผ ์—†์• ๋ฉด ๋˜์ง€ ์•Š์„๊นŒ?

@Embeddable
class BoardTags(
    tags: MutableList<BoardTag> = Collections.emptyList()
) {

    @OneToMany(fetch = FetchType.LAZY)
		@JoinColumn(name = "board_id")
    val tags: MutableList<BoardTag> = tags.toMutableList()

    fun addTag(boardTag: BoardTag) {
        tags.add(boardTag)
    }
}

@JoinColumn์„ ํ†ตํ•ด์„œ board_tag์— ๋ช…์‹œ์ ์œผ๋กœ board์— ๋Œ€ํ•œ ์•„์ด๋””๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” ํ•„๋“œ๋ฅผ ์ง€์ •ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.
1:N ๊ด€๊ณ„์—์„œ ์™ธ๋ž˜ํ‚ค๋Š” ํ•ญ์ƒ N ํ…Œ์ด๋ธ”์— ๊ฑธ๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— n์ชฝ์—์„œ column ์ •๋ณด๋ฅผ ์ง€์ •ํ•ด์ค€๋‹ค.

์ƒ์„ฑ๋œ ํ…Œ์ด๋ธ”์„ ๋ณด๋ฉด, ์ด๋ฒˆ์—๋Š” ์กฐ์ธ ํ…Œ์ด๋ธ” ๋Œ€์‹  board_tag ํ…Œ์ด๋ธ”์— board ์ •๋ณด๋ฅผ ์ง€์ •ํ•˜๊ธฐ ์œ„ํ•œ board_id ์ •๋ณด๊ฐ€ ์ถ”๊ฐ€๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 


โœ”๏ธ  ์ €์žฅ ํ…Œ์ŠคํŠธ

@Test
@Rollback(false) // ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ ๋กค๋ฐฑ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ 
fun save() { 
    val ํƒœ๊ทธ1 = BoardTag(name = "ํƒœ๊ทธ1")
    val ํƒœ๊ทธ2 = BoardTag(name = "ํƒœ๊ทธ2")
    boardTagRepository.save(ํƒœ๊ทธ1)
    boardTagRepository.save(ํƒœ๊ทธ2)

    val board = Board(
        title = BoardTitle("์ œ๋ชฉ"), content = BoardContent("๋‚ด์šฉ")
    )
    board.addTag(ํƒœ๊ทธ1)
    board.addTag(ํƒœ๊ทธ2)

    boardRepository.save(board)
}

๋จผ์ €, ํƒœ๊ทธ 2๊ฐœ๊ฐ€ ์ €์žฅ๋œ ๊ฒฐ๊ณผ์ด๋‹ค.
๋งˆ์ฐฌ๊ฐ€์ง€๋กœ name, id๋งŒ ๊ฐ’์ด ์ฑ„์›Œ์ ธ์„œ board_id์— ๋Œ€ํ•œ ์ปฌ๋Ÿผ์€ null ๊ฐ’์„ ๊ฐ€์ง€๊ฒŒ ๋œ๋‹ค.

๊ทธ๋ฆฌ๊ณ , ๊ฒŒ์‹œ๊ธ€์— ๋Œ€ํ•œ insert ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

ํ•˜์ง€๋งŒ, ์—ฌ๊ธฐ์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. board_tag์— ๋Œ€ํ•ด ์ถ”๊ฐ€์ ์œผ๋กœ ์—…๋ฐ์ดํŠธ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์ด๋‹ค.

์ด๋Š” ์ฒ˜์Œ์— board_tag๋ฅผ ์ €์žฅํ•  ๋•Œ board_id์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ์—†๋Š” ์ƒํƒœ์˜€๊ธฐ ๋•Œ๋ฌธ์— flush ์‹œ์ ์— board.addTag()๋กœ ์ธํ•ด ์ถ”๊ฐ€๋œ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์—…๋ฐ์ดํŠธ๋ฅผ ํ•˜๊ฒŒ ๋œ ๊ฒƒ์ด๋‹ค.

์œ„์™€ ๊ฐ™์ด @OneToMany ๋‹จ๋ฐฉํ–ฅ์—์„œ๋Š” ๋ฐ์ดํ„ฐ ์ €์žฅ ์‹œ ์—…๋ฐ์ดํŠธ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.

 


 

โœ”๏ธ  ์‚ญ์ œ ํ…Œ์ŠคํŠธ

@Test
@Rollback(false)
fun delete() {
    // ์ €์žฅ ๋กœ์ง
    ...

    // ๊ฐ•์ œ flush -> update ์ฟผ๋ฆฌ ๋ฐœ์ƒ
    entityManager.flush()

    // ์กฐํšŒ -> ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ๋ฐœ์ƒ
    val savedBoard = boardRepository.findById(1L).get()
	
   // ๋ณด๋“œ์— ์กด์žฌํ•˜๋Š” ๋ณด๋“œ ํƒœ๊ทธ ์ œ๊ฑฐ
    savedBoard.boardTags.tags.removeAt(0)
    
   // ๋ณด๋“œ ํƒœ๊ทธ ์ œ๊ฑฐ
    boardTagRepository.deleteById(1L)
}

entityManager.flush ์ดํ›„์˜ ๋กœ์ง์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ฟผ๋ฆฌ์ด๋‹ค.
์ €์žฅ ํ›„ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ๊ฒŒ์‹œ๊ธ€์„ ์กฐํšŒํ•œ ๋’ค, ํ•ด๋‹น ๊ฒŒ์‹œ๊ธ€์— ์—ฐ๊ด€๋œ ํƒœ๊ทธ๋ฅผ ์ œ๊ฑฐํ•˜์˜€๋‹ค. removeAt(0)์„ ์ง„ํ–‰ํ•˜๊ฒŒ ๋˜๋ฉด ๊ฒŒ์‹œ๊ธ€ ๊ธฐ์ค€์œผ๋กœ ‘๊ฒŒ์‹œ๊ธ€์˜ ์ฒซ ๋ฒˆ์งธ ํƒœ๊ทธ ์ •๋ณด๊ฐ€ ์ œ๊ฑฐ’ ๋œ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— id = 1์ธ board_tag์— ์กด์žฌํ•˜๋Š” board_id๋ฅผ null๋กœ ์„ธํŒ…ํ•˜๋Š” ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

 

์ด๋Š” ์‚ฌ์‹ค Hibernate์˜ flush ์ˆœ์„œ์™€ ์ข€ ๋” ๊ฐ€๊นŒ์šด ๋ฌธ์ œ์ด๋‹ค. Hibernae์—์„œ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋ฅผ FK ์—†์ด ์ €์žฅํ•˜๊ณ  ์ปฌ๋ ‰์…˜์— ๋Œ€ํ•ด ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ฒŒ ๋˜๋ฉด FK์— ๋Œ€ํ•œ update ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ํ˜„์žฌ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ ์ƒ FK๊ฐ€ ๊ฑธ๋ฆฌ์ง€ ์•Š์•„์„œ FK๊ฐ€ ์—†๋Š” ์ƒํƒœ๋‹ˆ๊นŒ update ์—ฐ์‚ฐ์ด ๋จผ์ € ๋ฐœ์ƒํ•˜๊ณ  ์—”ํ‹ฐํ‹ฐ์— ๋Œ€ํ•œ delete๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์ด๋‹ค.


cf) ์ฐธ๊ณ ๋กœ, update - delete ๋ชจ๋‘ flush ์‹œ์— ๋ฐœ์ƒํ•˜๋ฉฐ ์‹ค์ œ๋กœ board_tag๋ฅผ ์ œ๊ฑฐํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด (deleteById) ๋‘ ์ฟผ๋ฆฌ ๋ชจ๋‘ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค. delete ์‹œ์— ์ถ”๊ฐ€๋กœ update ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๋Š๋‚Œ์ด๋‹ค.

 

๐Ÿ’ก Hibernate์˜ flush ์ˆœ์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

1. OrphanRemovalAction
2. AbstractEntityInsertAction
3. EntityUpdateAction
4. QueuedOperationCollectionAction
5. CollectionRemoveAction
6. CollectionUpdateAction
7. CollectionRecreateAction
8. EntityDeleteAction

 

+ ๋ฌด์กฐ๊ฑด '์ œ๊ฑฐ ํ–‰์œ„'๋ฅผ ํ•œ๋‹ค๊ณ  ํ•ด์„œ update ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค.

@Test
@Rollback(false)
fun delete() {
    // ์ €์žฅ ๋กœ์ง
    ...

    // ๊ฐ•์ œ flush -> update ์ฟผ๋ฆฌ ๋ฐœ์ƒ
    entityManager.flush()

    // ์กฐํšŒ -> ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ๋ฐœ์ƒ
    val savedBoard = boardRepository.findById(1L).get()

    // ๋ณด๋“œ ํƒœ๊ทธ ์ œ๊ฑฐ
    boardTagRepository.deleteById(1L)
}

ํ•˜์ง€๋งŒ, removeAt()์„ ํ†ตํ•ด์„œ ์ œ๊ฑฐํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๋‹จ์ˆœํ•œ delete ์ฟผ๋ฆฌ๋งŒ ๋ฐœ์ƒํ•œ๋‹ค.

๋‹จ์ˆœํžˆ boardTag์˜ id ๊ฐ’๋งŒ์„ ์ฐธ๊ณ ํ•˜์—ฌ delete๋ฅผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, board_id์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ๋”ฑํžˆ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. (์ด์ „์—๋Š” ๊ฒŒ์‹œ๊ธ€์„ ๊ธฐ์ค€์œผ๋กœ ํƒœ๊ทธ ์ •๋ณด๋ฅผ ์ œ๊ฑฐํ•˜์˜€๊ธฐ ๋•Œ๋ฌธ)

๊ทธ๋ž˜์„œ ์‚ญ์ œ ์‹œ์— update ์ฟผ๋ฆฌ๊ฐ€ ๋ฌด์กฐ๊ฑด์ ์œผ๋กœ ๋ฐœ์ƒํ•œ๋‹ค๋Š” ๋ง์€ ํ‹€๋ฆฐ ๋ง์ด๋‹ค.

 


 

๐ŸŒฑ ๊ทธ๋ ‡๋‹ค๋ฉด, ์ผ๋Œ€๋‹ค ๋‹จ๋ฐฉํ–ฅ์€ ์•„์˜ˆ ์“ฐ๋ฉด ์•ˆ ๋˜๋Š” ๊ฑธ๊นŒ? - updatable = false ์ง€์ •ํ•˜๊ธฐ

๊ทธ๋ ‡๋‹ค๋ฉด ์šฐ๋ฆฌ๋Š” ํ•ญ์ƒ ์ผ๋Œ€๋‹ค ๋‹จ๋ฐฉํ–ฅ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋ ‡๊ฒŒ update ์ฟผ๋ฆฌ๋ฅผ ๋ด์•ผ ํ•˜๋Š” ๊ฒƒ์ผ๊นŒ?

update ์ฟผ๋ฆฌ๋ฅผ ์—†์• ๊ณ  ์‹ถ์„ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด, ์ถ”๊ฐ€์ ์œผ๋กœ board_id์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๊ฐ€ ์ง„ํ–‰๋˜์ง€ ์•Š๋„๋ก ์ฟผ๋ฆฌ ์ž์ฒด์— updatable์— ๋Œ€ํ•œ ์กฐ๊ฑด์„ false๋กœ ์ง€์ •ํ•ด๋ณด์ž.

@Embeddable
class BoardTags(
    tags: MutableList<BoardTag> = Collections.emptyList()
) {

    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "board_id", updatable = false)
    val tags: MutableList<BoardTag> = tags.toMutableList()

    ...
}

์œ„์™€ ๊ฐ™์ด updatable=false๋ฅผ ์ง€์ •ํ•˜๊ฒŒ ๋˜๋ฉด, board_tag ํ…Œ์ด๋ธ”์— ์กด์žฌํ•˜๋Š” board_id ํ•„๋“œ๋Š” ์ˆ˜์ • ๋ถˆ๊ฐ€๋Šฅ ์ƒํƒœ๊ฐ€ ๋œ๋‹ค.

์ฆ‰, ํ•œ ๋ฒˆ ์ง€์ •๋œ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋‹ค์‹œ ์„ธํŒ…ํ•˜๋Š” ๊ฒƒ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋œ ๊ฒƒ์ด๋‹ค.

 


 

โœ”๏ธ  ์ €์žฅ ํ…Œ์ŠคํŠธ

@Test
@Rollback(false)
fun save() {
    val ํƒœ๊ทธ1 = BoardTag(name = "ํƒœ๊ทธ1")
    val ํƒœ๊ทธ2 = BoardTag(name = "ํƒœ๊ทธ2")
    boardTagRepository.save(ํƒœ๊ทธ1)
    boardTagRepository.save(ํƒœ๊ทธ2)

    val board = Board(
        title = BoardTitle("์ œ๋ชฉ"), content = BoardContent("๋‚ด์šฉ")
    )
    board.addTag(ํƒœ๊ทธ1)
    board.addTag(ํƒœ๊ทธ2)

    boardRepository.save(board)
}

update ์ฟผ๋ฆฌ๋Š” ๋ฐœ์ƒํ•˜์ง€ ์•Š์ง€๋งŒ, ์ฒ˜์Œ board_tag๋ฅผ ์‚ฝ์ž…ํ•  ๋•Œ board_id์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ์ง€์ •๋˜์ง€ ์•Š๋Š” ์ƒํƒœ๋กœ insert๊ฐ€ ๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ถ”ํ›„ board๊ฐ€ ์‚ฝ์ž…๋˜๋”๋ผ๋„ ๊ณ„์† board_tag์˜ board_id๋Š” ๊ณ„์† null๋กœ ๋‚จ๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

 


 

โœ”๏ธ  ์‚ญ์ œ ํ…Œ์ŠคํŠธ

@Test
@Rollback(false)
fun delete() {
    // ์ €์žฅ ๋กœ์ง
    ...

    // ๊ฐ•์ œ flush -> update ์ฟผ๋ฆฌ ๋ฐœ์ƒ
    entityManager.flush()

    // ์กฐํšŒ -> ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ๋ฐœ์ƒ
    val savedBoard = boardRepository.findById(1L).get()
		
    // ๋ณด๋“œ์— ์กด์žฌํ•˜๋Š” ๋ณด๋“œ ํƒœ๊ทธ ์ œ๊ฑฐ
    savedBoard.boardTags.tags.removeAt(0)

    // ๋ณด๋“œ ํƒœ๊ทธ ์ œ๊ฑฐ
    boardTagRepository.deleteById(1L)
}

ํ•˜์ง€๋งŒ, ์‚ญ์ œ ํ…Œ์ŠคํŠธ์—์„œ๋Š” ๋ณด๋“œ์— ์กด์žฌํ•˜๋Š” ํƒœ๊ทธ ์ •๋ณด๋ฅผ removeAt์œผ๋กœ ์ œ๊ฑฐํ•˜๋”๋ผ๋„ update ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค!

insert ์‹œ ์ด๋ฏธ board_id๊ฐ€ null์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ณ„๋„์˜ update ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•  ํ•„์š”๊ฐ€ ์—†์–ด์„œ ๊ทธ๋ƒฅ delete ์ฟผ๋ฆฌ๋งŒ ๋‚˜๊ฐ„ ๊ฒƒ์ด๋‹ค.

 

 


 

๐ŸŒฑ board_id๋„ ๊ฐ’์ด ์ฑ„์›Œ์กŒ์œผ๋ฉด ์ข‹๊ฒ ์–ด! - nullable = false ์ง€์ •ํ•ด์ฃผ๊ธฐ

ํ•˜์ง€๋งŒ, ์œ„์˜ ๋ฐฉ๋ฒ•์€ ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ์„ธํŒ…๋˜์ง€ ์•Š์œผ๋‹ˆ๊นŒ ๋งค์šฐ ์ฐ์ฐํ•˜๋‹ค.

board_id๊ฐ€ null์ธ ๊ฒƒ์ด ๋งˆ์Œ์— ๋“ค์ง€ ์•Š๋Š” ๊ฒƒ์ด๋‹ˆ๊นŒ, ์• ์ดˆ์— null ๊ฐ’์ด ๋“ค์–ด์˜ค์ง€ ์•Š๋„๋ก ๋งŒ๋“ค์–ด๋ณด์ž.

@Embeddable
class BoardTags(
    tags: MutableList<BoardTag> = Collections.emptyList()
) {

    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "board_id", updatable = false, nullable = false)
    val tags: MutableList<BoardTag> = tags.toMutableList()
		...
}

 

โœ”๏ธ  ์ €์žฅ ํ…Œ์ŠคํŠธ

@Test
@Rollback(false)
fun save() {
    val ํƒœ๊ทธ1 = BoardTag(name = "ํƒœ๊ทธ1")
    val ํƒœ๊ทธ2 = BoardTag(name = "ํƒœ๊ทธ2")
    boardTagRepository.save(ํƒœ๊ทธ1)
    boardTagRepository.save(ํƒœ๊ทธ2)

    val board = Board(
        title = BoardTitle("์ œ๋ชฉ"), content = BoardContent("๋‚ด์šฉ")
    )
    board.addTag(ํƒœ๊ทธ1)
    board.addTag(ํƒœ๊ทธ2)

    boardRepository.save(board)
}

null ๊ฐ’์ด ์ €์žฅ๋˜์ง€ ์•Š์•„ ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ฌ ๊ฒƒ ๊ฐ™๋‹ค๊ณ  ์˜ˆ์ƒํ–ˆ์ง€๋งŒ, ์œ„์™€ ๊ฐ™์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

์ด๋Š”, board_tag์— ํƒœ๊ทธ ์ •๋ณด๊ฐ€ ์ €์žฅํ•˜๋Š” ์‹œ์ ์— board_id์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ๋“ค์–ด๊ฐ€์•ผ ํ•˜๋Š”๋ฐ ํ˜„์žฌ๋Š” board์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ํ•˜๋‚˜๋„ ์—†์œผ๋‹ˆ null ๊ฐ’์ด ๋“ค์–ด๊ฐ€๋ ค๋‹ค๊ฐ€ ์ œ์•ฝ ์กฐ๊ฑด ์œ„๋ฐ˜์œผ๋กœ ์ธํ•ด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์ด๋‹ค.

 


 

๐ŸŒฑ ํƒœ๊ทธ ์ •๋ณด๊ฐ€ ์ €์žฅ๋  ๋•Œ ๊ฒŒ์‹œ๊ธ€ ์ •๋ณด๋„ ํ•จ๊ป˜ ์ €์žฅํ• ๋ž˜! - CascadeType ์ง€์ •ํ•˜๊ธฐ

๊ทธ๋ ‡๋‹ค๋ฉด, board_tag๊ฐ€ ์ €์žฅํ•˜๋Š” ์‹œ์ ์— board์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์•Œ๋ฉด ๋˜๋Š” ๊ฒƒ์ด ์•„๋‹๊นŒ?
ํ•˜์ง€๋งŒ, ์šฐ๋ฆฌ๋Š” board → board_tag๋กœ์˜ ๋‹จ๋ฐฉํ–ฅ ๊ด€๊ณ„๋งŒ ๊ฐ€์ง€๋„๋ก ๋งŒ๋“ค๊ณ  ์‹ถ๊ธฐ ๋•Œ๋ฌธ์— board_tag๊ฐ€ board์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์•Œ์ง€ ์•Š๊ธฐ๋ฅผ ๋ฐ”๋ž€๋‹ค. ์ด๋ฅผ ์œ„ํ•ด์„œ๋Š”, board_tag๋ฅผ ์•Œ๊ณ  ์žˆ๋Š” board๊ฐ€ ์ €์žฅ๋˜๋Š” ์‹œ์ ์— ํƒœ๊ทธ์— ๋Œ€ํ•œ ์ •๋ณด๋„ ํ•จ๊ป˜ ์ €์žฅ๋˜๋„๋ก ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

@Embeddable
class BoardTags(
    tags: MutableList<BoardTag> = Collections.emptyList()
) {

    @OneToMany(fetch = FetchType.LAZY, cascade = [CascadeType.PERSIST])
    @JoinColumn(name = "board_id", updatable = false, nullable = false)
    val tags: MutableList<BoardTag> = tags.toMutableList()

    ...
}

์œ„์™€ ๊ฐ™์ด cascade ์˜ต์…˜์„ ํ™œ์šฉํ•˜์—ฌ PERSIST๋กœ ์ง€์ •ํ•ด์ฃผ์ž.
์ด๋Ÿฌ๋ฉด board๊ฐ€ ์ €์žฅ๋˜๋Š” ์‹œ์ ์— board_tag ์ •๋ณด๋„ ํ•จ๊ป˜ ์•Œ๊ฒŒ ๋œ๋‹ค.

 


โœ”๏ธ  ์ €์žฅ ํ…Œ์ŠคํŠธ

๊ธฐ์กด์˜ ์ €์žฅ ๋กœ์ง์„ ๋ณ€๊ฒฝํ•˜์—ฌ, board๊ฐ€ ์ €์žฅ๋  ๋•Œ board_tag๋„ ํ•จ๊ป˜ ์ง€์ •ํ•˜์—ฌ ์ €์žฅํ•ด๋ณด์ž.

@Test
@Rollback(false)
fun save_casacde() {
    val boardTags = mutableListOf(BoardTag(name = "ํƒœ๊ทธ1"), BoardTag(name = "ํƒœ๊ทธ2"))
    val board = Board(
        title = BoardTitle("์ œ๋ชฉ"), content = BoardContent("๋‚ด์šฉ"),
        boardTags = BoardTags(boardTags)
    )
    boardRepository.save(board)
}

๋จผ์ €, board์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ์ €์žฅ๋˜๋Š” insert ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ , board_id๊ฐ€ 1๋กœ ์ฑ„์›Œ์ง„ board_tag์— ๋Œ€ํ•œ insert ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
์ฆ‰, update ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์œผ๋ฉด์„œ ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š”๋Œ€๋กœ ์ž˜ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

 


 

๐ŸŒฑ @OneToMany ์–‘๋ฐฉํ–ฅ

์ฒ˜์Œ๋ถ€ํ„ฐ ์–‘๋ฐฉํ–ฅ์œผ๋กœ ์„ค์ •ํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ๋ณ„๋„์˜ ์„ค์ • ํ•„์š” ์—†์ด๋„ ์ž˜ ๋™์ž‘ํ•œ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜, ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•œ ์—ฐ๊ด€๊ด€๊ณ„ ํŽธ์˜ ๋ฉ”์„œ๋“œ๊ฐ€ ์กด์žฌํ•ด์•ผ ํ•œ๋‹ค.

@Entity
class Board(
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long = 0L,

    @Embedded
    val title: BoardTitle,

    @Embedded
    val content: BoardContent,

    @Embedded
    var boardTags: BoardTags = BoardTags(Collections.emptyList())
) {

    fun addTag(boardTag: BoardTag) {
        boardTag.board = this // here!
        boardTags.addTag(boardTag)
        boardTags = BoardTags(boardTags.tags)
    }
}

@Embeddable
class BoardTags(
    tags: MutableList<BoardTag> = Collections.emptyList()
) {

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "board")
    val tags: MutableList<BoardTag> = tags.toMutableList()
    
    ...
}

@Entity
class BoardTag(

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long = 0L,

    val name: String,

    @ManyToOne(fetch = FetchType.LAZY)
    var board: Board? = null
) {
	...
}

addTag() ๋ฉ”์„œ๋“œ์—์„œ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์„ธํŒ…ํ•ด์ฃผ๊ธฐ ์œ„ํ•ด boardTag.board = this๋ผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์—ˆ๋‹ค.

 


 

โœ”๏ธ ์ €์žฅ ํ…Œ์ŠคํŠธ

@Test
@Rollback(false)
fun save() {
    val ํƒœ๊ทธ1 = BoardTag(name = "ํƒœ๊ทธ1")
    val ํƒœ๊ทธ2 = BoardTag(name = "ํƒœ๊ทธ2")

    val board = Board(
        title = BoardTitle("์ œ๋ชฉ"), content = BoardContent("๋‚ด์šฉ")
    )
    board.addTag(ํƒœ๊ทธ1)
    board.addTag(ํƒœ๊ทธ2)

    boardRepository.save(board)
    boardTagRepository.save(ํƒœ๊ทธ1)
    boardTagRepository.save(ํƒœ๊ทธ2)
}

์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ์„ค์ •๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ํƒœ๊ทธ๋ฅผ ๋จผ์ € ์ €์žฅํ•˜๋Š” ๋Œ€์‹ ์—, ๊ฒŒ์‹œ๊ธ€์— ๋Œ€ํ•ด์„œ ๋จผ์ € ์ €์žฅํ•ด์ค€๋‹ค.
์ด๋•Œ board.addTag() ์‹œ board์— ๋Œ€ํ•œ ์—ฐ๊ด€๊ด€๊ณ„๋„ ์„ค์ •๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ boardTag๊ฐ€ ์ €์žฅ๋˜๋Š” ์‹œ์ ์— board์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์•Œ๊ฒŒ ๋˜์–ด insert ์‹œ ์—…๋ฐ์ดํŠธ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.

์‹ค์ œ๋กœ ์ฟผ๋ฆฌ ์ •๋ณด๋ฅผ ๋ณด๋ฉด, ๋จผ์ € ๊ฒŒ์‹œ๊ธ€์— ๋Œ€ํ•œ insert ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ , board_tag์— ๋Œ€ํ•ด์„œ insert ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ๋„ board_id์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ์ž˜ ์ฑ„์›Œ์ ธ์„œ ์‚ฝ์ž…๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 


 

โœ”๏ธ ์‚ญ์ œ ํ…Œ์ŠคํŠธ

@Test
@Rollback(false)
fun delete_bidirection() {
    // ์ €์žฅ ๋กœ์ง
    ...

    // ์กฐํšŒ -> ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ๋ฐœ์ƒ
    val savedBoard = boardRepository.findById(1L).get()

    // ๋ณด๋“œ์— ์กด์žฌํ•˜๋Š” ๋ณด๋“œ ํƒœ๊ทธ ์ œ๊ฑฐ
    savedBoard.boardTags.tags.removeAt(0)

    // ๋ณด๋“œ ํƒœ๊ทธ ์ œ๊ฑฐ
    boardTagRepository.deleteById(1L)
}

์ด๋•Œ๋„ ๋‹จ์ˆœํžˆ board_tag์— ๋Œ€ํ•œ delete ์ฟผ๋ฆฌ ํ•œ ๋ฒˆ๋งŒ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค.
tags.removeAt(0)์„ ์ง„ํ–‰ํ•˜๋”๋ผ๋„ ์™ธ๋ž˜ํ‚ค ์ œ์•ฝ์กฐ๊ฑด์œผ๋กœ ์ธํ•ด์„œ update ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๊ณ  ๋ฐ”๋กœ ์—”ํ‹ฐํ‹ฐ์— ๋Œ€ํ•œ delete๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค.

 


 

๐Ÿ’ก ๊ฒฐ๋ก 

- @OneToMany ๋‹จ๋ฐฉํ–ฅ ๊ด€๊ณ„์—์„œ๋Š” ๋ถˆํ•„์š”ํ•œ update ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
- ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” nullable = false, updatable = false๋ฅผ ์ง€์ •ํ•ด์ฃผ๊ณ  ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์˜์†ํ™”๋  ๋•Œ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ์˜์†ํ™”๋  ์ˆ˜ ์žˆ๋„๋ก PERSIST ์˜ต์…˜์„ ์ง€์ •ํ•˜์ž.
- ๊ทธ๋Ÿฐ ํ•˜์œ„ ๊ด€๊ณ„๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋ฅผ ๊ณ ๋ คํ•˜์ž.

์ผ๋Œ€๋‹ค ๋‹จ๋ฐฉํ–ฅ์€ ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ์™€ ์ž์‹ ์—”ํ‹ฐํ‹ฐ์˜ ์ƒ๋ช…์ฃผ๊ธฐ๊ฐ€ ๊ฐ™์„ ๊ฒฝ์šฐ๋งŒ ์‚ฌ์šฉํ•˜๋„๋ก ํ•˜์ž! (์™„์ „ํ•œ ํ•˜์œ„ ๊ด€๊ณ„์ผ ๋•Œ)

๊ทธ๊ฒŒ ์•„๋‹ˆ๋ผ๋ฉด ์–‘๋ฐฉํ–ฅ์œผ๋กœ ์„ค์ •ํ•˜๊ฑฐ๋‚˜, ์•„๋‹ˆ๋ฉด ๊ทธ๋ƒฅ ์—”ํ‹ฐํ‹ฐ๋ผ๋ฆฌ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๊ฐ€์ง€๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ๋‹จ์ˆœํžˆ id ๊ฐ’์„ ํ•„๋“œ๋กœ ๊ฐ€์ง€๋„๋ก ๋งŒ๋“ค ๊ฒƒ ๊ฐ™๋‹ค.

Comments