본문 바로가기
Java/JPA

[JPA_Basic] 기본키 매핑

by HJ0216 2023. 8. 5.

이 글은 김영한의 [자바 ORM 표준 JPA 프로그래밍 - 기본편] 수강하며 정리한 글입니다.

 

👉 기본 환경

- Language: Java

- DB: H2 Database

- IDE: IntelliJ

 

 

1. 기본키 직접 할당

1
2
3
4
5
6
7
@Entity
public class Member {
 
    @Id // PK 지정 필수, PK 직접 할당
    private Long id;
}
 
 

 

2. 기본키 자동 생성: @GeneratedValue

    2.1. AUTO

    - Default

    - DB 방언에 맞춰 Value 자동 생성

 

    2.2. IDENTITY

1
2
3
4
5
6
7
8
@Entity
public class Member {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
}
 
 
 

    - PK 생성을 DB에 위임(예: MySQL: AUTO_INCREMENT)

    - PK에 null값을 넘기면 DB에서 자동으로 생성해줌

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Main {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
 
        EntityManager em = emf.createEntityManager();
 
        EntityTransaction tx = em.getTransaction();
        tx.begin();
 
        try {
            Member member = new Member();
            System.out.println("==========");
            em.persist(member);
            System.out.println("==========");
 
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
 
        emf.close();
    }    
}
 
 
 

    - JPA는 tx.commit() 시점에 INSERT SQL 실행

1
2
3
4
5
6
7
8
9
10
11
==========
Hibernate: 
    /* insert jpa_basic.Member
        */ insert 
        into
            Member
            (id) 
        values
            (null)
==========
 
 

    - IDENTITY 전략은 em.persist() 시점에 즉시 INSERT SQL 실행

    - 영속성 컨텍스트에 의해 관리되기 위해서 (Key, Value)로 (PK, 객체) 값이 필요한데, 영속성 컨텍스트에 관리되는 시점인 persist 시, PK값을 알 수 없으므로 IDENTITY 전략만 예외적으로 persist에서 DB insert query 발생

 

    2.3. SEQUENCE

1
2
3
4
5
6
7
8
9
10
11
12
13
@Entity
@SequenceGenerator(
       name = "MEMBER_SEQ_GENERATOR"// Java에서의 SEQ 이름
       sequenceName = "MEMBER_SEQ"// DB에서의 SEQ 이름
       allocationSize = 3
)
public class Member {
 
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBER_SEQ_GENERATOR")
    private Long id;
}
 
 
 

    - Sequence 객체를 생성해서 생성한 Sequence 객체에서 값을 가져와 PK값에 세팅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class Main {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
 
        EntityManager em = emf.createEntityManager();
 
        EntityTransaction tx = em.getTransaction();
        tx.begin();
 
        try {
            Member member1 = new Member();
            System.out.println("=====11=====");
            em.persist(member1);
            System.out.println("=====22=====");
 
            Member member2 = new Member();
            Member member3 = new Member();
            Member member4 = new Member();
            Member member5 = new Member();
            Member member6 = new Member();
            Member member7 = new Member();
            Member member8 = new Member();
 
            em.persist(member2);
            em.persist(member3);
            em.persist(member4);
 
            System.out.println("=====33=====");
            em.persist(member5);
            em.persist(member6);
            em.persist(member7);
 
            System.out.println("=====44=====");
            em.persist(member8);
 
            System.out.println("=====55=====");
 
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
 
        emf.close();
    }    
}
 
 
 

    - em.persist() 실행 시, 영속성 컨텍스트에 의해 관리되기 위해서 IDENTITY 전략과 마찬가지로 PK 값을 필요로 함

    - 그러나, SEQUENCE 객체도 DB에 의해 관리되므로 PK를 알 수 없음

    - 그러므로, em.persist(); 시 SEQ 객체로부터 PK값을 얻어옴

  ⭐ Insert 시점은 commit 시점으로 query buffer 기능 사용 가능

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
=====11=====
Hibernate: 
    call next value for MEMBER_SEQ
Hibernate: 
    call next value for MEMBER_SEQ
=====22=====
=====33=====
Hibernate: 
    call next value for MEMBER_SEQ
=====44=====
Hibernate: 
    call next value for MEMBER_SEQ
=====55=====
Hibernate: 
    /* insert jpa_basic.Member
        */ insert 
        into
            Member
            (id) 
        values
            (?)
...
 
 
 

    - 처음에 call next value가 2번 호출되는 이유

        - SEQ 객체 초기 값이 -2로 call next value를 호출하여, 1부터 시작될 수 있도록 setting

        - allocationSize에 따라 메모리에 미리 seq값을 저장해두기 위해 호출

        - 그 이후서부터는 미리 저장해둔 seq를 다 사용하면 call next value

 

    2.4. TABLE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Entity
@TableGenerator(
        name = "MEMBER_SEQ_GENERATOR",
        table = "MY_SEQUENCES",
        pkColumnValue = "MEMBER_SEQ",
        allocationSize = 1
)
public class Member {
 
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "MEMBER_SEQ_GENERATOR")
    private Long id;
}
 
 
 

    - 키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 사용하는 것처럼 하는 전략

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Main {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
 
        EntityManager em = emf.createEntityManager();
 
        EntityTransaction tx = em.getTransaction();
        tx.begin();
 
        try {
            Member member = new Member();
            System.out.println("==========");
            em.persist(member);
            System.out.println("==========");
 
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
 
        emf.close();
    }    
}
 
 
 

    - 테이블을 직접 생성하므로 모든 데이터베이스에 적용 가능하나, 최적화 등 성능 문제가 발생할 수 있음

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
Hibernate: 
    
    create table Member (
       id bigint not null,
        primary key (id)
    )
Hibernate: 
    
    create table MY_SEQUENCES (
       sequence_name varchar(255not null,
        next_val bigint,
        primary key (sequence_name)
    )
Hibernate: 
 
    insert into MY_SEQUENCES(sequence_name, next_val) values ('MEMBER_SEQ',0)
 
=====11=====
Hibernate: 
    select
        tbl.next_val 
    from
        MY_SEQUENCES tbl 
    where
        tbl.sequence_name=? for update
            
Hibernate: 
    update
        MY_SEQUENCES 
    set
        next_val=?  
    where
        next_val=
        and sequence_name=?
 
Hibernate: 
    select
        tbl.next_val 
    from
        MY_SEQUENCES tbl 
    where
        tbl.sequence_name=? for update
            
Hibernate: 
    update
        MY_SEQUENCES 
    set
        next_val=?  
    where
        next_val=
        and sequence_name=?
=====22=====
Hibernate: 
    /* insert jpa_basic.Member
        */ insert 
        into
            Member
            (id) 
        values
            (?)
 
 
 

 

 

 

📚 참고 자료

 

엔티티 매핑

- 객체와 테이블 매핑 : @Entity, @Table - 필드와 컬럼 매핑 : @Column - 기본키 매핑 : @Id, @GeneratedValue 객체와 테이블 매핑, 필드와 컬럼 매핑, 기본키 매핑의 방법을 간단하게 알아보고 각 애노테이션의

programmingrecoding.tistory.com