Enum 타입 매핑# @Enumerated
EnumType.ORIDINAL은 enum에 정의된 순서대로 ADMIN은 0, USER는 1 값이 데이터베이스에 저장되는 방식이다.
EnumType.STRING은 enum에 정의된 ADMIN 타입을 “ADMIN”, USER 타입을 “USER” 문자열로 저장하는 방식이다.
public class Member {
// 생략 ...
@Enumerated(EnumType.ORDINAL)
// or
@Enumerated(EnumType.STRING)
private RoleType roleType;
위 2가지 방식은 자바의 Enum 타입을 아주 간편하게 다룰 수 있는 어노테이션이다. 하지만, 문제가 있다.
첫 번째는, EnumType.ORDINAL 설정 후, 의도치 않게 enum 값의 순서가 바뀌는 경우가 생기면 데이터가 꼬이는 문제가 발생한다.
두 번째는, 언제든지 Enum Type의 값이 바뀔 수 있다는 것이다. 비즈니스의 변화로 USER를 NORMAL로 변경한다면 기존에 USER로 저장해놓은 데이터(Row)는 Enum Type에서 Mapping 할 수 있는 데이터를 찾을 수 없게 되는 문제가 발생한다.
이러한 문제들 때문에 실무에서는 @Enumerated를 거의 사용하지 않고 @Convert 방식으로 매핑한다.
Enum 타입 매핑# @Convert
Entity의 Enum 값을 DB에서 가져올 때 변환하는 방법과 반대로 DB에서 Entity의 데이터를 가져올 때의 변환하는 방법을 정의해서 객체과 테이블 간의 데이터 간극을 맞춰주는 방식이다.
방식은 AttributConverter 인터페이스를 구현한 Converter 클래스를 정의하고 적절하게 규칙을 정의해주면 된다. 그리고 Converter 클래스를 사용해서 변환할 Entity에 @Convert 어노테이션 선언해주면 된다.
class RoleTypeConverter implements AttributeConverter {
// 규칙 정의
// Entity to DB
// DB to Entity
}
public class Member {
// 생략 ...
@Convert(converter = RoleTypeConverter.class)
private RoleType roleType;
기본 키;Primary Key 매핑#1
public class Member {
@Id
private String id;
기본 키 생성 전략
직접 할당
자동 생성
public class Member {
@Id
@GeneratedValue(strategy = ?)
private String id;
IDENTITY 전략; 기본 키 생성을 DB에 위임한다.
Entity를 Insert 할 때, id 필드로 null 값으로 두고 저장하면 DB에 자동으로 값을 할당한다. 이러한 이유로 JPA는 기본 키 값을 얻어오기 위해서 DB를 추가로 조회한다.
그리고 Entity가 영속 상태가 되기 위해서는 식별자(ID)가 반드시 필요하다. 그렇기 때문에 해당하는 전략을 사용하는 경우, 지연 쓰기 연산이 아니고 즉시 쓰기 연산으로 DB에 데이터가 반영된다.
SEQUENCE 전략; DB의 Sequence를 사용해서 기본 키를 할당한다.
Entity클래스에 Seqence 설정하여 사용할 데이터베이스 시퀀스를 매핑한다.
내부 동작은 IDENTITY와 동일하지만, 다른 점 하나는 트랜잭션이 발생하면 데이터베이스 시퀀스에서 먼저 식별자(ID)를 조회해서 Entity에 할당 후, 영속성 컨텍스트에 저장한다는 것이다. 그렇기 때문에 시퀀스는 쓰기 지연으로 동작한다.
TABLE 전략; 키 생성 테이블을 사용한다.
키 생성 전용 테이블을 하나 만들고 여기에 이름(ID의 이름)과 값(Value)으로 사용할 컬럼을 만들어 DB 시퀀스를 흉내내는 전략이다.
시퀀스는 Oracle에서만 제공하는 기능이고 이와 유사하게 만들어진 기능이 TABLE 전략이다. 그래서 오라클이 아닌 다른 RDB를 사용하는 경우, 시퀀스와 같은 기능을 사용하고 싶다면 TABLE 전력을 사용하면 된다.
기본키 종류
자연 키; Natural Key
비즈니스와 관련 있는 의미 있는 키를 말한다. 예를 들어, 사용자 정보를 저장하는 테이블의 경우, 사용자 정보에서 유니크한 데이터를 키로 사용하는 케이스를 말한다. ex) 주민등록번호, 휴대 ㅏㅏ전화번호
대리 키; Surrogate Key
비즈니스와 전혀 관련 없는, 의미 없이 만들어 고유한 키를 말한다. ex) UUID, Timestamp + UUID
어떤 키가 더 좋을까?
우리가 항상 개발을 하면서 잊어버리면 안되는 사실 중 하나는 비즈니스 환경은 언제든 변할 수 있다는 것이다. 그렇기 때문에 우리는 변하는 것에 의존해서 개발을 하면 안된다는 것이다. PK도 마찬가지이다. 자연 키로 PK를 지정하는 경우, 변경될 가능성이 높다. 예를 들면, 주민번호를 PK로 지정해놓았으나, 보안 상의 이유로 주민 번호 수집이 금지 되었다고 가정해보자. 이 경우, 비즈니스 로직과 관련된 코드만 수정하는 것이 아니라, 데이터의 구조와 기존 데이터까지 수정해줘야하는 아주 Fxxk 같은 경우를 마주하게 된다.
그렇기 때문에 자연 키보다는 대리 키를 사용하는 것이 유지보수 측면에서 있어서 효율적이고 효과적이다.
날짜 타입 매핑# @Temporal
날짜 타입을 매핑할 때 사용한다. @Temporal 어노테이션을 생략하면 데이터는 자바의 Date와 가장 유사한 timestamp로 정의된다. 하지만, timestamp 대신에 datetime을 예약어로 사용하는 DB도 있는데 DB 방언 덕분에 application 코드에서 변경하지 않아도 된다.
@Temporal(TemporalType.DATE)
private Date data;
// output; yyyy-MM-dd
@Temporal(TemporalType.TIME)
private Date time;
// output; 24hh:mm:ss
@Temporal(TemporalType.TIMESTAMP)
private Date timestamp;
// output; yyyy-MM-dd 24hh:mm:ss