Entity 기본 키 매핑 전략 - SEQUENCE/TABLE/IDENTITY
Entity - DB table mapping
Jpa를 사용해서 테이블과 매핑할 class를 @Entity를 필수로 붙여야하며, @Entity가 붙은 class를 entity라고 부른다.
@Entity 적용 시 다음과 같은 전제조건을 만족해야한다.
- 기본생성자
- final class , enum , interface , inner class가 아니여야 한다.
- 저장할 필드에 final 키워드가 붙으면 안된다.
Jpa는 다양한 mapping annotation을 지원하는데 , 크게 4가지로 분류될 수 있다.
객체와 테이블 매핑 : @Entity , @Table
기본 키 매핑 : @Id
Field와 Column 매핑 : @Column
연관관계 매핑 ex) @ManyTone , @JoinColumn
기본키 매핑 전략
DB id값을 client측에서 넘겨줄 수 있겠지만, 인조키의 경우 DB vendor에서 생성할 수도 있다.
DB vendor 마다 기본키를 생성하는 방식이 다르다, 예를 들면 oracle에서는 sequence 객체를 만들고, mysql에서는 auto_increment기능을 사용한다.
1 |
|
생성 전략은 @Id가 붙은 key 필드에 @GeneratedValue(strategy = “사용할 전략”) 을 명시해준다,
1 | public enum GenerationType { |
생성 전략은 총 4개가 있는데 하나씩 정리하였다.
기본키 매핑 전략 - IDENTITY
IDENTITY 전략은 기본 키 생성을 데이터베이스에 위임하는 전략으로, 주로 MySQL, PostgreSql , Sql server 에서 사용 한다. 예를 들어 MySQL vendor를 사용한다고 하면 DDL 의 key값에 auto_increment 가 추가된다.
1 | create table member ( |
기본적으로 entity가 영속 상태가 되려면 식별자가 필요한데 IDENTITY 전략은 식별자의 생성 주체가 DB이기 때문에 DB에 entity를 저장하고 나서, 식별자를 조회할 수 있다. 때문에 식별자가 바로 필요하기 떄문에 em.persist(entity)로 저장을 하면 write buffering 이 일어나지 않고, 바로 query가 나가고 조회된 식별자로 영속성 컨텍스트에 entity를 보관한다.
1 |
|
- JDBC3 에서 추가된 Statement.getGeneratedKeys() API를 사용하면 데이터 저장과 동시에 생성된 기본키를 가지고 올 수 있는데 hibernate는 이 method를 사용하기 떄문에 entity를 저장한 직후에 식별자를 다시 조회하더라도 SELECT Query가 나가지 않는다 (DB와 한번만 요청을 주고 받는다.)
기본키 매핑 전략 - SEQUENCE
SEQUENCE 전략은 Oracle 처럼 DB내에서 유일한 값을 순서대로 생성하는 sequence 객체를 가진 경우, 이 sequence를 이용해서 기본키를 생성하는 전략이다.
sequence 전략은 DB에 sequence 객체를 생성하고 매핑해주어야하는데 이를 Entity class내부에서 @SequnceGeneator 로 생성 및 매핑해줄 수 있다.
1 |
|
실제 나가는 query를 보면 다음과 같이 sequence object도 같이 생성된다.
1 | create sequence member_seq start with 1 increment by 1 |
@SequenceGeneator의 주요속성은 다음과 같다.
- name : 식별자 생성기 이름
- sequenceName : 시퀀스 이름
- initialValue : 시퀀스 초기 시작값
- allocationSize : 시퀀스 한번에 증가하는 수 default 50
내부 동작 방식은 IDENTITY 방식과 어떻게 다를까?
실제 수행되는 query를 보면 다음과 같다.
앞선 IDENTITY 전략에서는 write buffering 없이, entity를 저장함과 동시에 쿼리가 나가서 DB 식별자값을 가지고 영속성 컨텍스트에 저장하는 원리였다면
SEQUENCE전략은 먼저 DB sequence 객체에서 식별자 값을 가져와 entity에 할당한뒤 영속성 컨텍스트에 저장한다.그 이후 transaction commit 시점에 flsuh 되어 insert query가 한꺼번에 나간다 (write buffering 있음)
정리하면 IDENTITY 전략은 entity 저장 후 식별자를 가져오는 방식임으로 write buffering 불가, SEQUENCE 전략은 entity 저장 전에 sequence 객체로부터 식별자를 가져와서 write buffering 수행한다
기본키 매핑 전략 - TABLE
TABLE 전략은 key 생성 전용 테이블을 하나 만들어서 DB Sequence를 흉내내는 전략이다. key 생성 전용 테이블은 Entity class에 @TableGenerator를 이용해 생성할 수 있다.
1 |
|
ddl-auto : create 로 설정하면 application 실행시 다음과 같은 query문이 나가는 것을 볼 수 있다.
1 | create table member_seq ( |
@TableGenerator option에 대한 설명은 공식 레퍼런스에더 자세히 적혀있지만 https://docs.oracle.com/javaee/5/api/javax/persistence/TableGenerator.html 정리하면 다음과같다 .
- name (필수값) : 식별자 생성기 이름
- table : table 명
- pkColumnName : seq 컬럼명
- valueColumnName : seq 값 컬럼명
- initialValue : 초기값
- allocationSize : 시퀀스 한번 호출시 증가하는 수로 성능최적화에 사용된다 default는 50
원리는 SEQUENCE전략과 동일하다.
먼저 DB table의 key값을 조회(SELECT) 하고, 다음 값으로 증가시키기 위해 Update query가 나가므로 I/O통신을 2번한다.
이후에 영속성 컨텍스트에 조회된 식별자값과 entity를 보관하고 있다가 transaction commit 시점에 한번에 flush 한다(write buffering)
SEQUENCE,TABLE 전략 최적화
SEQUENCE 전략은 sequence 객체로부터 먼저 식별자값을 조회한다.
따라서 I/O 요청이 2번 일어난다. 이를 최적화하기 위해서 @SequenceGenerator( … ,allocationSize = x , …) 의 allocationSize값을 조정하여 메모리에 sequence 값을 할당해놓고 사용할 수 있다.
sequence 값을 캐싱해놓고, DB에는 캐싱한 값까지는 I/O요청을 안보낸다.
TABLE 전략도 마찬가지다. 대신 table전략에서는 말그대로 DB table을 생성해서 키값을 관리하는데, 이때 나가는 update query를 allocationSize값을 조정해서 최적화 할 수 있다.
기본키 매핑 전략 - AUTO
AUTO 전략은 설정한 데이터베이스 방언에 따라 위 3가지 전략(IDENTITY/SEQUENCE/TABLE) 중에 한가지를 자동으로 선택한다.
개발 초기 단계시 활용될 수 있다.
1 |
|
DB 스키마 자동 생성 전략
마지막으로 spring.jpa.hibernate.ddl-auto 속성에 따라 application이 실행될떄 DB 스키마 자동 생성 전략을 지정할 수 있다.
1 | // application.yaml |
참고로 spring.jpa.hibernate.ddl-auto의 값은 hibernate 구현체의 hibernate.hbm2ddl.auto 속성 값으로 전달된다.
(ref - https://stackoverflow.com/questions/42135114/how-does-spring-jpa-hibernate-ddl-auto-property-exactly-work-in-spring)
spring.jpa.hibernate.ddl-auto에서 사용할 수 있는 전략은 4가지다.
- create : DROP + CREATE
- create-drop : DROP + CREATE , application 종료시 DROP
- update : 매핑 변경 사항만 수정 ex) alter table ~
- validate : 실제 DB 스키마와 application entity간 매핑을 확인하고 일치하지 않으면 경고를 남기고 application은 실행하지 않는다.
- none
- Entity class에 제약조건 명시
다음과 같이 DDL 생성시 제약조건이나 타입 크기등을 조절할 수 있다.
1 |
|
정리
sequence , table 전략은 원리는 동일하지만 sequence object를 사용하느냐, table 을 사용하느냐의 차이이다.
sequence , table 전략은 식별자를 가지고 와서 영속성 컨텍스트에 저장을 한다.
- I/O 요청이 allocationSize =1 이라고 하면 2번 나간다.
- write buffering이 가능하다.
identity 전략은 식별자를 entity 저장시점에 알 수 있기 때문에 entity 저장시점에 바로 query가 나가서 entity를 먼저 저장하고 식별자 값을 가져와서 영속성 컨텍스트에 저장한다.
- I/O 요청이 1번 나간다 (hibernate의 경우에는)
- write buffering 이 불가능하다.