728x90
발단
OneToOne 관계를 맺은 테이블에서 Lazy Initialization 을 의도하였으나 의도한 바대로 동작하지 않고 FetchType.Eager 처럼 동작하여 이를 해결하기 위해 적용한 bytecode enhancement plugin 이 찾을 수 없다는 이유로 적용되지 않는 이슈를 해결하는 과정입니다.
환경
- Spring Boot 3.2.5
- data-jpa-starter
- hibernate-core 6.4.4.Final
OneToOne 사용시 FetchType.Lazy 가 의도대로 동작하지 않는 이유
동작하지 않는 이유에 대해 자세히 설명하는 글이 있어 참조하시면 더 정확한 이유를 아실 수 있을거라 생각합니다.
간단히 말해서 OneToOne 의 관계를 가지는 member 와 memebr_detail 테이블이 있다고 가정하겠습니다.
여기서 member_detail 에 외래키가 존재합니다. 이 때 member_detail 은 외래키를 통해 member 의 존재를 알고 있지만 member 는 member_detail 존재를 알지 못합니다.
여기서 정보의 간극이 발생하고 hibernate 정책에 따라 member 조회시 member_detail 에 대한 프록시를 생성하게 되는데 정보를 알기 위한 쿼리가 발생하여 FetchType.Lazy 가 적용되지 못하게 되는 겁니다.
문제 발생(build.gradle.kts)
plugins {
id("org.springframework.boot") version "3.2.5"
..
id("org.hibernate.orm") version "6.4.4.Final" // bytecode enhancement
kotlin("jvm") version "1.9.23"
..
}
- 위와 같이 plugin 을 kotlin("jvm").. 위에 선언하였지만 제목에서 언급한 오류가 발생하였습니다.
- org.hibernate.orm 플러그인 버전이 맞지 않아서 인가 확인해보았지만 제가 사용중인 spring boot 의 jpa 버전에서는 6.4.4.Final 버전이 적용되어 있는것을 확인할 수 있었습니다.
순서가 문제였다
plugins {
id("org.springframework.boot") version "3.2.5"
id("io.spring.dependency-management") version "1.1.4"
kotlin("jvm") version "1.9.23"
..
id("org.hibernate.orm") version "6.4.4.Final" // bytecode enhancement
}
- 위 코드에서 볼 수 있듯이 plugins 블럭의 가장 하위에 위치하니 정상적으로 동작하였습니다.
- 정확히는 kotlin("jvm") .. 라인 하위에 있어야 plugin 을 적용할 수 있습니다.
- plugins 블록 내부에서도 각각의 의존성이 그 순서에 맞게 배치되어있어 아무래도 org.hibernate.orm 의 경우 jvm 에 의존하고 있는 것으로 볼 수 있을거 같습니다.
설정
build.kotlin.kts
plugins {
id("org.springframework.boot") version "3.2.5"
..
kotlin("jvm") version "1.9.23"
..
id("org.hibernate.orm") version "6.4.4.Final" // bytecode enhancement
}
// OneToOne 에서 lazy loading 을 지원하기 위한 설정입니다.
hibernate {
// 블럭이 비었지만 이렇게라도 해놔야 설정된겁니다.
enhancement {
// 아래 4가지 요소가 설정된 겁니다.
// enableLazyInitialization, enableDirtyTracking, enableAssociationManagement, enableExtendedEnhancement
}
}
- 위 링크는 hibernate 문서로 kotlin 이 아닌 java 에 대한 내용이지만 두 언어는 완전 호환되기에 무관하게 참조하였습니다.
- 내용을 보면 hibernate.enhancement 를 재정의 하여 원하는 속성에 대해 값을 할당해주는 것을 볼 수 있습니다.
- 그런데, enableLazyinitialzation 속성을 포함한 EnhancementSpec 클래스를 보면 setter 메서드가 deprecated 되어 있는 것을 알 수 있습니다.
- 개별 조절을 하려면 argument 로 제공한다 명시되어 있어 세부 조정이 필요하지 않다면 위와 같이 작성하면 설정이 적용된 것 입니다.
Member, MemberDetailEntity
// Member 테이블
@Entity
@Table(name = "member")
class MemberEntity (
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "idx")
var idx: Long = 0,
..
) {
@OneToOne(mappedBy = "memberEntity", fetch = FetchType.LAZY)
lateinit var memberDetailEntity: MemberDetailEntity
}
// Member Detail 테이블
@Entity
@Table(name = "member_entity")
class MemberDetailEntity (
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "idx")
var idx: Long = 0,
@Column(name = "member_idx")
var memberIdx: Int,
) {
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_idx", insertable = false, updatable = false)
lateinit var memberEntity: MemberEntity
}
SQL 로그 확인
// bytecode enhancement 적용 X
Hibernate:
select
me1_0.idx,
..
from
member me1_0
where
me1_0.idx=?
Hibernate:
select
mde1_0.idx,
mde1_0.member_idx,
from
member_detail mde1_0
where
mde1_0.member_idx=?
// bytecode enhancement 적용 O
Hibernate:
select
me1_0.idx,
..
from
member me1_0
where
me1_0.idx=?
728x90
'Spring Boot > JPA' 카테고리의 다른 글
[JPA] 테이블간의 연관관계 연결 실패(feat. cannot be cast to java.io.Serializable) (0) | 2024.06.11 |
---|---|
[JPA] 연관관계를 맺은 객체에 대해 지연 로딩이 동작하지 않는다면(feat. kotlin, hibernate, lazy-loading) (0) | 2023.11.09 |