@ID
만 사용
@GeneratedValue
추가
@Entity
public class Member {
@Id
@GeneratedValuse(strategy = GenerationType.IDENTITY)
private Long id;
...
JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL을 실행한다.
IDENTITY 전략의 Id 값은 DB에 insert 쿼리로 데이터가 추가되어야 알 수 있다.
But, 영속상태인 Entity의 ID값을 알 수 없다.
(아직 DB에 insert 쿼리가 나가지 않았기 때문)
=> 울며 겨자먹기로, em.persist(member)
시점에 insert 쿼리가 날아간다.
(IDENTITY 전략을 이용할 때는 쿼리를 버퍼처럼 모아서 한번에 날리는 것이 불가능하다.)
Member member = new Member();
System.out.println("====================");
em.persist(member);
System.out.println("member.id = " + member.getId());
System.out.println("====================");
====================
Hibernate:
/* insert hellojpa.Member
*/ insert
into
Member
(id, name)
values
(null, ?)
member.id = 1
====================
persist()
시점에 insert 쿼리가 날아가는 것을 확인 할 수 있다.
member.getId()
조회 시점에는 select 쿼리가 날아가지 않는다.
(JDBC 드라이버에 insert 쿼리 직후 바로 리턴 받을 수 있는 기능이 있기 때문이다.)
@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;
데이터베이스에 "MEMBER_SEQ"라는 시퀀스를 생성 후 이용한다.
Member member = new Member();
System.out.println("====================");
em.persist(member);
System.out.println("member.id = " + member.getId());
System.out.println("====================");
Hibernate: create sequence MEMBER_SEQ start with 1 increment by 1
====================
Hibernate:
call next value for MEMBER_SEQ
member.id = 1
====================
Hibernate:
/* insert hellojpa.Member
*/ insert
into
Member
(id, name)
values
(null, ?)
IDENTITY 전략과 상당히 다른 것을 확인할 수 있다.
먼저 DB에 MEMBER_SEQ
시퀀스를 생성한다.
DB에 존재하는 MEMBER_SEQ
시퀀스에서 PK를 가져온다.
-> em.persist()
가 호출될 때 객체는 PK가 존재해야한다.
이 시점에 IDENTITY 전략은 DB에 쿼리를 날려 데이터를 추가하고 PK를 가져온다.
하지만 SEQUENCE 전략은 DB에 있는 시퀀스에서 PK를 가져오기만 하면 된다.
따라서em.persist()
시점에insert
쿼리가 나가지 않는 것이 차이점이다.
tx.commit()
시점에 insert 쿼리가 나가는 것을 확인할 수 있다.
하지만 이렇게 매번 시퀀스를 받기 위해 DB와 통신하는 것이 부담스러울 수 있다. 이때 allocationSize을 사용한다.
allocationSize
만약 allocationSize를 50으로 설정했다면
Hibernate:
call next value for MEMBER_SEQ
Hibernate:
call next value for MEMBER_SEQ
시퀀스를 두번 호출한다. 왜 시퀀스를 두번 호출하는 것일까?
시퀀스 생성 직후 현재값 : -49, 증가 : 50으로 설정 되어있다.
이후, 시퀀스가 호출이 되면 현재값 : 1, 증가 : 50으로 설정된다.
이후, 시퀀스가 다시 한번 호출이 되면 현재값 : 51, 증가 : 50으로 설정된다.
-> 우리가 allocationSize를 50으로 설정했기 때문에, 최초에 Id값으로 1을 가져오고
크기 50만큼 데이터에 미리 불러온 후, 현재 값을 51로 설정하는 것이다.
3개의 객체를 em.persist()
한다고 생각해보자.
em.persist(member1) // DB SEQ = 1 // 이 시점에 SEQUENCE 두번 호출 // ID : 1
em.persist(member2) // DB SEQ = 51 // MEMORY 호출 // ID : 2
em.persist(member3) // DB SEQ = 51 // MEMORY 호출 // ID : 3
장점 : 모든 데이터베이스에 적용 가능
단점 : 테이블이 따로 생기기 때문에 락이 걸리는 등 성능이 좋지 않다.
@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;
Hibernate :
create table MY_SEQUENCES (
sequence_name varchar(255) not null,
next_val bigint,
primary key ( sequence_name )
)
코드 실행 시점에 SEQUENCE
테이블을 생성하는 것을 확인할 수 있다.
allocationSize
SEQUENCE
전략과 같은 기능을 한다.
메모리에 미리 값을 올려두는 방식이기 때문에, 여러 서버에서 동시 호출해도 동시성 문제가 없다.
기본 키(PK) 제약 조건: null이 아니면서, 유일하고, 변하면 안된다.
미래까지 이 조건을 만족하는 자연키는 찾기 어렵다. ex) 주민번호, 전화번호 ...etc
-> 대리키를 이용하자
권장: Long형 + 대체키 + 키 생성전략 사용
참고 :
김영한. 『자바 ORM 표준 JPA 프로그래밍』. 에이콘, 2015.