0. 개요
- 이전 포스팅에서 Field와 Column을 매핑하는 방법을 배웠다.
- 이번 포스팅에서는 DB에 존재하는 기본 키(= Primary key, PK)를 매핑하는 방법에 대해서 배워보자.
1. Annotation
- PK 매핑에 필요한 Annotation을 알아보자.
a) @Id
- DB의 PK를 명시하기 위해서, Entity의 field에 @Id를 부착한다.
- @Id는 PK의 값을 직접 할당하는 경우에 사용한다.
@Entity
public class Member {
@Id
private Long id;
@Column(name = "name")
private String username;
}
b) @GeneratedValue
- PK의 값을 직접 할당하지 않고 DB에게 값 할당의 권한을 넘기는 경우 사용된다.
- 즉, DB가 자동으로 생성해주는 숫자를 사용한다.
- @GeneratedValue의 strategy 옵션은 다음과 같은 값을 설정할 수 있다.
→ IDENTITY
→ SEQUENCE
→ TABLE
→ AUTO(= default 설정)
- 모든 옵션은 persistence.xml에 설정한 DB 방언에 따라 알맞은 SQL로 변환되어 적용된다.
- 각 strategy에 대해서 알아보자.
2. GenerationType.IDENTITY
- PK 값의 생성을 DB에게 위임한다.
- JPA에서 id 필드의 값에 아무것도 입력하지 않고 그냥 null로 넘기면, DB에서 PK 값을 생성 및 할당한다.
- 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용한다. ex) MySQL의 AUTO_INCREMENT
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
a) IDENTITY 전략의 문제점
- IDENTITY를 사용하면, JPA에서 PK의 값을 Null로 넘긴다. 그러면 DB에서 PK 값을 생성하고 할당한다.
- 즉, DB에 값을 주입하기 전까지 JPA의 Persistence Context는 입력된 PK 값을 알 수 없다.
- 이렇게 되면 Persistence Context의 1차 캐시를 사용할 수 없게 된다.
- 어떻게 해야 할까?
b) IDENTITY 문제의 해결책
- IDENTITY를 사용하면 JPA에서 쿼리를 전달하는 시점이 달라진다.
- 기존의 동작 방식은 commit() 시점에서 쓰기 지연 SQL 저장소에 보관된 SQL을 일괄 처리했다.
- 그러나 IDENTITY를 사용하면 em.persist()를 호출할 때 DB에게 쿼리를 전달한다.
- DB에 데이터를 먼저 입력시킨 다음, JPA에서 SELECT 쿼리를 전달하여 해당 PK의 값을 가져온다.
- IDENTITY를 사용하면 이와 같은 동작 흐름을 가지게 된다.
3. GenerationType.SEQUENCE
- SEQUENCE는 unique 한 값을 순서대로 생성하는 DB object이다.
- 시퀀스 값이 PK에 순차적으로 할당된다.
- SQUENCE를 사용할 때에는 반드시 Entity 필드의 자료형을 숫자형으로 사용해야 한다.
- 그중에서도 반드시 Long을 사용하는 것을 권장한다. (표현 가능한 숫자의 범위가 가장 크기 때문이다.)
- 주로 Oracle에서 사용하는 방식이다.
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
}
a) @SequenceGenerator
- 만약 테이블마다 SEQUENCE를 분리하여 관리하고 싶다면, @SequenceGenerator를 사용하면 된다.
- SEQUENCE를 사용할 때에는 @SequenceGenerator를 통해 SEQUENCE를 설정할 수 있다.
→ name 속성(필수 값)
- @SequenceGenerator의 이름을 설정한다.
→ sequenceName 속성(default = hibernate_sequences)
- 매핑할 DB의 SEQUENCE 객체의 이름을 설정한다.
→ initialValue 속성(defualt = 1)
- 초기 값(= 시작 값)을 설정한다.
→ allocateSize 속성(default = 50)
- DB로부터 SEQUENCE 값을 가져올 때, 한 번에 몇 개의 SEQUENCE를 가져올 것인지 설정한다.
- JPA 메모리에 미리 SEQUENCE 값을 저장시켜놓고 사용한다.
- allocationSize를 1로 설정하면, em.persist()가 한번 호출될 때마다 한 개의 SEQUENCE를 DB로부터 받아온다.
- 그러므로 매번 em.persist()를 호출할 때마다 DB와의 네트워킹이 발생한다.
- 이와 같이 낭비되는 네트워킹을 방지하기 위해서 적정한 allocationSize를 사용한다.
* 다수의 서버가 동일한 DB를 사용하더라도, allocationSize로 인한 동시성 문제는 발생하지 않는다.
→ catalog와 schema 속성
- 두 옵션은 DB의 catalog와 schema 이름을 설정한다.
@Entity
@SequenceGenerator(name = "MEMBER_SEQ_GEN", sequenceName = "MEMBER_SEQ",
initialValue = 1, allocationSize = 1)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "MEMBER_SEQ_GEN")
private Long id;
}
- @GeneratedValue의 generator 옵션에는 사용할 시퀀스의 이름을 설정한다.
- 사용할 시퀀스의 이름이란, @SequenceGenerator의 name 값을 말한다.
b) SEQUENCE 호출 시점
- DB에 데이터를 저장하기 위해서는 em.persist()를 호출해야 한다.
- 그러나 SEQUENCE를 사용하기 때문에, JPA는 PK 값을 알지 못한다.
- 그러므로 em.persist()를 호출되면 JPA가 내부적으로 DB로부터 SEQUENCE 값을 가져온다.
4. GenerationType.TABLE
- key 생성을 위한 전용 테이블을 만드는 기능이다.
- 모든 DB에 적용할 수 있다는 것이 장점이다.
- 숫자를 생성하는 측면에서 최적화가 되어있지 않기 때문에 성능이 떨어진다는 단점이 있다.
- 그러므로 운영 서버에서는 잘 사용하지 않는 전략이다.
a) @TableGenerator
- TABLE 전략을 사용할 때에는 @TableGenerator와 함께 사용할 수 있다.
- @TableGenerator를 사용하면 key 생성을 위한 테이블을 설정할 수 있다.
→ name 속성(필수 값)
- @TableGenerator의 이름을 설정한다.
→ table 속성(default = hibernate_sequences)
- DB의 key 생성 테이블 이름을 설정한다.
→ pkColumnName 속성(default = sequence_name)
- 시퀀스 칼럼 이름을 설정한다.
→ valueColumnNa 속성(default = next_val)
- 시퀀스 값의 칼럼 이름을 설정한다.
→ pkColumnValue 속성(default = Entity 이름)
- key로 사용할 칼럼의 이름을 설정한다.
→ initialValue 속성(default = 0)
- 초기 값, 마지막으로 생성된 값을 기준으로 다음 값을 생성한다.
→ allocationSize 속성(default = 50)
- DB로부터 SEQUENCE 값을 가져올 때, 한 번에 몇 개의 SEQUENCE를 가져올 것인지 설정한다.
→ catalog, schema 속성
- DB의 catalog, schema 이름을 설정한다.
→ uniqueConstraints(DDL)
- 유니크 제약조건을 설정한다.
@Entity
@TableGenerator(
name = "MEMBER_SEQ_TAB",
table = "TABLE_SEQ",
pkColumnValue = "MEMBER_SEQ",
allocationSize = 1)
public class Member {
@Id
@GeneratedValue(
strategy = GenerationType.TABLE,
generator = "MEMBER_SEQ_TAB")
private Long id;
}
5. 권장하는 전략
- 여기까지 PK를 설정하고, 매핑하는 방법에 대해서 알아보았다.
- 그렇다면 IDENTITY, SEQUENCE, TABLE 중 무엇을 사용해야 할까?
- 사실 여기에 정해진 답은 존재하지 않는다. 다만, 다음의 요소를 고려해볼 필요가 있다.
a) PK 제약 조건
- PK는 null 값을 허용하지 않으며, 중복이 없어야 하고(= unique), 값이 변하면 안 된다.
- 이 3가지 조건을 만족해야지만 비로소 PK로 사용할 수 있다.
b) 불변성을 만족하는 방법
- PK 제약조건 중 가장 만족하기 어려운 것이 불변성이다.
- 존재하는 데이터의 PK는 서비스가 종료되기 전까지 변하면 안 되기 때문이다.
- 그러므로 PK는 비즈니스와 연관관계가 없는 랜덤 한 수를 사용하는 것을 권장한다.
→ PK = Long 타입 + 대체 key + 키 생성 전략 사용
'Back-end > JPA 개념' 카테고리의 다른 글
8. 양방향 연관 관계의 기본 개념 (0) | 2022.03.31 |
---|---|
7. 단방향 연관 관계 (0) | 2022.03.30 |
5. Field(칼럼) Mapping (0) | 2022.03.28 |
4. Entity Mapping (0) | 2022.03.25 |
3. JPA의 내부구조와 동작 (0) | 2022.03.24 |
댓글