identity전략은 DB에 값을 저장하고 나서야 기본 키 값을 구할 수 있습니다.
// IDENTITY 매핑 코드
@Entity
public class Member{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
}
.
.
.
private static void logic(EntityManager em) {
Member member = new Member();
em.persist(member);
System.out.println("member.id = " + member.getId());
}
// 출력: member.id = 1
이유는 엔티티가 영속 상태가 되기 위해서는 식별자가 필요합니다. IDENTITY 전략의 경우, 식별자 생성을 DB에 저장해야 얻을 수 있으므로 em.persist()
를 호출하여 객체를 영속성 상태로 만드는 순간 INSERT SQL이 호출
됩니다. 따라서 LAZY LOADING
못합니다.
DB 시퀀스는 유일한 값을 순서대로 생성하는 특별한 DB 오브젝트입니다. SEQUENCE 전력은 이 SEQUENCE를 사용해서
기본 키를 생성합니다.
// 시퀀스 DDL
CREATE TABLE MEMBER (
ID BIGINT NOT NULL PRIMARY KEY,
DATA VARCHAR(255)
)
// 시퀀스 생성
CREATE SEQUENCE MEMBER_SEQ START WITH 1 INCREMENT BY 1;
.
.
.
// 시퀀스 매핑 코드
@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;
}
em.persist()
를 호출 시, DB 시퀀스를 사용해서 식별자를 조회
합니다. 식별자를 엔티티에 할당한 후에 엔티티를 영속성 컨텍스트에 저장
합니다. 이 후 트랜잭션을 커밋해서 플러시가 일어났을 경우, 엔티티를 DB에 저장합니다.
IDENTITY과의 차이점은 IDENTITY의 경우 영속성 컨텍스트에 저장하기 위해 INSERT를 바로 날린다는 점입니다.
allocationSize
결론은 데이터베이스에 여러번 접근하는 것을 방지해서 성능적으로 장점이 있기 때문입니다.
SEQUENCE 전략은 데이터베이스 시퀀스를 통해 식별자를 조회하는 추가 작업이 필요합니다. 따라서 다음과 같이 데이터베이스와 2번의 통신을 합니다.
위와 같이 시퀀스에 접근하는 것을 줄이기 위해 JPA는 @SequenceGenerator.allocationSize를 사용합니다. 간단히 설명하면, 여기에 설정한 값만큼 한 번에 시퀀스 값을 증가시키고 나서 그만큼 메모리에 시퀀스 값을 할당합니다.
예를들어, allocation 값이 50이면 시퀀스를 한 번에 50을 증가시킨 다음에 1 ~ 50까지는 메모리에 시퀀스 값을 할당합니다. 그리고 51이 되면 시퀀스 값을 100으로 증가시킨 다음 51 ~ 100까지 메모리에서 식별자를 할당합니다.
이 최적화 방법은 시퀀스 값을 선점하므로 여러 JVM이 동시에 동작해도 기본 키 값이 충돌하지 않는 장점이 있습니다. 반면에 데이터베이스에 직접 접근해서 데이터를 등록할 때 시퀀스 값이 한 번에 많이 증가한다는 점을 염두해두어야 합니다. 이런 상황이 부담스럽고 INSERT 성능이 중요하지 않으면 allocationSize의 값을 1로 설정하면 됩니다.
GenerationType.IDENTITY
는 DB의 자동 증기 기능을 사용하여 주 키를 생성하는 방식이고, DB에 의존적입니다.
GenerationType.SEQUENCE
는 DB의 시퀀스를 사용하여 주 키를 생성하는 방식이고, DB에 의존적이지만 시퀀스의 사용을 명시적으로 지정할 수 있습니다.