[JPA 시리즈] 2 - 엔티티 매핑 (2 - 컬럼 매핑과 기본 키 매핑)

2025. 12. 11. 01:49·Development/Java(Spring, JPA, etc.)

전 포스트에 이어 컬럼 매핑과 기본 키 매핑에 대해 알아보자.

컬럼 매핑

오늘은 코드부터 보고 시작하자.

import jakarta.persistence.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;

@Entity
public class Member {
    @Id
    private Long id;
    
    @Column(name = "name")  // 컬럼 매핑
    private String username;
    
    private Integer age;
    
    @Enumerated(EnumType.STRING)  // enum 타입 매핑
    private RoleType roleType;
    
    @Temporal(TemporalType.TIMESTAMP)  // 날짜 타입 매핑
    private Date createdDate;
    
    @Temporal(TemporalType.TIMESTAMP)
    private Date lastModifiedDate;
    
    @Lob  // BLOB/CLOB 매핑
    private String description;
    
    //Getter, Setter…
}

 

@Column

이 어노테이션은 이름에서부터 보다시피 필드를 DB 테이블 컬럼에 매핑해 주는 역할을 한다. 
속성은 name, insertable/updatable, nullable, unique, columnDefinition, length, precision/scale 이렇게 구성되어 있고 name, insertable/updatable을 제외하고 전부 DDL 생성과 연관이 있다.

  • name: 필드와 매핑할 테이블의 컬럼명이다. 기본값은 객체의 필드명을 그대로 사용한다.
  • insertable/updatable: JPA에서 필드의 등록, 변경 가능 여부를 설정하는 속성. 기본값은 TRUE로 등록 및 변경 가능한 상태이다. 예시로 insertable이 True이고 updatable이 False이면 데이터가 처음 삽입되고 나면 이후에는 JPA 상에서는 절대 업데이트가 이루어지지 않는다. 업데이트가 필요하다면 직접 SQL 쿼리는 막히지 않았기 때문에 이 방법을 사용해야 한다.
  • nullable: 컬럼의 null 허용 여부를 설정한다. 이 값이 false이면 DDL 생성 시 컬럼에 NOT NULL 제약이 걸리게 된다.
  • unique: @Table 어노테이션의 uniqueConstraints와 같은 역할을 하나 한 컬럼에 간단하게 UNIQUE 제약조건을 걸 때 사용한다. 다만 이 속성으로 걸린 제약조건은 JPA에서 설정한 임의의 랜덤값으로 이름이 지정되어 문제 발생 시 원인 파악에 지장이 되기 때문에 잘 사용하지 않는다. UNIQUE 제약조건을 걸고 싶다면 @Table 어노테이션의 uniqueConstraints를 사용하는 것이 이름 지정도 가능하기 때문에 이쪽을 사용하는 편이 좋다.
  • columnDefinition: DB 컬럼 정보를 직접 지정하는 역할을 한다. 예를 들어 VARCHAR의 길이를 100자로 제한하고 기본값도 별도로 지정하고자 할 때 사용하면 좋다. Oracle, MySQL, PostgreSQL과 같이 특정 DBMS에 종속적인 옵션 또한 지정 가능하다.
  • length: 문자열 길이 제약조건을 걸어준다. 기본값은 255이며 Java의 String 타입에만 사용 가능하다.
  • precision/scale: BigDecimal/BigInteger과 같은 타입에서 사용 가능한 옵션. 각각 소수점 포함 전체 자릿수, 소수의 자릿수를 지정한다. 조건으로는 double/float 타입에는 적용되지 않으며 매우 큰 수나 고정밀 소수를 다룰 때 사용한다.

 

@Enumerated

이 어노테이션은 자바의 enum 타입을 매핑할 때 사용한다. 속성은 저장 타입을 지정하는 value 하나이다.

public enum EnumType {
    /** Persist enumerated type property or field as an integer. */
    ORDINAL,  // enum 타입을 0부터 시작하여 순서대로 저장

    /** Persist enumerated type property or field as a string. */
    STRING    // enum 타입을 이름 그대로 저장
}

이 어노테이션은 주의점이 하나 있는데, 저장 타입을 EnumType.ORDINAL로 한 경우에는 enum의 구성이 바뀌는 경우에 유의해야 한다.

예시를 보자. 아래는 EnumType.ORDINAL, EnumType.STRING으로 지정했을 때의 저장되는 값 차이를 보여준다.

/* EnumType.ORDINAL */
enum OrderStatus {
    ORDERED,   // 0
    COMPLETED  // 1
}

/* EnumType.STRING */
enum OrderStatus {
    ORDERED,   // ORDERED
    COMPLETED  // COMPLETED
}

이 enum 값이 바뀌지 않는다면 상관이 없겠지만 문제는 중간에 요구사항이나 Spec이 바뀌어 값이 바뀌는 경우이다.

예를 들어 위 OrderStatus에 두 값 사이에 ONDELIVERY가 추가되는 경우,

/* EnumType.ORDINAL */
enum OrderStatus {
    ORDERED,     // 0
    ONDELIVERY,  // 1
    COMPLETED    // 2
}

/* EnumType.STRING */
enum OrderStatus {
    ORDERED,     // ORDERED
    ONDELIVERY,  // ONDELIVERY
    COMPLETED    // COMPLETED
}

이렇게 되는데, STRING은 값 그대로 지정되는 반면 ORDINAL로 설정하면 값이 변경되기 전에 저장되는 데이터는 기존 COMPLETED에 해당하는 값이 ONDELIVERY로 바뀌어버리는 사태가 벌어진다.

또 ORDINAL의 경우 위에 보다시피 숫자로 저장되기 때문에 그리 직관적이지도 않다.

그렇기 때문에 이 어노테이션을 사용할 때에는 정 필요한 케이스가 아니고서는 어지간해서는 EnumType.STRING을 사용하는 것이 정신건강에 이롭다.

 

@Temporal

이 어노테이션은 날짜 타입(주로 java.util.Date 사용)을 매핑할 때 사용하는 어노테이션이다. 이것도 마찬가지로 속성은 value 하나이다.

TemporalType.DATE 날짜를 데이터베이스의 date 타입과 매핑
TemporalType.TIME 시간을 데이터베이스의 time 타입과 매핑
TemporalType.TIMESTAMP 날짜, 시간을 데이터베이스의 timestamp 타입과 매핑

어노테이션을 지정해주지 않는 경우에는 기본적으로 timestamp 타입이 사용된다.

또 생략 가능한 케이스는 LocalDate, LocalDateTime 타입을 사용하는 것이다. 이 경우에는 Hibernate가 자동으로 타입을 인식해 적용해 준다. 

 

@Lob / @Transient

@Lob 어노테이션의 경우 BLOB(Binary Large OBject)나 CLOB(Character Large OBject) 같이 대용량의 데이터를 저장해야 하는 필드에 사용한다.

@Transient 어노테이션은 DB에 저장도 조회도 하지 않고 임시로 메모리 상에서만 사용할 때에 적용하는 어노테이션이다.

 

기본 키 매핑

기본 키의 매핑은 @Id와 @GeneratedValue 이 두 가지 어노테이션의 조합으로 사용한다.

@Id 어노테이션은 기본 키값을 직접 지정할 경우에 사용하고, @GeneratedValue는 DB에 생성을 맡길 때 사용한다.

@GeneratedValue에는 설정할 수 있는 옵션이 4가지가 있는데 아래와 같다.

IDENTITY 데이터베이스에 생성을 위임한다. MySQL에서 사용
SEQUENCE DB 시퀀스 오브젝트를 사용한다. Oracle DB에서 사용하며 @SequenceGenerator 어노테이션이 같이 와야 한다.
TABLE 키 생성용 테이블을 별도로 사용한다. 모든 DB에서 사용 가능하며 @TableGenerator 어노테이션을 같이 사용해야 함
AUTO [기본값] DBMS 별 방언에 따라 자동으로 지정

IDENTITY 전략부터 알아보자.

IDENTITY 전략

@Entity
public class Member {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    /* ... */
}

이 전략은 기본 키 생성을 데이터베이스에 위임을 하는 전략이다. 대표적으로 MySQL이나 PostgreSQL에서 사용하며 MySQL에서 이 옵션을 지정하면 컬럼에 AUTO_INCREMENT 속성이 붙는다.

JPA에서는 보통 트랜잭션 커밋 시점에 DB에 INSERT SQL을 날리기 때문에 AUTO_INCREMENT 속성이 붙은 기본키 값은 INSERT 쿼리가 실행된 이후에나 그 값을 알 수 있다.

그렇기 때문에 이 전략에서는 EntityManager.persist() 실행 시점에 즉시 INSERT 쿼리를 실행하고 DB에서 식별자를 불러오게 된다.

SEQUENCE 전략

@Entity
@SequenceGenerator(
    name = “MEMBER_SEQ_GENERATOR",  // SequenceGenerator 이름
    sequenceName = “MEMBER_SEQ",     // 매핑할 DB 시퀀스 이름
    initialValue = 1,                // 시퀀스의 시작값 (기본값: 1)
    allocationSize = 1               // 시퀀스 호출 시마다 증가하는 값 (기본값: 50)
)
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBER_SEQ_GENERATOR")
    private Long id;
    /* ... */
}

이 전략은 SEQUENCE라는 유일한 값을 생성하는 DB 오브젝트를 별도로 생성하여 기본키를 생성하는 전략이다. 주로 Oracle이나 PostgreSQL에서 사용한다.

이 전략에서는 SequenceGenerator라는 어노테이션을 별도로 사용한다. 생략할 수도 있지만 그럴 경우에는 Hibernate에 설정된 전역 설정을 사용하게 된다. 주로 사용하는 속성은 위의 코드와 같다.

여기서 유심히 봐야 할 속성은 initialValue와 allocationSize 이 두 가지이다. 각각 시퀀스의 시작 지점, 시퀀스 호출 시마다 증가하는 값이다.

예를 들어 initialValue가 1이고 allocationSize가 50이라면 트랜잭션을 열어 시퀀스를 생성하면 50개의 기본키를 미리 할당받아 persist 시점에 이를 사용한다. 이렇게 하면 각 트랜잭션마다 50 단위로 기본키 값을 할당받기 때문에 동시성 이슈에서 자유로울 수 있게 된다.

물론 allocationSize를 1로 설정하여 IDENTITY 전략처럼 1 단위로 증가하게 할 수도 있다.

TABLE 전략

이 전략은 SEQUENCE 전략과 동일하게 시퀀스를 생성하나 대신 키 생성용 테이블을 생성해 DB 시퀀스를 모사한다.

장점은 SEQUENCE 전략과 달리 모든 DBMS에서 사용 가능하지만 단점은 테이블에서 조회하는 과정을 거쳐야 하기 때문에 성능에서 손해를 보게 된다.

속성은 SequenceGenerator와 크게 다르지 않으나 pkColumnName과 pkColumnValue로 키 생성용 테이블에 사용할 컬럼명과 키값을 설정할 수 있다.

저작자표시 비영리 변경금지 (새창열림)

'Development > Java(Spring, JPA, etc.)' 카테고리의 다른 글

[JPA 시리즈] 2 - 엔티티 매핑 (1 - 엔티티 매핑 / 스키마 자동 생성)  (0) 2025.11.27
[JPA 시리즈] 1 - 영속성 컨텍스트 (2 - 준영속)  (0) 2025.11.19
[JPA 시리즈] 1 - 영속성 컨텍스트(1)  (0) 2025.11.12
'Development/Java(Spring, JPA, etc.)' 카테고리의 다른 글
  • [JPA 시리즈] 2 - 엔티티 매핑 (1 - 엔티티 매핑 / 스키마 자동 생성)
  • [JPA 시리즈] 1 - 영속성 컨텍스트 (2 - 준영속)
  • [JPA 시리즈] 1 - 영속성 컨텍스트(1)
Polaris_
Polaris_
  • Polaris_
    아무거나 다 적는 블로그
    Polaris_
  • 전체
    오늘
    어제
    • 분류 전체보기 (17)
      • Development (16)
        • TIL (Today what I Learned) (0)
        • 데브코스(TIL, 회고록 등등...) (10)
        • Java(Spring, JPA, etc.) (4)
        • 코딩 테스트 (알고리즘) (0)
        • 기타 (1)
      • 일상 (1)
      • 여행기 (0)
        • 국내 (0)
        • 해외 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    풀스택
    웹
    준영속
    Express.js
    코딩부트캠프
    포트폴리오
    프로젝트 설계
    css
    데브코스
    HTTP
    typescript
    프로그래머스 데브코스
    docker
    React
    html
    javascript
    JPA
    Postman
    TIL
    node.js
    국비지원교육
    MySQL
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
Polaris_
[JPA 시리즈] 2 - 엔티티 매핑 (2 - 컬럼 매핑과 기본 키 매핑)
상단으로

티스토리툴바