Kotlin + JPA (1)

Kwon Donghyun·2024년 1월 31일
0

Kotlin

data class

Kotlin에서 제공하는 class

  • 기본으로 equals() / hashCode(), toString, copy() 같은 메서드들을 제공한다.
  • copy() 메서드는 얕은 복사만 지원하기 때문에 data class는 불변 클래스로 사용하는 것이 권장된다.
  • 순수하게 데이터만 관리하는 클래스로 open 키워드를 통한 상속을 지원하지 않는다.

JPA에서는 data class를 쓰는 것은 추천하지 않는다.

관련 자세한 내용을 다룬 : 블로그

JPA

  • 자바 ↔ db 연동을 위해 사용하는 기술
  • 코틀린은 자바와 100% 호환되는 특성이 있어 JPA를 사용 가능
    • JPA는 자바에 맞춰져 있기 때문에 코틀린에 적합하게 쓰기 위해서는 추가적으로 설정이 필요하다.
      (JPA를 자바 방식에 맞춰 사용했던 것일 수도 있다.)

Hibernate - JPA 요구사항

기존 Spring에서 사용하던 JPA 방식과 Kotlin에서 사용하는 방식이 다르기 때문에 Hibernate(JPA)의 요구사항에 대해 정확하게 알 필요가 있다.

정확한 내용은 Hibernate 문서를 참고하시기 바랍니다.

2.5. Entity types
2.5.1. POJO Models
2.5.2. Prefer non-final classes
2.5.3. Implement a no-argumeent constructor
2.5.4. Declare getters and setter for persistent attributes
2.5.5. Providing identifier attribute(s)
2.5.6. Mapping the entity
2.5.7. Implementing equals() and hashCode()
2.5.8. Mapping the entity to a SQL query
2.5.9. Define a custom entity proxy
2.5.10. Dynamic entity proxies using the @Tuplizer annotation
2.5.11. Define a custom entity persister
2.5.12. Access strategies

  • Entity 클래스는 매개변수가 없는 public 또는 protected 생성자를 가져야 하며, 추가 생성자를 정의할 수 있어야 한다.
  • Entity 클래스는 final이 아니어야만 한다.

Enity 클래스는 매개변수가 없는 public 또는 protected 생성자를 가져야 한다.

  • 코틀린은 클래스에 프로퍼티를 선언하면서 자동으로 생성되는 기본 생성자를 사용한다.
  • 추가적인 생성자는 반드시 기본 생성자를 상속해야하는 코틀린 문법 특징이 있다.
    • 코틀린에서 매개변수가 없는 생성자를 작성하는 것이 굉장히 번거롭다.

Gradle or Maven 에서 추가 플러그인 지정으로 @Entity, @Embeddable, @MappedSuperclass 어노테이션이 붙은 모든 클래스에 자동으로 매개변수가 없는 생성자를 만들 수 있다.

// Gradle(Groovy)

plugins {
		id 'org.jetbrains.kotlin.plugin.jpa' version '{Kotlin버전}'
}

// Gradle(Kotlin)

plugins {
    kotlin("plugin.jpa") version "{Kotlin버전}"
}
  • jpa 플러그인을 통해 자동으로 생성된 매개변수 없는 생성자는 public 생성자이다.
  • 자바나 코틀린 코드에서 직접 호출하는 것은 불가능하고, 리플렉션 통해서만 호출할 수 있기 때문에 외부 접근 허용하지 않는 생성자를 만들 수 있다.

Entity 클래스는 final이 아니어야 한다.

코틀린의 모든 클래스는 기본적으로 final 클래스이다.
엔티티 클래스가 final 클래스여도 JPA 사용에는 문제 없지만, 지연 로딩을 사용할 수 없다는 단점이 있다.

  • @OneToMany의 경우 final 클래스여도 지연 로딩을 사용할 수 있다.
    • @OneToMany는 보통 양방향 연관 관계를 위해 사용한다.
  • @ManyToOne은 Entity 클래스가 final이면 지연 로딩을 사용할 수 없다.
    • Entity 클래스를 open 해줘야한다.
    • Entity 마다 open 키워드를 붙여줄 수도 있지만 반복 작업을 줄이기 위해 추가 플러그인을 통해 특정 클래스에 open 처리를 해줄 수 있다.
  • 상속 가능한 상태의 Entity를 작성하기 위해서는 생성할 때마다 모든 클래스와 변수에 open 키워드를 적어줘야 한다.
    • no-arg 플러그인과 같이 allOpen 플러그인을 사용하여 반복 작업을 대체할 수 있다.
// kotlin-spring 플러그인이 있을 경우 all-open 플러그인을 포함하고 있다.

plugins {
    id 'org.jetbrains.kotlin.plugin.spring' version '1.7.22'
}

ㅡㅡㅡ

// Gradle (Spring Boot 3.0.0 이상)

plugins {
    ...
    id 'org.jetbrains.kotlin.plugin.allopen' version '{Kotlin버전}'	// Gradle(Groovy)
    kotlin("plugin.allopen") version "{Kotlin버전}"	// Gradle(Kotlin)
    ...
}
 
allOpen {
    annotation("jakarta.persistence.Entity")
    annotation("jakarta.persistence.Embeddable")
    annotation("jakarta.persistence.MappedSuperclass")
}

ㅡㅡㅡ

// Gradle (Spring Boot 3.0.0 미만)

plugins {
    ...
    id 'org.jetbrains.kotlin.plugin.allopen' version '{Kotlin버전}'	// Gradle(Groovy)
    kotlin("plugin.allopen") version "{Kotlin버전}"	// Gradle(Kotlin)
    ...
}
 
allOpen {
    annotation("javax.persistence.Entity")
    annotation("javax.persistence.Embeddable")
    annotation("javax.persistence.MappedSuperclass")
}
  • 위 설정을 통해 allOpen이 설정된 @Entity, @Embeddable, @MappedSuperclass 어노테이션이 붙은 모든 클래스는 자동으로 open 된다.

JPA의 Entity에는 data class가 어울리지 않는다.

코틀린의 data class
기본적으로 Lombok의 toString(), equals(), hashCode(), copy()를 포함하고 구조 분해를 사용할 수 있는 클래스

  • equals()와 hasCode()는 특별한 상황이 아니면 구현하지 않는 것이 좋은데 매번 구현하기 귀찮아 data class로 Entity를 만드는 것은 JPA의 엔티티로 사용하기에 적합하지 않다.
    • data class가 기본적으로 포함하는 메소드(ex. toString() or equals())가 양방향 연관 관계 갖는 엔티티 사이에서 서로의 메소드를 무한히 호출하는 순환참조가 발생하여 StackOverflow를 발생시킨다.
      • 원하는 프로퍼티에 한해서 toString(), equals(), hashCode()를 재정의해야 한다.
    • data class는 Kotlin 공식 문서에 따르면 3가지 요구 사항을 충족해야 합니다.
      1. 기본 생성자에는 매개변수가 하나 이상 있어야 한다.
      2. 모든 기본 생성자 매개변수는 val or var 으로 표기되어야 한다.
      3. 데이터 클래스는 추상, 공개, 봉인 또는 내부 클래스일 수 없다.
    • 하지만 allOpen을 플러그인을 이용하면 data class 또한 open이 가능하다.
      • 순환 참조 문제, data class를 억지로 open하면서 Entity로 만드는 것은 적합하지 않은 것 같다.

참조링크

[Kotlin/JPA] 코틀린과 JPA를 함께 사용할 때 추가적으로 설정해야 하는 것들과 Data class

JPA Entity 심층탐구 (1) Entity에 Kotlin Data class를 써도 될까?

0개의 댓글