본문 바로가기

JPA

기본키 매핑

기본키를 객체에 매핑할 때는 필드에 @Id를 붙여서 매핑한다.
이 때 @GeneratedValue라는 Annotation이 있으면 insert 쿼리에서 Id 컬럼의 값을 자동할당해준다.

값을 자동할당할 때에 몇 가지 전략이 있는데, 이에 대해 알아보자~~~

 

IDENTITY

package hellojpa;

import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;

@Getter
@Setter
@Entity
@Table(name = "MEMBER")
public class Member {
    @Id //PK라는 것을 명시해주는 Annotation; 필수
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "name", nullable = false)
    private String username;
}

@GeneratedValue의 strategy 값으로 IDENTITY를 전달한 것을 볼 수 있다.

IDENTITY 전략으로 설정하고 insert 쿼리를 살펴보면 Id 컬럼에 null을 날리는 것을 확인할 수 있다.

이는 Id 컬럼에 null을 날림으로써 기본키 생성을 DB에 위임하는 것으로 AUTO_INCREMENT가 설정된 열에 사용하면 된다. 
이 전략에는 한 가지 특이점이 있다.
영속화할 때(쉽게 생각하면 persist 메서드 호출) DB에 insert 쿼리를 날린 뒤 Id값을 받아와서 Entity를 영속화시킨다.
영속화를 할 경우 1차캐시에 Id와 함께 Entity 객체를 저장해야 하는데, DB에 값을 직접 넣어보는 것 말고는 Id값을 알 수가 없다.

(잘 모르겠으면 code-mania.tistory.com/12를 참고해보자~~~~)

그래서 ID 전략이 IDENTITY인 경우에는 commit을 하지 않아도 DB에 쿼리가 날아간다.

 

SEQUENCE

DB의 시퀀스 오브젝트를 사용하겠다는 전략이다.(아래 글을 이해하려면 DB의 시퀀스부터 공부해주세요)

package hellojpa;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;

@Getter
@Setter
@Entity
@Table(name = "MEMBER")
@SequenceGenerator(name="MEMBER_SEQ_GENERATOR", sequenceName = "MEMBER_SEQ", initialValue = 1, allocationSize = 50)
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBER_SEQ_GENERATOR")
    private Long id;
    @Column(name = "name", nullable = false)
    private String username;
}

 

이 전략은 @SequenceGenerator과 함께 사용해야 한다.

ddl mode가 create라는 가정 하에 SequenceGenerator가 있는 Entity는 해당 Generator에 맞게 Sequence를 생성해준다.

하지만 없는 Entity의 경우 다같이 하나의 Sequence 오브젝트를 공유하니 주의하도록 하자~~~

name은 generator의 name을 정해주는 옵션이고, sequenceName은 DB에 있는 Sequence 오브젝트의 이름을 적어주면 된다. 
또한 @GeneratedValue에서는 이렇게 설정된 name을 통해 generator를 참조한다.

 

Generator를 보면 정말 수상하게 생긴 initialValue와 allocationSize라는 아직 살펴보지 않은 옵션이 있다.

DB에서 sequence를 만들 때 몇부터 시작할지(START WITH) 정할 수 있는데, 그걸 정해주는 옵션이 initialValue이다.

ddl이 create모드가 아닐 경우 그냥 DB에 있는 시퀀스의 시작값이랑 맞춰주면 된다.

 

allocationSize는 몇 씩 증가할지(INCREMENT BY) 정하는 옵션이다.

이것도 ddl이 create모드가 아닐 경우 DB에 있는 값이랑 맞춰주자~~~

지금 allocationSize가 50으로 설정되어 있다.(기본값도 50이다.) 이 상태에서 영속화(persist)가 일어난다고 해보자!!!

위에서도 말했지만 영속화를 시키려면 키, 즉 @Id로 설정된 필드의 값이 필요하다. 

SEQUENCE 전략의 경우 @Id의 값을 가져오기 위해 nextval을 얻어오는 쿼리를 DB에 날리게 된다.

그런데 50씩 증가하니까 받아오는 값이 51이다...

(참고로 맨 처음에는 쿼리를 2번 날린다. 첫번째 nextval로 1을 얻고 나서 50보다 작아서 한 번 더 날린다고 한다.)

자 이러고 나서 2번째 영속화를 하는 상황이라고 가정하자!!!

그러면 nextval을 또 실행시키면??? 101이 나온다... 그러면 인덱스가 뒤죽박죽되어버린다. 최악이다.

그래서 2번째 영속화부터 51번째 영속화까지 nextval이 호출되지 않는다. 이 때는 메모리에서 인덱스를 계산해준다.

그리고 52번째 영속화에서 다시 nextval을 호출하고, 또 101번째 영속화까지는 메모리에서 계산해주고 이런 식이다.

이로 인해 persist할 때마다 nextval을 DB에 날리지 않아도 되므로 성능에서 이점을 볼 수 있다.

 

TABLE

키 생성 전용 테이블을 하나 만들어서 DB시퀀스처럼 작동시키는 전략이다.

모든 Db에 적용 가능하지만, table을 하나 더 만들어서 따로 관리하는 작업이다보니 성능이 좋지 않다.

package hellojpa;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;

@Getter
@Setter
@Entity
@Table(name = "MEMBER")
@TableGenerator(name="MEMBER_SEQ_GENERATOR", table="MY_SEQUENCES", pkColumnValue = "MEMBER_SEQ")
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "MEMBER_SEQ_GENERATOR")
    private Long id;
    @Column(name = "name", nullable = false)
    private String username;
}

 

@TableGenerator의 table과 pkColumnValue옵션만 빠르게 짚어보고 넘어가겠다.

table의 값으로 Sequence 관리 Table이 생성된다. 또한 그 Table에서 pkColumnValue로 설정된 값이 SEQUENCE_NAME에 들어가게 된다.
여기에도 allocationSize 옵션이 있어 SEQUENCE와 같은 메커니즘으로 동작한다.

그리고 이거 말고 웬만하면 IDENTITY랑 SEQUENCE 둘 중 하나 쓰자!!!(성능도 안 좋은데 뭐하러...)

 

생성되는 테이블 모습

 

AUTO - 적당히 알아서 JPA가 해준다는 옵션

 

기본키를 매핑할 때 자연키(비즈니스와 관련된 키)를 사용하는 것보다는

비즈니스와 전혀 상관없는 무의미한 일련번호를 키로 사용하는 것이 가장 좋다.

왜냐하면 실무에서 어떤 일이 일어날지 모르기때문이다.

실화) 주민등록번호가 PK였는데, 갑자기 주민등록번호를 보관하는 것이 법적으로 금지되면서
PK를 바꿨다고 한다. 이로 인한 여파는 말 안해도 감이 온다. 얼마나 끔찍할지....
그냥 일련번호를 PK로 사용하자!!!

 

 

참고강의: 배달의 민족 개발팀장 김영한 강사님의 JPA 강의

'JPA' 카테고리의 다른 글

@MappedSuperclass  (0) 2021.08.19
단방향 연관관계  (0) 2021.03.09
DB 스키마 자동 생성  (0) 2021.02.14
객체와 Table 매핑  (0) 2021.02.14
flush  (0) 2021.02.13