๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Trouble Shooting

LazyInitializationException: could not initialize proxy - no Session Error

by ์ฃผ๋ฐœ2 2022. 6. 1.
๋ฐ˜์‘ํ˜•

๐Ÿ“Ž org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: ... entity.Folder.children could not initialize proxy - no Session

๊ฐœ์ธ์ ์œผ๋กœ ์ง„ํ–‰์ค‘์ธ ํ”„๋กœ์ ํŠธ์—์„œ JPA ์—ฐ๊ด€๊ด€๊ณ„ ํ…Œ์ด๋ธ”์˜ ์กฐํšŒ & ์‚ญ์ œ ํ•˜๋Š” ๊ณผ์ •์—์„œ ์œ„์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ด€๋ จ๋œ ์ฝ”๋“œ๋ฅผ ๊ฐ„๋‹จํžˆ ๋‚˜ํƒ€๋‚ด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

(ํฌ์ŠคํŒ…์—์„œ ํ‹€๋ฆฐ ๋‚ด์šฉ์ด ์žˆ๋‹ค๋ฉด ํ”ผ๋“œ๋ฐฑ ์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๐Ÿ™Œ)

 

 

Folder ์—”ํ‹ฐํ‹ฐ

@Entity
class Folder(
    ...
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id")
    @JsonBackReference
    var parentFolder: Folder?,
) : BaseTimeEntity() {

    @OrderBy("index asc")
    @OneToMany(mappedBy = "parentFolder", cascade = [CascadeType.ALL])
    @JsonManagedReference
    var children: MutableList<Folder>? = mutableListOf()

    @OneToMany(mappedBy = "folder", cascade = [CascadeType.ALL])
    var folders: MutableList<AccountFolder>? = mutableListOf()
    
    ...

Entity๋Š” ์œ„์™€ ๊ฐ™์ด ์ž์‹ ์„ ์ˆœํ™˜ ์ฐธ์กฐ๋กœ ๋ถ€๋ชจ ๋ฐ ์ž์‹์„ ๊ฐ€์ง€๋Š” ํ˜•์‹์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋Š”๋ฐ์š”, ๋Œ€๋Œ“๊ธ€๊ณผ ๋™์ผํ•œ ๊ตฌ์กฐ๋ผ๊ณ  ์ƒ๊ฐํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

 

Folder Conrtoller & Service

// Controller
@ApiOperation(value = "ํด๋” ์‚ญ์ œ(์‚ญ์ œํ•˜๋ ค๋Š” ํด๋”์™€ ํ•˜์œ„์— ์กด์žฌํ•˜๋Š” ๋ชจ๋“  ํด๋” ๋ฐ ๋ถ๋งˆํฌ๋“ค ์‚ญ์ œ) API")
@DeleteMapping("/{folderId}")
fun deleteAllBookmarkAndAllFolderWithRelatedFolder(
    @PathVariable @ApiParam(value = "ํด๋” ID", example = "2", required = true) folderId: Long
): ResponseEntity<String> {

    // ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๋ถ€๋ถ„!
    val folder = folderService.findByFolderId(folderId)
    folderService.deleteFolderRecursive(folder)
    folderService.deleteFolder(folder)
    return ResponseEntity.status(HttpStatus.OK).body(Message.SUCCESS)
}


// Service
fun deleteFolder(folder: Folder) {
    folderRepository.deleteByFolder(folder)
}

fun deleteFolderRecursive(folder: Folder) {
    // Base Condition: ์ตœํ•˜์œ„ Depth์˜ ํด๋”
    val children: MutableList<Folder> = folder.children ?: return

    children.let { folderList ->
        folderList.stream().forEach { folder ->
            deleteFolderRecursive(folder)
        }
    }

    bookmarkRepository.findByFolderId(folder.id!!)
        .let { list ->
            list.forEach {
                it.deletedByFolder()
                it.remindOff()
                bookmarkRepository.save(it)
            }
        }

    folderRepository.deleteByFolder(folder)
}

์ปจํŠธ๋กค๋Ÿฌ ๋ฐ ์„œ๋น„์Šค์˜ ์ฝ”๋“œ๋Š” ์œ„์™€ ๊ฐ™์€๋ฐ์š”, ํด๋”๋ฅผ ์‚ญ์ œํ•  ์‹œ ์ž์‹์˜ ๋ชจ๋“  ํด๋” ๋ฐ ํด๋”์— ์กด์žฌํ•˜๋Š” ๋ถ๋งˆํฌ๋“ค๋„ ์‚ญ์ œ๊ฐ€ ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


๋˜ํ•œ ์Šคํ”„๋ง ํŠธ๋žœ์žญ์…˜์˜ Self Invocation ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์˜€๊ธฐ์— ๋‘ ๊ฐœ์˜ ์„œ๋น„์Šค ๋ฉ”์„œ๋“œ๋ฅผ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ํ˜ธ์ถœํ•˜๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

 

Spring Transactional self invocation

 

์‹ค์ œ ๋””๋ฒ„๊น…์„ ํ•ด๋ณด๋ฉด ์•„๋ž˜ ์ฝ”๋“œ์—์„œ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์กฐํšŒํ•  ๋•Œ ์ด๋ฏธ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

val folder = folderService.findByFolderId(folderId)

Unable to evaluate the expression Method threw 'org.hibernate.LazyInitializationException' ...

์œ„ ๋ฌธ์ œ๋Š” ์ง€์—ฐ ๋กœ๋”ฉ์—์„œ ํ˜„์žฌ ์„ธ์…˜์ด ์—†๊ธฐ์— ๋ฐœ์ƒํ•˜๋Š” ์˜ค๋ฅ˜์ธ๋ฐ์š”, ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” JPA์™€ OSIV(Open Session In View)๋ผ๋Š” ๊ฐœ๋…์— ๋Œ€ํ•ด ์•Œ์•„์•ผ ํ•˜๊ธฐ์— ๊ฐ„๋žตํžˆ ์„ค๋ช…์„ ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

 

 

JPA ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ

JPA์—์„œ๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ผ๋Š” ๊ฐœ๋…์ด ์กด์žฌํ•˜๋Š”๋ฐ์š”, ์ด๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜๊ตฌ ์ €์žฅํ•˜๋Š” ํ™˜๊ฒฝ ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

 

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‚ฌ์ด์—์„œ ๊ฐ์ฒด๋ฅผ ๋ณด๊ด€ํ•˜๋Š” ๊ฐ€์ƒ์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ™์€ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

์ฃผ๋กœ ์‚ฌ์šฉํ•˜๋Š” JPA์˜ ๊ฒฝ์šฐ ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•˜๋Š” ์ˆœ๊ฐ„ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ƒˆ๋กœ ์ €์žฅ๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜ํ•˜๋Š”๋ฐ, ์ด๋ฅผ flush๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

 

Entity์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋กœ ๋น„์˜์†, ์˜์†, ์ค€์˜์† ๋“ฑ์ด ์กด์žฌํ•˜๋Š”๋ฐ์š” ๊ฐ„๋žตํžˆ ๋ง์”€๋“œ๋ฆฌ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ๋น„์˜์†(new/transient): ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ ์ „ํ˜€ ๊ด€๊ณ„๊ฐ€ ์—†๋Š” ์ƒํƒœ, Entity ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์—ฌ๋„ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅํ•˜์ง€ ์•Š์€ ์ƒํƒœ
  • ์˜์†(managed): ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅ๋œ ์ƒํƒœ, Entity๊ฐ€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์˜ํ•ด ๊ด€๋ฆฌ๋จ
  • ์ค€์˜์†(detached): ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅ๋œ ํ›„ ๋ถ„๋ฆฌ๋œ ์ƒํƒœ

์ค€์˜์† ๊ฒฝ์šฐ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅ๋œ ๊ฒฝํ—˜์ด ์กด์žฌํ•˜๊ธฐ์— ๋น„์˜์†๊ณผ๋Š” ๋‹ฌ๋ฆฌ ์‹๋ณ„์ž๊ฐ€ ์กด์žฌํ•œ๋‹ค๋Š” ๊ฒƒ์ด ๋ณด์žฅ์ด ๋ฉ๋‹ˆ๋‹ค.

 

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” 1์ฐจ ์บ์‹œ, ๋™์ผ์„ฑ ๋ณด์žฅ, ์ง€์—ฐ ๋กœ๋”ฉ, ๋ณ€๊ฒฝ ๊ฐ์ง€(Dirty Checking) ๋“ฑ์˜ ํŠน์ง•์ด ์žˆ๋Š”๋ฐ์š”, ์—ฌ๊ธฐ์„œ๋Š” ๋”ฐ๋กœ ์„ค๋ช…ํ•˜์ง€๋Š” ์•Š๊ฒ ์Šต๋‹ˆ๋‹ค ^^; ๊ด€๋ จํ•˜์—ฌ ๊ถ๊ธˆํ•˜์‹œ๋ฉด ์•„๋ž˜ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”. ๐Ÿ˜€

 

 

ํŠธ๋žœ์žญ์…˜ ๋ฒ”์œ„ ๋ฐ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ

์Šคํ”„๋ง JPA์˜ ๊ฒฝ์šฐ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ „๋žต์„ ๋”ฐ๋ฅด๊ณ  ์žˆ๋Š”๋ฐ์š”, ๊ธฐ๋ณธ ์ „๋žต์œผ๋กœ ํŠธ๋žœ์žญ์…˜ ๋ฒ”์œ„์˜ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ์ „๋žต์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํŠธ๋žœ์žญ์…˜์ด ์‹œ์ž‘ํ•˜๋Š” ์ˆœ๊ฐ„ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋„ ์ƒ์„ฑ๋˜๊ณ , ํŠธ๋žœ์žญ์…˜์ด ๋๋‚˜๋Š” ์ˆœ๊ฐ„ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ์ข…๋ฃŒ๋˜๋Š” ์ „๋žต ์ž…๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ํŠธ๋žœ์žญ์…˜์ด ๋™์ผํ•  ๊ฒฝ์šฐ ๋™์ผํ•œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

// Service
@Autowired val repo1: Repository1
@Autowired val repo2: Repository2

@Transactional
fun logic() {
    repo1.save();
    repo2.save();
}

์œ„์™€ ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ์žˆ์„๋•Œ logic() ๋ฉ”์„œ๋“œ์—์„œ ํŠธ๋žœ์žญ์…˜์ด ์„ค์ •๋˜์–ด์žˆ๊ธฐ ๋•Œ๋ฌธ์— repo1๊ณผ repo2๋Š” ๋™์ผํ•œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

 

OSIV(Open Session In View)

OSIV(Open Session In View)๋ž€ JPA์—์„œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ Hibernate Session์„ View๊ฐ€ ๋ Œ๋”๋ง์ด ๋  ๋•Œ ๊นŒ์ง€ ์œ ์ง€์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

์Šคํ”„๋ง์˜ ๊ฒฝ์šฐ ๋ณดํ†ต ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง(Service Layer)์—์„œ ํŠธ๋žœ์žญ์…˜์„ ์„ค์ •ํ•˜๊ณ (@Transactional) ์ด ํŠธ๋žœ์žญ์…˜์ด ์œ ์ง€๊ฐ€ ๋˜๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ๋ฒ”์œ„๋Š” ๋ทฐ ๋ Œ๋”๋ง๊นŒ์ง€๋Š” ๋˜์ง€๋Š” ์•Š๋Š”๋ฐ์š”, ๋”ฐ๋ผ์„œ ์œ„์—์ฒ˜๋Ÿผ Controller์—์„œ Entity๋ฅผ ์กฐํšŒํ•  ๋•Œ Lazy loading์ด ๋˜์–ด์žˆ๋‹ค๋ฉด LazyInitializationException์˜ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.(ํ˜„์žฌ ๋ฐœ์ƒํ•˜๊ณ  ์žˆ๋Š” ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.)

@Entity
class Folder(
    ...
    
    @ManyToOne(fetch = FetchType.Lazy)
    @JoinColumn(name = "parent_id")
    @JsonBackReference
    var parentFolder: Folder?,  // LazyInitializationException
) : BaseTimeEntity() {

    @Column
    var emoji: String? = ""

    @OrderBy("index asc")
    @OneToMany(mappedBy = "parentFolder", cascade = [CascadeType.ALL])
    @JsonManagedReference
    var children: MutableList<Folder>? = mutableListOf()
    
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "folder", cascade = [CascadeType.ALL])
    var folders: MutableList<AccountFolder>? = mutableListOf()
    ...


// Controller
@ApiOperation(value = "ํด๋” ์‚ญ์ œ(์‚ญ์ œํ•˜๋ ค๋Š” ํด๋”์™€ ํ•˜์œ„์— ์กด์žฌํ•˜๋Š” ๋ชจ๋“  ํด๋” ๋ฐ ๋ถ๋งˆํฌ๋“ค ์‚ญ์ œ) API")
@DeleteMapping("/{folderId}")
fun deleteAllBookmarkAndAllFolderWithRelatedFolder(
    @PathVariable @ApiParam(value = "ํด๋” ID", example = "2", required = true) folderId: Long
): ResponseEntity<String> {
    val folder = folderService.findByFolderId(folderId)
    folderService.deleteFolderRecursive(folder)
    folderService.deleteFolder(folder)
    return ResponseEntity.status(HttpStatus.OK).body(Message.SUCCESS)
}

 

 

OSIV = false(default) ์ผ ๋•Œ

// application.properties
spring.jpa.open-in-view=false

https://velog.io/@yebali/Spring-OSIV%EC%99%80-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94

 

 

OSIV = true ์ผ ๋•Œ

// application.properties
spring.jpa.open-in-view=true

https://velog.io/@yebali/Spring-OSIV%EC%99%80-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94

 

 

LazyInitializationException ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

์œ„ ์—๋Ÿฌ์™€ ๊ด€๋ จํ•˜์—ฌ ์—ฌ๋Ÿฌ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•์ด ์กด์žฌํ•˜๋Š”๋ฐ์š”, ์–ด๋– ํ•œ ๋ฐฉ๋ฒ•์ด Best Practice์ธ์ง€๋Š” ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค๋ฅผ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

 

1. Entity์˜ FetchType์„ Lazy -> Eager๋กœ ์ˆ˜์ •

์œ„์—์„œ ์—ฐ๊ด€๊ด€๊ณ„๋กœ ์„ค์ •๋˜์–ด ์žˆ๋Š” Entity์—์„œ Lazy๋ฅผ Eager๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ ๋กœ JPA์—์„œ default Fetch ์ „๋žต์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • OneToMany: Lazy
  • ManyToOne: Eager
  • ManyToMany: Lazy
  • OneToOne: Eager
@Entity
class Folder(
    ...
    
    @ManyToOne
    @JoinColumn(name = "parent_id")
    @JsonBackReference
    var parentFolder: Folder?,  // LazyInitializationException
) : BaseTimeEntity() {

    @Column
    var emoji: String? = ""

    // FetchType์„ Lazy์—์„œ Eager๋กœ ๋ณ€๊ฒฝ
    @OrderBy("index asc")
    @OneToMany(fetch = FetchType.Eager, mappedBy = "parentFolder", cascade = [CascadeType.ALL])
    @JsonManagedReference
    var children: MutableList<Folder>? = mutableListOf()
    
    // FetchType์„ Lazy์—์„œ Eager๋กœ ๋ณ€๊ฒฝ    
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "folder", cascade = [CascadeType.ALL])
    var folders: MutableList<AccountFolder>? = mutableListOf()
    ...

~ToOne์˜ ๊ฒฝ์šฐ deafult๋กœ Eager์ด๊ธฐ์— ~ToMany๋งŒ Lazy์—์„œ Eager๋กœ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

 

๊ทธ ํ›„ ๋””๋ฒ„๊น…์„ ํ†ตํ•ด ๊ฐ’์„ ํ™•์ธํ•ด๋ณด๋ฉด ์œ„์—์„œ ๋ฐœ์ƒํ–ˆ๋˜ LazyInitializationException ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Lazy์—์„œ Eager๋กœ ๋ณ€๊ฒฝํ•˜์˜€๊ธฐ์—, ์—ฐ๊ด€ ๊ด€๊ณ„ ์„ค์ •์ด ๋˜์–ด์žˆ๋Š” Entity๊นŒ์ง€ ๋ชจ๋‘ ๊ฐ€์ ธ์˜จ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

2. OSIV = true๋กœ ์„ค์ •

์œ„์—์„œ OSIV์— ๋Œ€ํ•ด ๊ฐ„๋žตํžˆ ๋ง์”€๋“œ๋ ธ๋Š”๋ฐ์š”, OSIV๋ฅผ true๋กœ ์„ค์ •ํ•˜์—ฌ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ Hibernate Session์„ View๊ฐ€ ๋ Œ๋”๋ง์ด ๋  ๋•Œ ๊นŒ์ง€ ์œ ์ง€์‹œํ‚ค๋ฉด Controller์—์„œ Entity๋ฅผ ์กฐํšŒํ•  ๋•Œ Lazy Loading์ด๋”๋ผ๋„ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

// application.properties
spring.jpa.open-in-view=true

OSIV๋ฅผ true๋กœ ์„ค์ •ํ•œ ํ›„ ํ™•์ธํ•ด๋ณด๋ฉด ์˜ˆ์™ธ ์—†์ด ์ž‘๋™ํ•˜๋Š”๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ OSIV์˜ ๊ฒฝ์šฐ Anti-Pattern์ธ ๋“ฏ ํ•˜๋ฉฐ ์„ฑ๋Šฅ์ ์ธ ๋ฌธ์ œ์—๋„ ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ OSIV=true๋Š” ์–ด๋“œ๋ฏผ์ด๋‚˜ ๋‚ด๋ถ€ ์ „์šฉ ์‚ฌ์ดํŠธ ๋“ฑ ์ค‘์š”๋„๊ฐ€ ๋–จ์–ด์ง€๋Š” ํ”„๋กœ์ ํŠธ์— ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ถ”๊ฐ€์ ์ธ ๋‚ด์šฉ์€ ์•„๋ž˜ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

 

 

3. Controller์—์„œ Lazy Loading์˜ Entity๋ฅผ ์กฐํšŒํ•˜์ง€ ์•Š๊ธฐ

๊ฐœ์ธ์ ์ธ ์ƒ๊ฐ์œผ๋กœ๋Š” ๊ฐ€์žฅ ์ด์ƒ์ ์ธ ๋ฐฉ๋ฒ•์ธ ๋“ฏ ํ•ฉ๋‹ˆ๋‹ค. ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ์ง€์—ฐ๋กœ๋”ฉ์„ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํŠธ๋žœ์žญ์…˜์ด ์„ค์ •๋˜์–ด ์žˆ๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋‚ด์—์„œ Entity๋ฅผ ์กฐํšŒํ•˜๋ฉด ์œ„์™€ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

    // Service
    fun deleteFolderWithChild(folderId: Long) {
        val folder = folderRepository.findById(folderId).orElseThrow { folderNotFoundException }
        folderRepository.deleteByFolder(folder)

        val children: MutableList<Folder> = folder.children ?: return
        
        ...
        
    }

์œ„ ์ฒ˜๋Ÿผ Controller๊ฐ€ ์•„๋‹Œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ์ž‘์„ฑ๋˜์–ด ์žˆ๋Š” Service์—์„œ Entity๋ฅผ ์กฐํšŒํ•œ ํ›„ ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๋ฉด ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘์ด ๋ฉ๋‹ˆ๋‹ค.

 

 

4. Use JOIN FETCH in JPQL

์œ„ ๋ฐฉ๋ฒ•์€ ์ง์ ‘ ํ•ด๋ณด์ง€๋Š” ์•Š์•˜์œผ๋‚˜, JPQL์—์„œ JOIN FETCH๋ฅผ ํ†ตํ•ด์„œ๋„ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

SELECT f FROM Folder f
	JOIN FETCH f.parentFolder
    JOIN FETCH f.children
    JOIN FETCH f.folders

+ ์œ„์™€ ๊ฐ™์ด JOIN FETCH๋ฅผ ์—ฌ๋Ÿฌ๊ฐœ ์‚ฌ์šฉํ• ๊ฒฝ์šฐ ์•„๋ž˜์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ์š”, ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฐœ์ƒํ• ๊ฒฝ์šฐ List๋ฅผ Set์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ๋ฉด ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

"MultipleBagFetchException: cannot simultaneously fetch multiple bags is there a workaround"

 

 

 

๊ฒฐ๋ก 

ํ•ด๋‹น ๋ฌธ์ œ๋กœ ๊ตฌ๊ธ€๋ง์„ ํ•ด๋ณด๋ฉด ๋Œ€๋ถ€๋ถ„ @Transactional ์„ค์ •, Fetch๋ฅผ Eager๋กœ ๋ณ€๊ฒฝ ์˜ ๋ฐฉ๋ฒ•์ด ๋Œ€๋ถ€๋ถ„์ด์—ˆ๋Š”๋ฐ์š”, ์ด๋ฒˆ ๊ธฐํšŒ์— ์ข€ ๋” ์ฐพ์•„๋ณด๋ฉด์„œ ํ—ท๊ฐˆ๋ ธ๋˜ OSIV์˜ ๊ฐœ๋…์ด๋‚˜ ์ž์ฃผ ๊นŒ๋จน๋Š” JPA์˜ Fetch default ์ „๋žต ๋“ฑ ๋‹ค์‹œ ํ•œ๋ฒˆ ํ•™์Šตํ•  ์ˆ˜ ์žˆ์–ด์„œ ์ข‹์•˜์Šต๋‹ˆ๋‹ค.

 

ํ˜น์‹œ ์œ„์™€ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์•„๋‹๊ฒฝ์šฐ ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ์ง€์—ฐ๋กœ๋”ฉ์„ ํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•ด์ฃผ์‹œ๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

 

 

์ฐธ๊ณ  ๋ฌธ์„œ

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€