JPA Mapping Annotation

wujin·2023년 3월 30일
0

JPA Mapping Annotation

JPA에서는 주로 어노테이션(annotation)을 사용하여 데이터베이스와 매핑될 객체를 정의한다. 어노테이션은 자바 소스코드에 부가적인 정보를 제공하며, 컴파일러나 런타임에 사용된다.


1. @Entity

JPA에서 엔티티 클래스임을 나타내는 어노테이션이다. 해당 어노테이션을 사용하면 해당 클래스가 데이터베이스 테이블과 매핑되는 엔티티 클래스임을 JPA가 인식하게 된다.

@Entity
public class Member {
    ...
}

2. @Table

@Table 어노테이션은 엔티티 클래스와 매핑될 테이블의 이름을 지정하는 어노테이션이다.
만약 @Table 어노테이션을 생략하면, JPA는 엔티티 클래스의 이름을 기본적으로 테이블 이름으로 사용한다.

@Entity
@Table(name = "members", schema = "{DB schema name}")
public class Member {
    ...
}

3. @Id

@Id 어노테이션은 엔티티 클래스의 기본 키(primary key) 필드를 나타내는 어노테이션이다.
@Id 어노테이션을 사용하여 기본 키 필드를 지정할 수 있습니다. 만약 @Id 어노테이션을 생략하면, JPA는 엔티티 클래스의 이름이 "id"인 필드를 기본 키 필드로 사용한다.

@Entity
@Table(name = "members", schema = "{DB schema name}")
public class Member {

    @Id
    private Long id;
    
    private String name;
    
    private int age;
}

4. @Embeddable, @IdClass

JPA에서는 복합키를 설정하는 두 가지 방법이 존재한다.

1) @Embeddable 사용

@EmbeddedId 어노테이션을 사용하여 복합키 클래스를 정의하고, 해당 클래스를 엔티티 클래스에서 사용하는 방법이다.

@Entity
public class Order {

    @EmbeddedId
    private OrderPK pk;
    
    private String product;
    
    private int price;
    
    // getter, setter 생략
}

@Embeddable
public class OrderPK implements Serializable {

    private Long orderId;
    
    private Long memberId;
    
    // getter, setter 생략
}

위 코드에서 Order 엔티티 클래스는 복합키로 OrderPK 클래스를 사용하고 있다. OrderPK 클래스는 @Embeddable 어노테이션을 사용하여, 복합키 클래스임을 나타낸다.

2) @IdClass 사용

@IdClass 어노테이션을 사용하여 복합키 클래스를 정의하고, 해당 클래스를 엔티티 클래스에서 사용하는 방법이다.

@Entity
@IdClass(OrderPK.class)
public class Order {

    @Id
    private Long orderId;
    
    @Id
    private Long memberId;
    
    private String product;
    
    private int price;
    
    // getter, setter 생략
}

public class OrderPK implements Serializable {

    private Long orderId;
    
    private Long memberId;
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (!(obj instanceof OrderPK))
            return false;

        OrderPK other = (OrderPK) obj;
        return Objects.equals(getOrderId(), other.getOrderId()) && Objects.equals(getMemberId(), other.getMemberId());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getOrderId(), getMemberId());
    }
}

위 코드에서 Order 엔티티 클래스는 복합키로 OrderPK 클래스를 사용하고 있다. @IdClass 어노테이션을 사용하여, OrderPK 클래스를 복합키 클래스로 사용함을 나타낸다.

equals() 메소드는 Object 클래스의 equals() 메소드를 먼저 호출하여, 주소값이 같은지를 비교한다. 그 다음에는 obj가 OrderPK 클래스의 인스턴스인지를 검사한 뒤, orderId와 memberId 필드값이 같은지를 비교하여 결과를 반환한다.

hashCode() 메소드는 Objects 클래스의 hash() 메소드를 호출하여, orderId와 memberId 필드값을 이용하여 해시코드를 생성한다. 이렇게 생성된 해시코드는 JPA에서 엔티티를 비교하거나 검색할 때 사용된다.

이렇게 두 가지 방법으로 복합키를 설정할 수 있다. 복합키를 사용하는 경우, 복합키 클래스에 equals()와 hashCode() 메소드를 구현해주어야 한다. 이는 JPA에서 엔티티를 비교하거나 검색할 때 사용되기 때문이다.


5. @GeneratedValue

@GeneratedValue 어노테이션은 기본 키(primary key) 값의 자동 생성 전략을 지정하는 어노테이션이다. 이 어노테이션은 @Id 어노테이션과 함께 사용된다.
자동 생성의 종류는 총 4가지가 있다.

1) IDENTITY

데이터베이스에서 기본 키 값을 생성한다.
MySQL에서는 AUTO_INCREMENT, SQL Server에서는 IDENTITY를 사용한다.
IDENTITY 전략은 데이터베이스에 값을 저장하고 나서야 기본 키 값을 구할 수 있습니다.
아래의 코드를 보면 transaction.commit(); 커밋 코드가 없음에도 출력이 되는 것을 알 수 있다.

@Entity
public class Member {

	@Id
 	@GeneratedValue(strategy = GenerationType.IDENTITY)
 	private Long id;
 	...
}
/* ------------------------------------------------------- */
private static void logic(EntityManager em) {
	Member member = new Member();
	em.persist(member);
	System.out.println("member.id = " + member.getId());
    // 출력: member.id = 1
}

엔티티가 영속 상태가 되기 위해서는 식별자가 필요하다. IDENTITY 전략의 경우, 식별자 생성을 DB에 저장해야 구할 수 있으므로 em.persist()를 호출하여 객체를 영속성 상태로 만드는 순간 INSERT SQL이 호출된다. 따라서, 이 전략은 쓰기 지연이 동작하지 않는다.

2) SEQUENCE

데이터베이스 시퀀스를 사용해서 기본 키를 할당한다.
추가적인 어노테이션으로 @SequenceGenerator 필요하다.
보통 ORACLE, PostgreSQL, DB2, H2 데이터베이스에서 사용된다.

// 시퀀스 DDL
CREATE TABLE MEMBER (
	ID BIGINT NOT NULL PRIMARY KEY,
   	DATA VARCHAR(255)
)

// 시퀀스 생성
CREATE SEQUENCE MEMBER_SEQ START WITH 1 INCREMENT BY 1;
// 시퀀스 매핑 코드
@Entity
@SequenceGenerator(
	name = "MEMBER_SEQ_GENERATOR",
 	sequenceName = "MEMBER_SEQ", //매핑할 데이터베이스 시퀀스 이름
 	initialValue = 1, allocationSize = 1)
public class Member{

	@Id
 	@GeneratedValue(strategy = GenerationType.SEQUENCE,
    				generator = "MEMBER_SEQ_GENERATOR"
	)
 	private Long id;
 	...
 }

SEQUENCE 전략의 경우, IDENTITY 전략과 코드는 동일하지만, 내부 동작이 다르다.

동작과정
em.persist() 호출
-> 데이터베이스 시퀀스를 사용해서 식별자를 조회
-> 식별자를 엔티티에 할당
-> 엔티티를 영속성 컨텍스트에 저장
-> 트랜잭션을 커밋해서 플러시가 일어났을 경우, 엔티티를 데이터베이스에 저장

IDENTITY와의 차이점은 IDENTITY의 경우, 영속성 컨텍스트에 저장하기 위해서 INSERT를 바로 날린다는 것.

allocationSize 기본값이 50으로 존재하는 이유
결론은 데이터베이스에 여러번 접근하는 것을 방지해서 성능적으로 장점이 있기 때문이다.
SEQUENCE 전략은 데이터베이스 시퀀스를 통해 식별자를 조회하는 추가 작업이 필요하다. 따라서 다음과 같이 데이터베이스와 2번의 통신을 한다.
.
1. 식별자를 구하기 위해 데이터베이스 시퀀스를 조회 ex) SELECT MEMBER_SEQ.NEXTVAL …
2. 조회한 시퀀스를 기본 값으로 사용해 데이터베이스에 저장 ex) INSERT INTO MEMBER …
.
위와 같이 시퀀스에 접근하는 것을 줄이기 위해 JPA는 @SequenceGenerator.allocationSize를 사용한다. 간단히 설명하면, 여기에 설정한 값만큼 한 번에 시퀀스 값을 증가시키고 나서 그만큼 메모리에 시퀀스 값을 할당한다. 예를들어, allocation 값이 50이면 시퀀스를 한 번에 50을 증가시킨 다음에 1 ~ 50까지는 메모리에 시퀀스 값을 할당한다. 그리고 51이 되면 시퀀스 값을 100으로 증가시킨 다음 51 ~ 100까지 메모리에서 식별자를 할당한다.
.
이 최적화 방법은 시퀀스 값을 선점하므로 여러 JVM이 동시에 동작해도 기본 키 값이 충돌하지 않는 장점이 있다. 반면에 데이터베이스에 직접 접근해서 데이터를 등록할 때 시퀀스 값이 한 번에 많이 증가한다는 점을 염두해두어야 한다. 이런 상황이 부담스럽고 INSERT 성능이 중요하지 않으면 allocationSize의 값을 1로 설정하면 된다.

3) TABLE

별도의 키 생성 전용 테이블을 하나 만들고 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내내는 전략이다.
모든 DB에서 사용이 가능하며, 추가적으로 @TableGenerator가 필요하다.
ddl-auto가 아닌 경우, 별도의 Table을 생성해줘야 한다.

장점: 모든 데이터베이스에 적용 가능
단점: 성능이 떨어짐

// TABLE 전략 키 생성 DDL
create table MY_SEQUENCE(
	sequence_name varchar(255) not null ,
   	next_val bigint,
   	primary key ( sequence_name )
)

SEQUENCE 전략에서 @SequenceGenerator를 사용한 것처럼, Table은 @TableGenerator를 사용한다.

// TABLE 전략 매핑 코드
@Entity
@TableGenerator(
	name = "BOARD_SEQ_GENERATOR",
    table = "MY_SEQUENCES",
    pkColumnValue = "BOARD_SEQ", allocationSize = 1
)
public class Member {

	@Id
 	@GeneratedValue(strategy = GenerationType.TABLE,
    				generator = "BOARD_SEQ_GENERATOR"
	)
 	private Long id;
 	...
}

allocationSize가 기본값 50으로 존재하는 이유
TABLE 전략은 값을 조회하면서 SELECT 쿼리를 사용하고 다음 값으로 증가시키기 위해 UPDATE 쿼리를 사용한다. 이 전략은 SEQUENCE 전략과 비교해서 데이터베이스와 한 번 더 통신하는 단점이 있다. 이 전략도 최적화하기 위해 allocationSize를 사용하면 되고, 방법은 SEQUENCE 전략과 같다.

4) AUTO

자동으로 기본 키 값을 생성한다. 데이터베이스에 따라 다르게 처리될 수 있다. 예를 들어 MySQL에서는 AUTO_INCREMENT를 사용하고, Oracle에서는 시퀀스(Sequence)를 사용한다.


6. @Column

@Column 어노테이션은 엔티티의 속성이 데이터베이스의 컬럼과 매핑됨을 나타낸다. 이 어노테이션을 사용하여 컬럼의 이름, 길이, NULL 허용 여부 등을 설정할 수 있다.

  • name
@Column(name = "user_name")
private String username;

매핑할 데이터베이스 열의 이름을 지정한다. 위 예시에서는 "user_name"으로 매핑된다. 만약 이 옵션을 생략하면 엔티티 클래스의 필드 이름이 데이터베이스 열 이름으로 매핑된다.

  • nullable
@Column(nullable = false)
private String username;

해당 필드가 데이터베이스 열에서 null 값을 허용하는지 여부를 지정한다. 위 예시에서는 false로 지정하였기 때문에 데이터베이스 열에서 null 값을 허용하지 않는다. 기본값은 true이다.

  • length
@Column(length = 20)
private String username;

해당 필드가 데이터베이스 열에서 차지하는 최대 길이를 지정한다. 위 예시에서는 최대 20자리까지 저장할 수 있다. 만약 이 옵션을 생략하면 문자열의 길이에 따라 데이터베이스에서 자동으로 결정된다.

  • unique
@Column(unique = true)
private String username;

해당 필드가 데이터베이스 열에서 고유한 값을 가지는지 여부를 지정한다. 위 예시에서는 true로 지정하였기 때문에 username 값은 고유해야 한다. 기본값은 false이다.

  • precision
@Column(precision = 5, scale = 2)
private BigDecimal amount;

precision 옵션은 해당 필드가 소수점을 포함한 최대 자릿수를 지정한다. 위 예시에서는 소수점을 포함한 최대 5자리까지 저장할 수 있다. 소수점 이하 자릿수는 scale 옵션으로 지정할 수 있다.

  • scale
@Column(precision = 5, scale = 2)
private BigDecimal amount;

scale 옵션은 해당 필드가 소수점 이하 자릿수를 지정한다. 위 예시에서는 소수점 이하 최대 2자리까지 저장할 수 있다. precision 옵션과 함께 사용하여 BigDecimal과 같은 숫자 타입에서 정밀도를 제어할 수 있다.

  • insertable, updatable
@Column(insertable = false, updatable = false)
private Date createdDate;

insertable 옵션은 해당 필드가 데이터베이스에 삽입될 때 값을 지정할 수 있는지 여부를 지정한다. 위 예시에서는 false로 지정하였기 때문에 createdDate 값은 데이터베이스에 삽입되지 않는다.
updatable 옵션은 해당 필드가 데이터베이스에서 업데이트될 때 값을 지정할 수 있는지 여부를 지정한다. 위 예시에서는 false로 지정하였기 때문에 createdDate 값은 데이터베이스에서 업데이트되지 않는다.

  • columnDefinition
@Column(columnDefinition = "TEXT")
private String description;

해당 필드가 매핑되는 데이터베이스 열의 데이터 타입을 직접 지정할 수 있다. 위 예시에서는 "TEXT"로 지정하였기 때문에 description 필드는 TEXT 타입의 데이터베이스 열에 매핑된다. 이 옵션을 사용하면 JPA가 자동으로 데이터베이스 열의 데이터 타입을 결정하지 않고 직접 지정할 수 있다.


7. @Transient

@Transient 어노테이션은 엔티티와 매핑하지 않을 필드를 표시할때 사용된다. 즉, 데이터베이스 테이블과 매핑되지 않는 일시적인 필드를 정의할 때 사용된다.


8. @OneToMany, @ManyToOne, @OneToOne, @ManyToMany

JPA에서는 엔티티 클래스에서 다른 엔티티 클래스와의 관계를 표현하기 위해 다양한 어노테이션이 제공된다.

1) @OneToMany

엔티티 간의 일대다 관계를 표현하기 위해 사용되는 어노테이션이다. 즉, 한 엔티티가 여러 개의 다른 엔티티와 관계를 가질 수 있는 경우 사용된다.

예를 들어, 하나의 게시글은 여러 개의 댓글을 가질 수 있으므로, 게시글 엔티티에서 댓글 엔티티와 일대다 관계를 가지는 경우 @OneToMany 어노테이션을 사용한다.

@OneToMany 어노테이션은 다음과 같은 속성을 가진다.

  • mappedBy
    일대다 관계에서 일쪽에 해당하는 엔티티에서만 사용한다.
    다쪽 엔티티에서는 @ManyToOne 어노테이션을 사용해 매핑한다.
    mappedBy 속성에는 다쪽 엔티티에서 매핑할 필드명을 지정한다.

  • cascade
    엔티티 간의 관계에서 연관된 엔티티들의 상태 변화를 전파할 지를 결정한다.
    CascadeType.ALL, CascadeType.PERSIST 등 다양한 옵션이 있다.

  • fetch
    연관된 엔티티를 어떻게 가져올 지를 결정한다.
    FetchType.EAGER, FetchType.LAZY 등 다양한 옵션이 있다.

  • orphanRemoval
    일대다 관계에서 일쪽 엔티티에서 다쪽 엔티티를 삭제할 때, 다쪽 엔티티를 자동으로 삭제할 지를 결정한다.

@OneToMany 어노테이션을 사용하면, 연관된 엔티티를 컬렉션으로 저장할 수 있다. 이를 통해, 객체지향적인 설계를 할 수 있다.

2) @ManyToOne

JPA에서 다대일 관계를 표현하기 위해 사용되는 어노테이션이다. 다수의 엔티티가 하나의 엔티티와 관계를 가질 수 있는 경우 사용된다.

예를 들어, 하나의 게시글은 여러 개의 댓글을 가질 수 있으므로, 댓글 엔티티에서 게시글 엔티티와 다대일 관계를 가지는 경우 @ManyToOne 어노테이션을 사용한다.

@ManyToOne 어노테이션은 다음과 같은 속성을 가진다.

  • fetch
    연관된 엔티티를 어떻게 가져올 지를 결정한다.
    FetchType.EAGER, FetchType.LAZY 등 다양한 옵션이 있다.

  • optional
    연관된 엔티티가 없을 때 어떻게 처리할 지를 결정한다.
    false로 설정하면, 반드시 연관된 엔티티가 있어야 한다.

  • targetEntity
    연관된 엔티티의 클래스를 지정한다.
    기본적으로는 필드의 타입에서 추론한다.

  • cascade
    엔티티 간의 관계에서 연관된 엔티티들의 상태 변화를 전파할 지를 결정한다.
    CascadeType.ALL, CascadeType.PERSIST 등 다양한 옵션이 있다.

@ManyToOne 어노테이션을 사용하면, 다수의 엔티티가 하나의 엔티티를 참조할 수 있습니다. 이를 통해, 객체지향적인 설계를 할 수 있다.

3) @OneToOne

JPA에서 일대일 관계를 표현하기 위해 사용되는 어노테이션이다. 두 개의 엔티티가 단 하나의 관계만 가질 수 있는 경우 사용된다.

예를 들어, 회원 엔티티와 회원 프로필 엔티티 사이에는 일대일 관계가 있을 수 있으므로, 회원 엔티티에서 회원 프로필 엔티티와 일대일 관계를 가지는 경우 @OneToOne 어노테이션을 사용한다.

@OneToOne 어노테이션은 다음과 같은 속성을 가진다.

  • mappedBy
    일대일 관계에서 양쪽 모두에서 사용할 수 있다.
    반대쪽 엔티티에서의 필드명을 지정한다.

  • cascade
    엔티티 간의 관계에서 연관된 엔티티들의 상태 변화를 전파할 지를 결정한다.
    CascadeType.ALL, CascadeType.PERSIST 등 다양한 옵션이 있다.

  • fetch
    연관된 엔티티를 어떻게 가져올 지를 결정한다.
    FetchType.EAGER, FetchType.LAZY 등 다양한 옵션이 있다.

  • optional
    연관된 엔티티가 없을 때 어떻게 처리할 지를 결정한다.
    false로 설정하면, 반드시 연관된 엔티티가 있어야 한다.

@OneToOne 어노테이션을 사용하면, 두 개의 엔티티가 단 하나의 관계만을 가지므로, 서로를 참조할 수 있다. 이를 통해, 객체지향적인 설계를 할 수 있다.

4) @ManyToMany

JPA에서 다대다 관계를 표현하기 위해 사용되는 어노테이션이다. 다수의 엔티티가 서로 다수의 엔티티와 관계를 가질 수 있는 경우 사용된다.

예를 들어, 한 명의 사용자는 여러 개의 책을 대여할 수 있고, 한 권의 책은 여러 명의 사용자에게 대여될 수 있으므로, 사용자 엔티티와 책 엔티티 사이에는 다대다 관계가 있을 수 있다.

@ManyToMany 어노테이션은 다음과 같은 속성을 가진다.

  • mappedBy
    다대다 관계에서 양쪽 모두에서 사용할 수 있다.
    반대쪽 엔티티에서의 필드명을 지정한다.

  • cascade
    엔티티 간의 관계에서 연관된 엔티티들의 상태 변화를 전파할 지를 결정한다.
    CascadeType.ALL, CascadeType.PERSIST 등 다양한 옵션이 있다.

  • fetch
    연관된 엔티티를 어떻게 가져올 지를 결정한다.
    FetchType.EAGER, FetchType.LAZY 등 다양한 옵션이 있다.

  • targetEntity
    연관된 엔티티의 클래스를 지정한다.
    기본적으로는 관계 테이블에서 추론한다.

@ManyToMany 어노테이션을 사용하면, 다수의 엔티티가 서로 다수의 엔티티와 관계를 가질 수 있다. 이를 통해, 객체지향적인 설계를 할 수 있다. 하지만, 실제 DB에서는 연결 테이블을 사용하여 다대다 관계를 표현한다. 따라서 @ManyToMany 어노테이션을 사용하는 경우, 관계를 표현하기 위한 연결 테이블을 별도로 생성해야 한다.


9. @JoinColumn, @JoinTable

1) @JoinColumn

엔티티 간의 관계를 맺는 데 사용되는 어노테이션 중 하나이다. 이 어노테이션은 @ManyToOne, @OneToOne, @OneToMany, @ManyToMany 어노테이션과 함께 사용되며, 외래키(Foreign Key)를 지정할 때 사용된다.

@JoinColumn 어노테이션은 다음과 같은 속성을 가진다.

  • name
    외래키 컬럼의 이름을 지정. 기본값은 현재 엔티티와 연관된 엔티티의 기본 키 컬럼 이름을 사용한다.

  • referencedColumnName
    현재 엔티티와 연관된 엔티티에서 참조할 컬럼 이름을 지정한다.
    기본값은 참조하는 엔티티의 기본 키 컬럼 이름을 사용한다.

  • nullable
    외래키가 null 값을 허용할지 여부를 지정한다. 기본값은 true.

  • unique
    외래키가 유일한 값을 가져야 하는지 여부를 지정한다. 기본값은 false.

  • columnDefinition
    데이터베이스 컬럼에 대한 SQL 정의를 지정한다. 기본값은 사용하지 않는다.

@Entity
public class Order {
    @Id
    private Long id;

    @ManyToOne
    @JoinColumn(name = "customer_id")
    private Customer customer;

    // ...
}

위의 코드에서, @ManyToOne 어노테이션과 함께 @JoinColumn 어노테이션을 사용하여, Order 엔티티와 Customer 엔티티 간의 관계를 맺고 있다. 이때, @JoinColumn 어노테이션에서 name 속성을 사용하여, 외래키 컬럼의 이름을 "customer_id"로 지정하였다. 이를 통해, Order 엔티티와 Customer 엔티티 사이에 외래키 관계를 맺을 수 있다.

2) @JoinTable

다대다(N:M) 관계에서 사용되며, 연결 테이블(Join Table)을 생성하는 데 사용된다.

예를 들어, 학생과 과목 간에 다대다 관계가 있을 때, 학생과 과목을 연결하는 연결 테이블이 필요하다. 이때, @JoinTable 어노테이션을 사용하여 연결 테이블을 생성할 수 있다.

@JoinTable 어노테이션은 다음과 같은 속성을 가집니다.

  • name
    연결 테이블의 이름을 지정.

  • joinColumns
    현재 엔티티에서 외래 키(Foreign Key)가 지정되는 컬럼을 지정.
    @JoinColumn 어노테이션과 동일한 속성을 가진다.

  • inverseJoinColumns
    반대쪽 엔티티에서 외래 키(Foreign Key)가 지정되는 컬럼을 지정.
    @JoinColumn 어노테이션과 동일한 속성을 가진다.

  • uniqueConstraints
    연결 테이블에 대한 유니크 제약 조건을 지정한다.

@Entity
public class Student {

    @Id
    private Long id;
    
    @ManyToMany
    @JoinTable(name = "student_course",
    		   joinColumns = @JoinColumn(name = "student_id"),
        	   inverseJoinColumns = @JoinColumn(name = "course_id")
	)
    private Set<Course> courses = new HashSet<>();
    
    // ...
}

@Entity
public class Course {

    @Id
    private Long id;
    
    @ManyToMany(mappedBy = "courses")
    private Set<Student> students = new HashSet<>();
    
    // ...
}

위의 코드에서, Student 엔티티와 Course 엔티티 간의 다대다(N:M) 관계를 맺고 있다. 이때, @JoinTable 어노테이션을 사용하여, 연결 테이블의 이름을 "student_course"로 지정하였다. 또한, joinColumns 속성과 inverseJoinColumns 속성을 사용하여, 각각 "student_id"와 "course_id"라는 컬럼을 외래 키로 지정하였다. 이를 통해, Student 엔티티와 Course 엔티티 사이에 다대다 관계를 맺을 수 있다.


10. @Enumerated

엔티티 클래스에서 enum 타입의 필드를 매핑할 때 사용된다.

enum 타입을 매핑할 때, JPA는 enum의 값을 데이터베이스에 저장한다. 이때, @Enumerated 어노테이션을 사용하여, enum의 값을 어떻게 데이터베이스에 저장할 것인지 지정할 수 있다.

@Enumerated 어노테이션은 다음과 같은 속성을 가진다.

  • EnumType.STRING
    enum의 문자열 값을 데이터베이스에 저장한다.
@Entity
public class Student {

    @Id
    private Long id;
    
    @Enumerated(EnumType.STRING)
    private Gender gender;
    
    // ...
}

public enum Gender {
    MALE, FEMALE
}

위의 코드에서, Student 엔티티 클래스에는 Gender enum 타입의 필드가 있다. 이때, @Enumerated(EnumType.STRING) 어노테이션을 사용하여, Gender enum의 값을 문자열로 데이터베이스에 저장하도록 지정하였다.

  • EnumType.ORDINAL
    enum의 순서 값을 데이터베이스에 저장한다.
@Entity
public class Student {

    @Id
    private Long id;
    
    @Enumerated(EnumType.ORDINAL)
    private Gender gender;
    
    // ...
}

public enum Gender {
    MALE, FEMALE
}

위의 코드에서, @Enumerated(EnumType.ORDINAL) 어노테이션을 사용하여, Gender enum의 값을 순서 값으로 데이터베이스에 저장하도록 지정하였다.

EnumType.STRING을 사용하는 경우, 데이터베이스에 저장되는 문자열 값은 enum의 name() 메소드를 사용하여 얻을 수 있다.
EnumType.ORDINAL을 사용하는 경우, 데이터베이스에 저장되는 순서 값은 enum의 ordinal() 메소드를 사용하여 얻을 수 있다.

하지만, EnumType.ORDINAL을 사용하는 경우에는 다음과 같은 문제점이 있다.

enum 타입에 새로운 값이 추가되거나, 기존의 값이 삭제되면 데이터베이스에 저장된 값의 의미가 변경된다.
enum의 순서 값을 변경하면 데이터베이스에 저장된 값의 의미가 변경된다.
따라서, EnumType.STRING을 사용하는 것이 EnumType.ORDINAL을 사용하는 것보다 안전하다.

0개의 댓글