[JPA] auto_increment 전략 IDENTITY vs SEQUENCE

2023. 1. 9. 15:10·JVM/JPA

 

기본키 생성 전략에 따른 JPA 의 기본 동작 차이.

 

IDENTITY

IDENTITY  전략에서는 PK (auto_increment) 값이 Insert 쿼리가 DB에 COMMIT 되는 시점에 확정된다.

 

문제 상황

영속성 컨텍스트를 가진 JPA 입장에서 어떤 트랜잭션 내에서 COMMIT 하기 전에 PK (ID) 를 사용해야 한다면 어떻게 해야할까?

 

 

em.persist(entity) 시점에 
INSERT 쿼리를 실행한다.
(아직 COMMIT 전)

 

Member.java

@Entity
@Table(name = "member")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class Member {
   
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "member_id")
    private Long id;
    
    @Column(name = "member_name", updatable = true, nullable = false)
    private String name;
    
    public static Member registerMember(String name) {
        var member = new Member();
        member.name = name;
        return member;
    }
}

 

MemberTest.java

class MemberTest {
    private final EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa-practice");
    private EntityManager em;
    
    @Test
    void strategyIdentityTest() {
        var tx = getEntityTransaction();

        try {
            tx.begin();
            var member1 = Member.registerMember("제이스");
            System.out.println("=============================");
            em.persist(member1);
            System.out.println("=============================");
            assertThat(member1.getId()).isEqualTo(1L);

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }
    }
}

실행 결과

=============================
15:07:13.566 [Test worker] DEBUG org.hibernate.engine.spi.ActionQueue - Executing identity-insert immediately
15:07:13.577 [Test worker] DEBUG org.hibernate.SQL - 
    /* insert com.example.jpapractice.domain.member.Member
        */ insert 
    into
        member (member_id, age, create_date, description, last_modified_date, member_name, role_type) 
    values
        (default, ?, ?, ?, ?, ?, ?)
Hibernate: 
    /* insert com.example.jpapractice.domain.member.Member
        */ insert 
    into
        member (member_id, age, create_date, description, last_modified_date, member_name, role_type) 
    values
        (default, ?, ?, ?, ?, ?, ?)
15:07:13.590 [Test worker] DEBUG org.hibernate.id.IdentifierGeneratorHelper - Natively generated identity: 1
=============================

em.persist(member) 하자마자 INSERT 쿼리가 실행된다.

물론 곧바로 영속성 컨텍스트에 써서 등록한다. (1차 캐싱상태 완료)

 

IDENTITY PK를 갖는 엔티티 클래스의 em.persist() 내부 동작 순서

  1. INSERT 쿼리를 일단 실행한다.
    (완전히 COMMIT 하진 않는다.)
  2. 생성된 IDENTITY 값을 내부적으로 가져온다.
  3. 영속성 컨텍스트에 가져온 PK값을 ID로 삼아 객체를 등록한다.
  4. 트랜잭션을 COMMIT 하여 영구 반영한다.

 

 

 


 

SEQUENCE

별도로 sequence 라고하는 object 가 DB 내에 생성된다.

em.persist 를 호출하는 시점에 타겟팅할 sequence 와 값만 불러온다.

 

Member.java

@Entity
@Table(name = "member")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class Member {
   
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    @Column(name = "member_id")
    private Long id;
    
    @Column(name = "member_name", updatable = true, nullable = false)
    private String name;
    
    public static Member registerMember(String name) {
        var member = new Member();
        member.name = name;
        return member;
    }
}

생성된 테이블 DDL

테이블 정보와 생성된 시퀀스를 모두 확인할 수 있다.

create table MEMBER
(
    MEMBER_ID          BIGINT auto_increment
        primary key,
    AGE                INTEGER
);

-- auto-generated definition
create sequence MEMBER_SEQ
    increment by 50;

 

 

MemberTest.java

class MemberTest {
    private final EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa-practice");
    
    private EntityManager em;
    
    @Test
    void strategySequenceTest() {
        var tx = getEntityTransaction();

        try {
            tx.begin();
            var member1 = Member.registerMember("제이스");
            System.out.println("=============================");
            em.persist(member1);
            System.out.println("=============================");
            assertThat(member1.getId()).isEqualTo(1L);

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }
    }

    
    private EntityTransaction getEntityTransaction() {
        this.em = emf.createEntityManager();
        return em.getTransaction();
    }
}

 

 

실행 결과

member.getId() 에서 SELECT 를 통해 결정된 시퀀스 값을 끌고온다.

마지막으로 INSERT 쿼리가 실행된다.

=============================
14:53:05.757 [Test worker] DEBUG org.hibernate.SQL - 
    select
        next value for member_seq
Hibernate: 
    select
        next value for member_seq
14:53:05.759 [Test worker] DEBUG org.hibernate.id.enhanced.SequenceStructure - Sequence value obtained: 1
14:53:05.761 [Test worker] DEBUG org.hibernate.event.internal.AbstractSaveEventListener - Generated identifier: 1, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator
=============================

/* insert com.example.jpapractice.domain.member.Member
    */ insert 
into
    member (age, create_date, description, last_modified_date, member_name, role_type, member_id) 
values
    (?, ?, ?, ?, ?, ?, ?)

 

 

em.persist 해도
곧바로 ISNERT 쿼리가 실행되지 않는다.

 

SEQUENCE PK를 갖는 엔티티 클래스의 em.persist() 내부 동작 순서

  1. DB에서 지정된 next SEQUENCE 값을 찾아온다.
  2. 영속성 컨텍스트에 객체를 등록한다.
  3. INSERT 쿼리를 실행한다.
  4. COMMIT 한다.

 

allocationSize 가 50이 Default 인 이유 - 동시성 Issue

⚠️ 추후 내용 추가 예정

 

allocationSize 만큼 DB에는 미리 올려놓고 메모리에서는 1씩 쓰는 것.

여러 웹서버가 있어도 동시성 이슈 없이 다양한 문제를 해결할 수 있게한다.

 

 

✒️ TIP

`em.persist()` 시점에 즉시 INSERT 쿼리를 실행하는 하는 IDENTITY 보다 쿼리를 버퍼링하여 지연 실행하는 SEQUENCE 전략이 좋아보일 수 있으나, 버퍼링 전략에 대한 성능 차이는 미미하다.

 

JDBC Batch 같은 측면에서 보면 Network I/O 가 줄어들기 때문에 SEQUENCE 가 낫다.

 

상상해보라. IDENTITY 를 쓰면 총 50개의 INSERT 쿼리를 실행하고자 할 때 50번 각각의 INSERT 실행이 필요하다. (다음 PK 값을 생성하기 위해) 반면에 SEQUENCE 는 JVM 메모리 내에서 가상의 번호로 allocationSize 를 갖고 JDBC Batch 연산이 가능하므로 최적화에 용이하다.

 

 

 

 

 

📝 Conclusion

 

 

INSERT Batch 처리가 필요하다면 SEQUENCE를 권장하고
그 외에는 IDENTITY 를 사용해도 좋다.
It is important to realize that using IDENTITY columns imposes a runtime behavior where the entity row 
must be physically inserted prior to the identifier value being known.

There is yet another important runtime impact of choosing IDENTITY generation: Hibernate will not be able to batch INSERT statements for the entities using the IDENTITY generation.
The importance of this depends on the application-specific use cases. If the application is not usually creating many new instances of a given entity type using the IDENTITY generator, then this limitation will be less important since batching would not have been very helpful anyway.
 - Hibernate docs

 

 

 


🔗 Reference

 

Hibernate ORM 6.1.6.Final User Guide

Fetching, essentially, is the process of grabbing data from the database and making it available to the application. Tuning how an application does fetching is one of the biggest factors in determining how an application will perform. Fetching too much dat

docs.jboss.org

 

[JPA] 식별자 할당 SEQUENCE(시퀀스) 사용 전략

JPA 식별자 JPA는 엔티티들을 논리적인 공간인 영속성 컨텍스트에서 관리하는데, 엔티티를 구분할 수 있는 식별자가 필요합니다. 식별자가 되는 필드는 엔티티 클래스의 @Id 애노테이션을 통해 지

dololak.tistory.com

 

 

저작자표시 (새창열림)

'JVM > JPA' 카테고리의 다른 글

[JPA] 상속관계 매핑  (0) 2023.01.13
[JPA] 1:1 연관관계 설정  (0) 2023.01.12
[JPA] JPA 는 왜 등장했을까?  (0) 2023.01.07
[JPA] API 생성시 Entity 를 반환하지 말자.  (0) 2022.12.29
'JVM/JPA' 카테고리의 다른 글
  • [JPA] 상속관계 매핑
  • [JPA] 1:1 연관관계 설정
  • [JPA] JPA 는 왜 등장했을까?
  • [JPA] API 생성시 Entity 를 반환하지 말자.
M_Falcon
M_Falcon
  • M_Falcon
    Falcon
    M_Falcon
  • 전체
    오늘
    어제
    • 분류 전체보기 (432)
      • Web (16)
        • Nodejs (14)
        • Javascript (23)
        • FrontEnd (4)
      • DataBase (39)
        • Fundamental (1)
        • Redis (4)
        • PostgreSQL (10)
        • NoSQL (4)
        • MySQL (9)
        • MSSQL (3)
        • Error (4)
      • Algorithm (79)
        • Algorithm (문제풀이) (56)
        • Algorithm (이론) (23)
      • JVM (65)
        • Spring (13)
        • JPA (5)
        • Kotlin (13)
        • Java (24)
        • Error (7)
      • 기타 (70)
        • Kafka (3)
        • Kubernetes (3)
        • Docker (13)
        • git (19)
        • 잡동사니 (27)
      • 재테크 (11)
        • 세무 (4)
        • 투자 (3)
        • 보험 (0)
      • BlockChain (2)
        • BitCoin (0)
      • C (32)
        • C (10)
        • C++ (17)
        • Error (3)
      • Low Level (8)
        • OS (3)
        • 시스템 보안 (5)
      • 네트워크 (3)
      • LINUX (30)
        • Linux (26)
        • Error (4)
      • 저작권과 스마트폰의 이해 (0)
      • 생각 뭉치 (6)
      • 궁금증 (2)
      • Private (4)
        • 이직 경험 (0)
        • 꿈을 찾아서 (1)
      • Android (21)
        • OS (4)
  • 블로그 메뉴

    • 홈
    • WEB
    • 알고리즘
    • DataBase
    • Linux
    • Mobile
    • C
    • 방명록
  • 링크

    • github
  • 공지사항

  • 인기 글

  • 태그

    Bitcoin
    프로그래머스
    C++
    algorithm
    알고리즘
    ubuntu
    java
    docker
    백준
    database
    javascript
    android
    Kotlin
    kafka
    Git
    JPA
    linux
    Spring
    PostgreSQL
    Programmers
  • hELLO· Designed By정상우.v4.10.3
M_Falcon
[JPA] auto_increment 전략 IDENTITY vs SEQUENCE
상단으로

티스토리툴바