1. Kotlin의 data class는 JPA에서 사용하지 않는다

영속성 계층의 Entity가 되려면 아래를 포함한 조건이 갖춰져야 한다.

  • non-final 최상위 클래스이거나 정적 이너클래스일 것
  • public 또는 protected, no-args 생성자를 가질 것
  • final 메서드나 persistent한 인스턴스 변수를 가지지 않을 것 ... (이하 생략)

출처: javadoc - jakarta.persistence.entity

JPA는 객체의 상태 변경을 기본 전제로 만들어진 패러다임이다. 반면 함수형을 지향하는 코틀린에서는 기본 속성이 val(read-only)immutable인 것을 전제로 한다.

내 경우 Entity를 기본 속성이 val(read-only)로 설정되는 data class로 설계했다가 모두 var을 가진 일반 클래스로 바꿔야 했다. 수정자가 추가되며 리턴타입도 copy된 객체를 리턴하던 것이 void로 바뀌었고 테스트도 모두 바꿔야 했다.

이는 스프링 공식 가이드에도 기술된 바이다. Spring Data JPA가 아닌 다른 Spring Data의 경우 data class의 사용이 문제되지 않는다고 한다.

위 가이드의 샘플 코드에서 속성들의 접근제어자는 그냥 디폴트이다. 그래서 나도 따로 private var로 만들지는 않았다. 검색을 해봐도 많은 경우 Entity의 속성을 private으로 따로 지정하지 않는 것 같았다. 조금 의아하지만 일단 공식문서에 기술된 대로 따라보기로 했다.

그리고 그래들 플러그인을 다음과 같이 추가해주었다.

plugins {
  kotlin("plugin.allopen") version "1.9.22"
  kotlin("plugin.noarg") version "1.9.22"
}

allOpen {
  annotation("jakarta.persistence.Entity")
  annotation("jakarta.persistence.Embeddable")
  annotation("jakarta.persistence.MappedSuperclass")
}

매 엔티티 클래스마다 open을 선언해주거나 no-arg 생성자를 만들지 않아도 영속성 Entity가 될 수 있도록 해준다.

별도로 지정해주지 않아도 open class로 바뀌어있다

data class일 때는 자동으로 만들어줬던 hashcodeequals, toString은 직접 구현해줘야 했다.

2. Key Generation?

insert 하기 전에 select가 실행되지만 실제 쿼리가 실행되는 건 아니다. insertselect를 실행하면 영속성 계층에 select하는 대상의 id를 가진 객체가 있는지 먼저 탐색한다고 한다. GenerationType.SEQUENCE 전략으로 키를 생성하기로 했다. 하지만 매번 키 탐색을 위해 DB를 다녀오는게 이상하다. 기본적으로 50개가 할당된다고 들었는데.

그런데 키 생성전략을 위해서는 이렇게 DB를 이용하는게 최선일까? 좋은 방법이 있을 것 같은데.

3. H2 DB와 테스트 환경

H2 db로 테스트하면 매 테스트마다 롤백이 된다고 한다. 즉 실제 DB에 들어가지 않는다는 말이다. 테스트마다 아이디를 일부러 중복으로 넣어도 무결성 에러도 나지 않고 디버깅으로 멈춰 놓고 콘솔에 접속해도 아무것도 없어서 어리둥절했다. 그럼 DB가 초기화될 때 DDL도 실행되지 않는 것인가..?