개발
home
🚚

Kotlin Spring. Abstract Class의 Entity 시 getter final 이슈

Created
2022/08/16
Tags
SpringBoot
Kotlin
trouble shooting
2022-08-16 @이영훈
Kotlin Spring에서 abstract class의 Entity를 설정하면 다음과 같은 에러를 만납니다.
org.hibernate.HibernateException: Getter methods of lazy classes cannot be final: me.leedo.application.work.domain.Item#getId
위 에러를 알아보고 이슈를 해결한 과정을 기록으로 남깁니다.

문제 상황

다음과 같이 상속 전략을 SINGLE_TABLE로 하는 부모 클래스를 abstract class로 생성합니다.
@Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "dtype") abstract class Item( @Id @Column(name = "item_id") @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long? = null, val name: String, )
Kotlin
복사
Spring을 실행하면 다음과 같이 에러가 나타납니다
org.hibernate.HibernateException: Getter methods of lazy classes cannot be final: me.leedo.application.work.domain.Item#getId
hibernate에서 lazy 클래스의 getter 메소드는 final일 수 없다라는 에러입니다.

에러 해결 과정

abstract class인 Item의 모든 맴버 변수를 open으로 열면 됩니다. Kotlin에서는 모든 클래스와 맴버 변수는 기본적으로 final 입니다.
변수를 open으로 열어도 되겠지만 저는 @AllOpen 이라는 커스텀 어노테이션과 AllOpen Plugin을 사용해서 해결했습니다.
다음과 같이 AllOpen 어노테이션을 생성합니다.
package me.leedo.application.shared @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.RUNTIME) annotation class AllOpen
Kotlin
복사
build.gradle.kts 파일에서 allOpen을 설정합니다
plugins { val kotlinVersion = "1.7.10" // plugin.spring은 allOpen 플러그인과 스프링과 통합하기 위해 allOpen 플러그인에 추가적인 설정을 넣은 플러그인 입니다 kotlin("plugin.spring") version kotlinVersion } // 위에서 생성한 AllOpen 어노테이션을 추가합니다 allOpen { annotation("me.leedo.application.shared.AllOpen") }
Kotlin
복사
Item 클래스에서 @AllOpen 어노테이션을 붙입니다
@Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "dtype") @AllOpen abstract class Item( @Id @Column(name = "item_id") @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long? = null, val name: String, )
Kotlin
복사
kotlin 바이트코드를 decompile 해보면 46번째 줄과 같이 final이 없어진 것을 확인할 수 있습니다.
그리고 스프링을 실행해도 에러가 뜨지 않습니다
 여기까지만해도 문제가 해결이 됩니다.
저는 실무 코드에서는 다음과 같이 해결하였습니다.

BaseEntity에 @AllOpen 설정

보통 실무에서는 BaseTimeEntity나 BaseEntity 클래스를 두고 상속 받아서 많이 구현합니다.
BaseTimeEntity@AllOpen 을 적용하고 이것을 상속받아서 해결하였습니다.
다음과 같이 BaseTimeEntity 클래스가 있습니다.
@MappedSuperclass @EntityListeners(AuditingEntityListener::class) @AllOpen // @AllOpen 어노테이션을 적용하였습니다 abstract class BaseTimeEntity { lateinit var createdAt: OffsetDateTime lateinit var updatedAt: OffsetDateTime @PrePersist fun prePersist() { createdAt = OffsetDateTime.now() updatedAt = OffsetDateTime.now() } @PreUpdate fun preUpdate() { updatedAt = OffsetDateTime.now() } }
Kotlin
복사
Item 클래스를 BaseTimeEntity 클래스를 상속받아 구현합니다
@Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "dtype") abstract class Item( @Id @Column(name = "item_id") @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long? = null, val name: String, ) : BaseTimeEntity()
Kotlin
복사
Kotlin 파일을 decompile해서 확인해보면 getter 메소드 모두 open 되어 있음을 확인할 수 있습니다.

Reference