티스토리 뷰
728x90
반응형
Body에 선언 - 안전한 Kotlin Entity 패턴
- id는 클래스 바디에서 var로 선언
- id가 null이면 "아직 DB에 저장되지 않은 비영속(Transient) 상태
- 자동 생성이 아니면 서비스/팩토리에서 member.id = "값" 형태로 세팅
- JPA는 프록시 객체 생성을 위해 기본 생성자가 필요
- 자동으로 기본 생성자를 만들어주는 라이브러리 "plugin.noarg", "org.jetbrains.kotlin.plugin.jpa"
- Body에 선언하면 직접 equals와 hashCode를 구현하기 용이
- JPA Entity의 동등성은 오직 PK(Primary Key)로만 판단해야 안전
- 영속화 전(Transient) 객체끼리 비교할 때도 동등으로 보고 싶은 경우는 PK 비교 안됨
@Entity
class Member(
var name: String
) {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: String? = null // 자동 생성이 아니면 직접 할당
protected constructor() : this("") // JPA 기본 생성자
}
ID를 수동 할당해야 할 경우
- 수동으로 ID를 넣고 싶다면 별도 팩토리 메서드를 두는 것이 더 안전
@Entity
class Member(
var name: String
) {
@Id
var id: String? = null
protected constructor() : this("")
companion object {
fun createWithId(id: String, name: String) = Member(name).apply {
this.id = id
}
}
}
Body 에 @Id 필드를 val 로 선언
- 일반적인 코드: user.id = 1L (컴파일 에러! val이라 수정 불가)
- Hibernate: field.setAccessible(true)를 호출하여 접근 제어자와 final 여부를 무시하고 값을 씁니다.
@Entity
class Member(
var name: String
) {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: String? = null
protected constructor() : this("")
}
굳이 var를 써야 하는 경우?
"나는 리플렉션 같은 매직이 싫고, 정말 논리적으로 값이 변하는 거니까 var여야 한다"고 생각하신다면,
var를 쓰되 외부 수정을 막아야 합니다.
하지만 val을 쓰면 protected set 같은 코드를 안 적어도 되니 더 간결
@Entity
class Member(
var name: String
) {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: String? = null
protected set // 외부에서는 수정 불가, 상속받은 클래스나 JPA는 접근 가능
protected constructor() : this("")
}
생성자에 선언하는 경우 - 권장 ❌
불변 읽기 전용 엔티티
- JPA가 값 세팅을 할 필요가 없고,
데이터베이스에서 Native Query + DTO 매핑 또는 @QueryProjection 같은 방식으로 즉시 생성되는 경우 - 이때는 JPA 영속성 컨텍스트 관리 대상이 아니거나, 조회 전용 Projection 엔티티일 때만 가능
@Entity
class Member(
@Id
val id: String, // 이미 DB에서 읽어온 값, 변경 없음
val name: String
)
@Id가 수동 생성되고, 생성 시점에 값이 확정
- 서비스 로직에서 ID를 항상 생성자 호출 전에 만들어 주는 경우
(예: UUID, KSUID, 특정 규칙 기반 키) - 이 경우 엔티티 생성 시점에 ID가 이미 세팅되므로 JPA가 ID를 채워 넣을 필요가 없음
@Entity
class Member(
@Id
val id: String, // 서비스에서 미리 생성
var name: String
) {
protected constructor() : this("", "") // JPA 기본 생성자
}
생성자에 val로 선언하는 경우
@Entity
class Member(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0,
var name: String
)
- val 불변 필드라도 생성된 id 값이 정상 생성
- Hibernate는 리플렉션으로 val 필드에 직접 값을 써버림
- 객체를 먼저 만듦
- INSERT 실행
- DB에서 생성된 ID를 받음
- 리플렉션으로 final 필드에 직접 값 써버림
- 하지만 객체 생성 시 id 값을 모른 상태로 Member(0, "Kim") 으로 생성해야하니 어색함
728x90
반응형
'Java' 카테고리의 다른 글
| [Kotlin] JPA에서 올바른 equals / hashCode 규칙, kassava 사용 시 문제 (0) | 2026.01.11 |
|---|---|
| [Kotlin] JPA Entity를 data class 로 사용할 경우 문제 (0) | 2026.01.11 |
| [Kotlin] nested class vs. inner class (0) | 2026.01.11 |
| [Java] openjdk:11-jdk-slim 이미지 사용 시 POI 엑셀 기능 불가 (0) | 2026.01.10 |
| [Java] Jackson @JsonAnySetter, @JsonAnyGetter (2) | 2025.07.08 |
반응형
300x250