기본 키 매핑과 관련한 어노테이션은 크게
@Id
/@GeneratedValue
가 있음
그 중,@GeneratedValue
와 관련한전략(strategy)
에 집중해서 알아보자1)
@Id
: 현재 필드가 기본키임을 알리기 위한 어노테이션
2)@GeneratedValue
: 기본키 값 생성에 관련된 어노테이션@Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id;
- 기본 키 값을 자동생성하는 어노테이션으로 여러가지 전략이 있다.
IDENTITY
: DB에 위임SEQUENCE
: DB의 시퀀스 오브젝트를 사용TABLE
: 키 생성용 테이블을 사용AUTO
: 방언에 따라 자동 지정 (default 전략이다)
개념
- 주로 MySQL 사용시 선택하는 전략
- DB에게 알아서 증가하도록 위임하는 방법
(MySQL의auto increment
수행함)public class Member { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; }
:
MySQL
로 실행시,auto_increment
속성이 적용된다
주의할 점 (매우 중요)
- JPA는 영속성 컨텍스트를 통해
transaction.commit
시점에 SQL이 실행
--> DB가 알아서 기본키 값을 증가시키려면 현재 어디까지 번호가 발급되었는지 알아야하는데 JPA가 이렇게 동작하면 알 수가 없다!!- 그래서 JPA에서
IDENTITY
전략을 사용하면 영속화와 동시에 SQL이 실행되도록 설정되어있다!! (예외처리됨)em.persist(member)
하면 바로 SQL 수행되고 DB 식별자 번호를 반환해서 다음 번호로 영속성 컨텍스트에 저장된다!
개념
- DB의
시퀀스 오브젝트
를 사용한다!- DB
시퀀스
란 ?
: 유일한 값을 순서대로 생성하는 특별한 DB 오브젝트Oracle
등에서 사용됨
사용 순서
@Entity /* SequenceGenerator로 Sequence를 생성 */ @SequenceGenerator( name = "MEMBER_SEQ_GENERATOR", sequenceName = "MEMBER_SEQ", // 매핑할 데이터베이스 시퀀스 이름 initialValue = 1, allocationSize = 1) public class Member { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBER_SEQ_GENERATOR") // 만든 SequenceGenerator 연결 private Long id; }
1.
@SequenceGenerator
선언
: 제너레이터 이름 / 시퀀스 이름 / 속성 값 을 설정
2.@GeneratedValue(...)
지정
: 전략(strategy)를 SEQUENCE / generator를 위에서 만든 시퀀스 제너레이터 연결
동작 원리
em.persist(Member)
로 영속화되어 등록될 때sequence number
가 몇까지 나왔는지 모르기 때문에sequence call
을 수행sequence call
로 알아온number
를 통해 pk로 등록 후 영속성 컨텍스트에 등록
주의할 점
- 매번
em.persist()
할 때마다sequence call
을 하면 비효율적이지 않을까? 하는 의문이 든다- 그래서 제공되는 옵션이
allocationSize
이다
- 어떻게 ?
- 기본값인 50이 가지는 의미는 50번호만큼 미리 메모리에 할당받아 두겠다는 것
- 미리 50까지는 메모리에 저장해서 증가시키기 때문에 DB의
sequence number
는 51이 등록되어 있을 것임- 50번의 번호가 가득 찼을 때 다음
sequence number
을 알아내는sequence call
이 수행되어 나름 효율적인 구조를 가지게 된다
개념
- 키를 생성하고 관리하는 테이블을 하나 만들어서 DB 시퀀스를 흉내
- 모든 DB에 대해 적용 가능
- 성능이 좋지 못해 실무에서 잘 사용되지는 않는다
- 시퀀스처럼 동작하기 때문에 역시
영속성 컨텍스트
문제를 겪게 되고,
시퀀스와 동일하게allocationSize
옵션으로 동작된다- 사용 예시
@Entity @SequenceGenerator( 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; }
- Default로 설정되는 값
- 방언에 따라 위의 3가지 전략을 자동으로 지정
(MySQL인지 / Oracle인지 등등)
설명
- 기본키 제약조건을 만족
- not null
- 유일성과 최소성
- 변하면 안된다
- 미래까지 조건들을 만족하는 자연키는 찾기가 어렵다
--> 대체키를 사용하자
- 자연키(natural key) ?
: 비즈니스적으로 의미가 있는 키
(전화번호, 주민번호)- 대체키(Generate Value) ?
: 비즈니스적으로 상관없는 키 (랜덤값 등등)ex) 주민번호가 기본키일 경우
: 갑자기 개인정보 보호목적으로 DB에 저장하지 못하게 된다면 관련된 모든 테이블을 수정해야 함
즉, 모든 테이블을 Migration 해야하는 끔찍한 상황!
정리
- 기본키 제약조건을 만족
- 권장하는 식별자 구성 전략
:(Long형) + (대체키) + (적절한 키생성 전략)
이용
Long
형을 사용해야 하는 이유
:int
는 0을 사용해서 null과 0의 구분이 애매해서 오류날 수 있음
Integer
는 10억이 넘어가면 순환해서 오류가 날 수 있음
Long
이 위 두 문제를 어느정도 해결하는 좋은 합의 지점임!- 대체키 사용
: 랜덤값 /UUID
등 비즈니스와 관계없는 값- 적절한 키 생성 전략
:IDENTITY
/SEQUENCE
추천