1. 기본 키 매핑 어노테이션
- @Id
- @GeneratedValue
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
기본 키 매핑 방법
- 직접 할당 : @Id만 사용
- 자동 생성(@GeneratedValue)
- IDENTITY : 데이터베이스에 위임, MySQL
- SEQUENCE : 데이터베이스 시퀀스 오브젝트 사용. @SequenceGenerator 필요
- TABLE : 키 생성용 테이블 사용. 모든 DB에서 사용 가능. @TableGenerator 필요
- AUTO : 방언에 따라 자동 지정. 기본값
2. IDENTITY 전략 - 특징
- 기본 키 생성을 데이터베이스에 위임
- 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용 (예: MySQL의 AUTO_ INCREMENT)
- JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL 실행
- AUTO_ INCREMENT는 데이터베이스에 INSERT SQL을 실행 한 이후에 ID 값을 알 수 있음
- IDENTITY 전략은 em.persist() 시점에 즉시 INSERT SQL 실행 하고 DB에서 식별자를 조회
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
IDENTITY 전략에서 애매한게 있습니다. 내가 이 아이디에 값을 넣으면 안되고 DB에 INSERT를 합니다. NULL로 넘어오면 DB에서 그때 값을 세팅해줍니다. ID값을 알 수 있는 시점이 DB에 들어가야 알 수 있습니다.
하지만, 영속성 컨텍스트에서 관리하려면 PK값이 무조건 있어야합니다. 그래서 JPA입장에서는 키가 없으니까 값을 넣을 방법이 없습니다. 어쩔 수 없이 IDENTITY 전략에서만 예외적으로 em.persist를 호출하자마자 바로 DB에 INSERT 쿼리를 커밋 전에 날려버립니다.
JDBC 드라이버에 INSERT 쿼리를 했을때 바로 return을 받는게 내부적으로 짜여져있어서, 따로 select 쿼리가 날라가거나 하지 않습니다. 모아서 INSERT하는게 IDENTITY 전략에서는 불가능합니다. 하지만 버퍼링해서 INSERT하는게 비약적으로 성능에 차이가 있지는 않습니다.
3. SEQUENCE 전략 - 특징
- 데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트(예: 오라클 시퀀스)
- 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용
@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 사용시 Long형 데이터를 사용해야합니다. Integer는 10억쯤 넘어가면 한바퀴돕니다. 결론은 Long을 써야합니다. 공간이야 2배는 차이나겠지만, 어플리케이션 전체로 봤을때는 그렇게 크게 영향이 많지 않습니다. 10억이 넘어갈때 Integer을 Long으로 바꾸는게 더힘듭니다.
하이버네이트가 만드는 기본 시퀀스. 테이블 마다 다른 시퀀스를 사용하고 싶으면 @SequenceGenerator을 사용하면 됩니다. 그리고 원하는 Sequence로 동작하게 매핑을 컬럼에 걸어줍니다. Sequence Object가 보통 1부터 시작해서 1씩 증가시킵니다.
[SEQUENCE 전략과 최적화]
memberId를 구할 때 데이터베이스에 call next value for MEMBER_SEQ 날라갑니다. Sequence 전략이면 db에서 값 받아와서 member에 넣어주고 영속성 컨텍스트에 저장해줍니다. 아직 DB에 INSERT는 안날라갔습니다. 실제 트랜잭션 커밋하는 시점에 INSERT 쿼리가 호출하게됩니다. 즉, Sequence 전략은 버퍼링이 가능한것입니다.
성능에 대해 고민하게되면 네트워크를 왔다갔다 해야하는데 차라리 INSERT 쿼리를 한번 날리지 하고 고민할 수 있습니다. 그래서 JPA에는 allocationSize를 가지고 성능을 향상시킬 수 있습니다. allocationSize는 기본값으로 50으로 되있습니다. 미리 50개를 땡기는 것입니다. next call한번할 때 50개 사이즈를 DB에 올려놓고 나는 메모리에서 1씩 사용하는 것 입니다. 그리고 next call 또오면 db는 100번으로 가는 것입니다. 이게 동시성 이슈 없이 다양한 이슈들이 해결됩니다.
1을 만나면 50번까지 메모리에 확보. DB에 50으로 해두고, 그리고 50을 만나면 또 next call 호출. 51번~100 메모리 올리고 db에는 100번까지로 해둡니다. 이론적으로는 이 값을 충분히 늘리면 좋긴한데, 웹서버를 내리는 시점에 날라가고 구멍이 생겨도 상관이 없지만, 50이나 100정도가 적정한 것 같습니다.
4. Table 전략
- 키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략
- 장점: 모든 데이터베이스에 적용 가능
- 단점: 테이블을 직접 사용하기 때문에 Lock도 걸릴수도있고, 성능의 이슈가 있음. Sequence는 숫자 뽑는데 최적화가되있는데 Table은 아님.
create table MY_SEQUENCES (
sequence_name varchar(255) not null,
next_val bigint,
primary key ( sequence_name )
)
@Entity
@TableGenerator(
name = "MEMBER_SEQ_GENERATOR",
table = "MY_SEQUENCES",
pkColumnValue = “MEMBER_SEQ",
allocationSize = 1)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "MEMBER_SEQ_GENERATOR")
private Long id;
}
Sequence와 비슷하게 사용합니다. 하지만 운영에서는 Sequence를 사용하는 것을 권장합니다.
@TableGenerator - 속성
5. 권장하는 식별자 전략
- 기본 키 제약 조건: null 아님, 유일, 변하면 안된다.
- 미래까지 이 조건을 만족하는 자연키는 찾기 어렵다. 대리키(대 체키)를 사용하자.
- 예를 들어 주민등록번호도 기본 키로 적절하기 않다.
- 권장: Long형 + 대체키 + 키 생성전략 사용
권장하는 식별자 전략은 기본 키 제약 조건은 null아니고, 유일, 변하면 안됩니다. 변하면 안된다는건 어렵습니다. 문제는 미래까지 이 조건을 만족하는 자연키를 찾기가 어렵다는 것 입니다. -> 자연키(주민번호 같은거)
자연키는 찾기가 어려우고 대리키를 사용함. 비즈니스와 상관없이 사용합니다. 나라에서 주민번호 보관하면 안된다고 했을 때 회원테이블의 주민번호를 pk로 쓰고있다면, PK만 문제가 아니라 그 회원을 가져다가 쓰는 나머지도 문제가 됩니다.
REFERENCE
https://www.inflearn.com/course/ORM-JPA-Basic
'JPA' 카테고리의 다른 글
[JPA] 연관 관계 매핑의 종류 (0) | 2020.04.19 |
---|---|
[JPA] 연관관계 매핑 기초 (0) | 2020.04.14 |
[JPA] 객체와 테이블 매핑 (0) | 2020.04.10 |
[JPA] 영속성 콘텍스트 (0) | 2020.04.10 |
[JPA] 관계형 데이터베이스의 문제 (1) | 2020.04.08 |