JPA

기본 키 매핑 - 2

방구석 대학생 2020. 9. 21. 22:23

"인프런 - 자바 ORM 표준 JPA 프로그래밍 강의를 듣고 작성한 글 입니다."

www.inflearn.com/course/ORM-JPA-Basic#

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다. 초급 웹 개발 프로그

www.inflearn.com

 

 

기본 키 매핑 전략(2) - SEQUENCE 전략

- @GeneratedValue(strategy = GenerationType.SEQUENCE) // 주로 Oracle 같은 데이터베이스에서 많이 사용한다.

해당 전략을 사용하였을 경우 hibernate message

Hibernate: create sequence hibernate_sequence start with 1 increment by 1 //sequence object 를 하나 만들어낸다.

(원래 Oracle DB 의 SEQUENCE 전략 자체가 데이터베이스에 있는 sequence object 를 통해서 값을 generate 한다. -> sequence object 를 통해서 값을 가져온 다음 해당 값을 세팅해주는 것)

 

기본 키 전략이 sequence 일 경우 기본 키의 타입을 String 으로 지정해주면 안된다.(int, Integer 도 애매함)

왜? : sequence object 에서 값이 10억 쯤 넘어가면 한 바퀴 돌아서 다시 시작하게 된다.(즉, 기본 키 값의 크기에 제약이 있다.)

해법 : Long 타입을 사용한다.(애플리케이션 전체를 볼 때는 Integer 나 Long 이나 큰 차이가 없지만 기본 키 값의 갯수가 10억개가 넘어갈 경우에는 오히려 타입을 바꿔주는게 힘들다, 되도록이면 Long 을 쓰자)

 

@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;

 

기본 키 값은 1 부터 시작하며, 이후론 숫자가 1씩 증가한다.

아래는 sequence 에서 값을 가져오는 것을 알려주는 hibernate message 이다.(hibernate 가 만들어준 기본 sequence 에서 값을 가져온다.)

Hibernate: 
    call next value for hibernate_sequence

 

그런데 만약 테이블 마다 sequence 를 따로 관리해주고 싶으면 어떻게 해야 할까?

도메인 클래스 자체에 @SequenceGenerator 어노테이션을 이용하여 각 도메인 클래스 마다 직접 매핑을 해주면 된다.

이후 @GeneratedValue 어노테이션을 통해 sequence 전략을 활용할 경우 generator 속성을 통해 매핑해준 이름을 함께 지정해주자.

 

아래는 도메인 클래스 별로 sequence 를 관리할 때 작성하는 코드의 예시이다.

@Entity
@SequenceGenerator(
	name = "MEMBER_SEQ_GENERATOR",
	sequenceName = "MEMBER_SEQ", // 매핑할 데이터베이스 시퀀스 이름
	initialValue = 1, allocationSize = 1
)
public class Member {
	@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBER_SEQ_GENERATOR")
	private Long id;
}

 

아래의 hibernate message 를 보면 sequenceName 과 initialValue, allocationSize 속성이 잘 적용되어 있는 것을 확인할 수 있다.

Hibernate: create sequence MEMBER_SEQ start with 1 increment by 1
Hibernate: 
    
    create table Member (
       id bigint not null,
        name varchar(255),
        primary key (id)
    )
Hibernate: 
    call next value for MEMBER_SEQ 
// 키 값을 가져오기 위해 데이터베이스의 MEMBER_SEQ 시퀀스의 다음(next) 값을 가져와라.
Hibernate: 
    /* insert hellojpa.Member
        */ insert 
        into
            Member
            (name, id) 
        values
            (?, ?)

여기서 allocationSize 속성의 기본 값은 50 이므로 증가폭을 직접 조절해주지 않으면 기본 키 값이 50씩 증가하게 된다.

 

* SEQUENCE 전략의 특징

- 데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트 이다.(예시 : 오라클 시퀀스)

- persist 를 통해 entity 를 영속성 컨텍스트에 올리고자 할 경우 이번에도 기본 키가 필요하다.

SEQUENCE 전략에서는 데이터베이스에 생성된 시퀀스에 있는 키 값을 가져오게 된다.

(위의 hibernate message 에서 Hibernate:  call next value for MEMBER_SEQ 를 통해 시퀀스에 있는 키값을 가져오는 것을 알 수 있다.)

- persist 하는 시점에 영속성 컨텍스트에 entity 를 넣으려는데 SEQUENCE 전략임이 확인되면, 데이터베이스 에서 시퀀스를 찾아가 키 값을 얻어온 다음 entity 에 id 값을 넣어준 후 영속성 컨텍스트에 해당 entity 를 올려놓는다.

- IDENTITY 전략과는 달리 persist 시점에 insert query 가 데이터베이스에 전달되지 않는다.

   -> 왜? : 기본 키 값만 얻고 난 후 필요하면 버퍼링 같은 기능을 지원해야 하기 때문에 영속성 컨텍스트에 쌓아둔 상태로 실제 트랜잭션이 커밋되는 시점에 insert query 가 전달되기 때문

(즉, 시퀀스 방식은 버퍼링과 같은 기능 지원이 가능하다.)

 

그런데 결국 키 값을 가져오는 과정으로 인해 자꾸 네트워크를 왔다갔다 하느라 성능이 저하되지 않을까?

해당 문제를 해결하기 위해 JPA 가 지원하는 것이 있다.

-> allocationSize 를 가지고 네트워크 통신 횟수를 줄임 으로서 성능을 증가시켜 줄 수 있다.

 

allocationSize 의 default 값 50을 이용하여 기본 키 값의 범위를 미리 50 까지 땡겨놓는다.

이후 next call 을 한 번 할때 미리 데이터베이스에 기본 키 50개를 올려놓고 메모리에서 1씩 사용한다.

그러다가 기본 키의 숫자가 데이터베이스에 있는 숫자에 거의 도달하면 다시 next call 을 통해 그 다음 숫자부터 기본 키 50개를 미리 쌓아둔다.

즉, 기본 키의 범위를 미리 정해두는 것을 통해 네트워크 통신 횟수를 획기적으로 줄일 수 있다.

이 방법이 여러 웹 서버가 있어도 동시성 문제 없이 다양한 문제들을 해결할 수 있다.

 

기본 default 값으로 allocationSize 지정 시 hibernate message

Hibernate: create sequence MEMBER_SEQ start with 1 increment by 50 

entity 를 실제로 데이터베이스에 저장하기 위해 persist 메소드를 호출했을 경우 hibernate message

Hibernate: 
    call next value for MEMBER_SEQ
Hibernate: 
    call next value for MEMBER_SEQ

-> 시퀀스 호출을 두 번 하게 된다.

첫번째 호출 : 처음 저장한 entity 에 키 값 1 부여

두번째 호출 : 키 값 시퀀스 범위 50 까지 확보(여기서 정확하게는 51 까지 -> 이미 범위중 1개의 공간은 채워졌기 때문)

이후로는 확보한 범위 까지 키 값의 갯수가 도달하지 않았다면 시퀀스가 호출되지 않는다.(메모리 내부에서 키 값 부여가 진행됨)

 

이론적으로는 최대한 많은 범위가 확보되는 것이 좋으나, 웹 서버가 내려갈 때 모두 사라지기 때문에 중간에 숫자 구멍이 발생하게 된다.

물론 그 구멍이 생기는 건 크게 문제가 되는 건 아니지만 괜한 낭비가 될 수 있기 때문에 50 에서 100 정도가 적절하다.

이는 다음에 작성할 TABLE 전략에 대해서도 마찬가지 이다.