일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 1차원 DP
- 2차원 dp
- 99클럽
- @Builder
- @GeneratedValue
- @GenericGenerator
- @NoargsConstructor
- @Transactional
- Actions
- Amazon EFS
- amazon fsx
- Android Studio
- ANSI SQL
- ApplicationEvent
- assertThat
- async/await
- AVG
- AWS
- Azure
- bind
- builder
- button
- c++
- c++ builder
- c03
- Callback
- case when
- CCW
- chat GPT
- CICD
- Today
- Total
기록
[자바 ORM 표준 JPA 프로그래밍] @Entity부터 기본 키 매핑까지 (4장) 본문
이번 글에서는 JPA의 엔티티 매핑에 대해 정리해보았습니다. 이 글은 링크에서 소개된 책 '자바 ORM 표준 JPA 프로그래밍'의 4장 '엔티티 매핑' 부분을 읽고, 제 경험과 함께 정리한 내용입니다. JPA의 핵심 어노테이션과 다양한 매핑 전략에 대해 설명하며, 특히 초심자들이 놓치기 쉬운 주의점들을 함께 다루고자 합니다. JPA 엔티티 매핑을 좀 더 명확하게 이해하고 활용하고자 하는 분들에게 도움이 되기를 바랍니다.
1. 엔티티 매핑의 기초
1.1 @Entity
JPA를 사용해 테이블과 매핑할 클래스에는 반드시 @Entity
어노테이션을 붙여야 합니다. 이 어노테이션은 클래스가 JPA의 관리 대상임을 나타내며, 이를 통해 데이터베이스 테이블과의 매핑을 수행합니다.
- 사용 불가한 클래스:
@Entity
는 final 클래스, enum, interface, inner class에는 사용할 수 없습니다. - name 옵션:
@Entity(name = ?)
로 엔티티의 이름을 지정할 수 있습니다. 이를 통해 JPQL에서 해당 이름을 사용할 수 있습니다. - 기본 생성자 필수: JPA는 내부적으로 리플렉션을 통해 엔티티 객체를 생성합니다. 기본 생성자가 없으면
InstantiationException
이 발생하며 객체를 인스턴스화할 수 없습니다. 기본 생성자는protected
또는public
으로 선언하는 것이 일반적입니다.
1.2 @Table
@Table
어노테이션은 엔티티와 매핑될 테이블을 지정합니다. 생략하면 엔티티 클래스의 이름을 테이블 이름으로 사용합니다. 주요 옵션은 다음과 같습니다:
- name: 매핑할 테이블의 이름을 지정합니다.
- catalog, schema: 특정 데이터베이스 카탈로그나 스키마에 있는 테이블을 지정할 수 있습니다.
1.3 코틀린에서 기본 생성자 설정 가이드
코틀린에서 JPA 엔티티를 작성할 때는 기본 생성자를 추가로 고려해야 합니다. 코틀린은 프로퍼티를 val
로 선언하면 불변이 되어 기본 생성자를 만들기 어려운데, 이를 해결하기 위한 몇 가지 방법을 소개합니다.
- 프로퍼티를
var
로 선언하거나 nullable로 설정하기: JPA는 엔티티 생성 시 기본 생성자를 호출한 후 값을 설정합니다. 따라서 모든 프로퍼티를var
로 선언하거나 nullable로 설정해야 합니다.
@Entity
@Table(name = "member")
class Member(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null,
@Column(nullable = false, length = 50)
var username: String? = null
)
@JvmOverloads
어노테이션 사용하기: 기본값을 제공하여 여러 생성자를 자동으로 생성할 수 있습니다. 이를 통해 JPA가 요구하는 파라미터 없는 기본 생성자를 생성할 수 있습니다.
@Entity
@Table(name = "member")
class Member @JvmOverloads constructor(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null,
@Column(nullable = false, length = 50)
var username: String = "defaultUsername"
)
1.3.1 코틀린과 JPA의 불편한 점
제가 JPA와 코틀린을 사용한 프로젝트를 진행할 때, 몇 가지 고민이 있었습니다. 우선, null이 허용되지 않는 컬럼을 Long?
과 같은 nullable 타입으로 선언해야 했습니다. 이 경우 코틀린의 강력한 타입 안전성을 활용하지 못하고 있다는 느낌을 받았습니다. 이는 코틀린을 "코틀린답게" 쓰지 못하고 있다는 고민을 야기했습니다.
또한, 모델의 ID 값을 외부에서 직접 변경하지 못하게 하고 싶었지만, JPA의 요구에 따라 var
로 선언해야 하다 보니 이를 방지할 수 없게 되었습니다. 이러한 문제는 코틀린의 불변성을 훼손하며, 코틀린의 장점을 충분히 살리지 못하는 결과를 낳았습니다.
이 두 가지 문제가 결국 코틀린을 코틀린스럽지 않게 만드는 것 같아 많은 고민을 하게 되었습니다. 코틀린의 불변성을 유지하면서도 JPA의 요구 사항을 만족시키기 위한 절충점을 찾기 위해 @JvmOverloads
와 같이 여러 가지 기법을 사용했지만, 여전히 이러한 점들은 코틀린과 JPA를 함께 사용하는 데 있어 어려운 부분 중 하나였습니다. 앞으로도 이러한 문제를 해결하기 위해서는 더 많은 연구와 고민이 필요할 것 같습니다.
2. 데이터베이스 스키마 자동 생성
JPA는 애플리케이션 실행 시점에 데이터베이스 테이블을 자동으로 생성할 수 있습니다. 이를 제어하기 위해 Hibernate의 hibernate.hbm2ddl.auto
설정을 사용합니다.
- create: 애플리케이션 시작 시점에 테이블을 생성합니다.
- create-drop: 시작 시점에 테이블을 생성하고 종료 시점에 삭제합니다.
- update: 엔티티 구조에 맞게 데이터베이스 스키마를 갱신합니다.
- validate: 데이터베이스와 엔티티가 일치하는지 확인합니다. 변경은 수행하지 않습니다.
- none: 아무 작업도 수행하지 않습니다.
Tip: 운영 환경에서는 hibernate.hbm2ddl.auto를 validate 또는 none으로 설정하는 것이 안전합니다. 개발 단계에서만 테이블을 자동으로 수정하고, 운영 환경에서는 데이터 무결성을 유지하는 것이 중요합니다.
3. 기본 키 매핑 전략
기본 키 매핑은 엔티티의 생명 주기에서 중요한 역할을 합니다. JPA에서는 다양한 방식으로 기본 키를 매핑할 수 있습니다.
3.1 직접 할당
기본 키를 직접 할당하는 방식입니다. 엔티티에 @Id
어노테이션을 사용해 기본 키를 지정합니다.
@Entity
@Getter @Setter
public class Product {
@Id
private String productId;
// 기본 생성자
public Product() {}
}
// service.java
Product product = new Product();
product.setProductId("product0001");
3.2 자동 생성 전략
JPA는 기본 키를 자동으로 생성하기 위한 여러 가지 전략을 제공합니다:
- IDENTITY: 데이터베이스에 기본 키 생성을 위임합니다. MySQL의
AUTO_INCREMENT
처럼 데이터베이스가 식별자를 자동으로 생성합니다.em.persist()
시점에 즉시INSERT SQL
을 실행하고, 기본 키 값을 조회합니다.
@Entity @Getter @Setter public class Customer { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; }
- SEQUENCE: 데이터베이스의 시퀀스를 사용해 기본 키를 생성합니다. Oracle, PostgreSQL 등에서 사용 가능합니다.
em.persist()
시점에 먼저 시퀀스를 사용해 식별자를 조회하고, 이를 엔티티에 할당한 후 영속성 컨텍스트에 저장합니다.
@Entity @Getter @Setter @SequenceGenerator(name = "member_seq", sequenceName = "member_sequence") public class Member { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "member_seq") private Long id; private String username; }
- TABLE: 키 생성 전용 테이블을 만들어서 시퀀스를 흉내냅니다. 모든 데이터베이스에서 사용할 수 있지만 성능이 좋지 않을 수 있습니다.
em.persist()
시점에 키 생성 테이블을 조회하여 다음 식별자를 가져온 후 엔티티에 할당합니다.
@Entity @Getter @Setter @TableGenerator(name = "order_table_gen", table = "id_gen", pkColumnName = "gen_name", valueColumnName = "gen_value", allocationSize = 1) public class Order { @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "order_table_gen") private Long id; private String orderNumber; }
- AUTO: 데이터베이스 방언에 따라 적절한 키 생성 전략을 자동으로 선택합니다.
- JPA가 사용 중인 데이터베이스에 적합한 전략을 자동으로 선택하며,
em.persist()
시점에 해당 전략에 따라 동작합니다.
@Entity @Getter @Setter public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String email; }
- JPA가 사용 중인 데이터베이스에 적합한 전략을 자동으로 선택하며,
Tip: 자연 키보다는 대리 키 사용을 권장합니다. 비즈니스 규칙은 변하기 쉽기 때문에 대리 키를 사용하면 변화에 더 유연하게 대처할 수 있습니다.
3.3 시퀀스 사용 경험과 대처 전략
제가 보통 프로젝트에서 ID를 IMG0001
처럼 어떤 데이터인지 한분에 쉽게 이해할 수 있도록 설정하곤 했습니다. ([Web/Spring] - Spring Boot와 JPA를 활용한 커스텀 아이디 생성 전략 구현) 이를 위해 주로 시퀀스를 따서 사용했는데, 일부 데이터베이스는 시퀀스를 지원하지 않아 곤란했던 경험이 있습니다. 예를 들어, MySQL의 경우 시퀀스를 기본적으로 지원하지 않기 때문에 이러한 방식이 불가능했습니다.
CREATE SEQUENCE member_sequence
START WITH 1
INCREMENT BY 1
NO MAXVALUE;
이러한 상황에 대처하기 위해 TABLE 전략도 함께 사용했으나, 이는 성능상의 문제가 있었습니다. 따라서, 시퀀스를 지원하지 않는 데이터베이스에서는 별도의 키 생성 로직을 애플리케이션 레벨에서 구현하는 등의 방법으로 문제를 해결했습니다.
4. 필드와 컬럼 매핑
엔티티의 필드를 데이터베이스 컬럼에 매핑하기 위해 다양한 어노테이션을 사용할 수 있습니다.
@Column
: 필드를 테이블의 컬럼에 매핑합니다.@Enumerated
:enum
타입을 매핑할 때 사용합니다.ORDINAL
또는STRING
으로 지정할 수 있습니다.@Temporal
:Date
타입을 매핑할 때 사용합니다.DATE
,TIME
,TIMESTAMP
중 하나를 선택합니다.@Lob
: 대용량 데이터 (BLOB
,CLOB
)를 매핑할 때 사용합니다.@Transient
: 데이터베이스에 저장하지 않을 필드를 지정합니다.@Access
: 필드 접근 방식과 프로퍼티 접근 방식을 지정합니다.
5. 마무리
JPA 엔티티 매핑은 객체와 데이터베이스 간의 연결을 설정하는 중요한 작업입니다. 올바른 매핑을 통해 애플리케이션의 유지보수성을 높이고 데이터 일관성을 유지할 수 있습니다. 특히 기본 키 매핑 전략을 잘 이해하고 상황에 맞는 전략을 사용하는 것이 중요합니다. 코틀린과 함께 사용하는 경우, 불변성과 JPA의 요구사항 사이에서 적절한 절충점을 찾아야 하는 과제가 있습니다.
'교육 > 책' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍] 다양한 연관관계 매핑: 일대다, 다대일, 다대다 (6장) (0) | 2024.11.27 |
---|---|
[자바 ORM 표준 JPA 프로그래밍] JPA 연관관계 매핑 기초: 단방향과 양방향 연관관계 (5장) (0) | 2024.11.23 |
[자바 ORM 표준 JPA 프로그래밍] 영속성 컨텍스트와 엔티티 생명주기 (3장) (0) | 2024.11.16 |
[자바 ORM 표준 JPA 프로그래밍] JPA 시작하기: 개발 환경부터 객체 매핑까지(2장) (0) | 2024.11.14 |
[책리뷰] C++ Builder Step by Click (0) | 2024.11.04 |