Programming/Java

[JPA] JPA에서 PK를 다루는 방법(@Id, @GeneratedValue)

kmindev 2024. 12. 12. 17:39

1. 개요

김영한 님의 '자바 ORM 표준 JPA 프로그래밍 - 기본편' 을 들으면서 정리하는 포스팅입니다.

 

https://www.inflearn.com/course/ORM-JPA-Basic

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 강의 | 김영한 - 인프런

김영한 | JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., 실무에서도

www.inflearn.com

 

2. 기본키 매핑 @Id

  • Entity에는 반드시 PK가 필요하다.
  • PK를 매핑할 때 Entity 필드에 @Id를 붙혀주면 된다.
@Entity
public class Member {
    @Id private String userId; // 기본키 정의 
    
    // 필드 정의 ...
}

 

3. 기본키 할당 전략

기본키를 할당 전략은 '직접할당방식', '자동생성방식' 두 가지 전략이 있다.

 

a. 직접 할당 방식

직접할당 방식은 애플리케이션 내에서 PK에 해당하는 필드를 Setter, 생성자 등을 통해 직접 할당하는 방식이다.

@Entity
public class Member {
    @Id @Setter private String userId; // 기본키 정의
    // 기타 필드 정의..
}

EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();

Member member = new Member();
member.setUserId("member1"); // 기본키 직접 할당
em.persist(member);

 

b. 자동 생성 방식 @GeneratedValue

자동생성방식은 대리키를 사용하는 방식으로 IDENTITY, SEQUENCE, TABLE, AUTO 4가지 방식이 있다.

자동생성방식은 MySQL에서 AUTO_INCREMENT 를 떠올리면 이해하기 쉽다.

 

 

4. PK 자동 생성 방식 4가지

a. IDENTITY 전략

기본키 생성 방식을 DB에 위임하는 방식이다.

MySQL, PostgreSQL, SQL Server 등에서 사용할 수 있다.(예: MySQL에서 AUTO_INCREMENT)

 

작동 방식

  • PK는 데이터베이스가 자동으로 생성한다.(쿼리만 날리면 DB에서 알아서 PK를 할당한다)
  • Entity는 반드시 PK를 알아야 하기 때문에 em.persist(o) 호출 시 즉시, SQL Insert 쿼리를 실행하여 DB로부터 생성된 PK를 가져오는 방식으로 동작한다. => 쓰기 지연이 동작하지 않으므로 배치 처리 불가능하다.

사용 예시

@Entity
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    
    // 생성자, Setter 등
}

 

DDL 자동 생성 결과(MySQL)

 

 

b. SEQUENCE 전략

  • DB Sequence를 사용해 기본키를 할당하는 방식이다.
  • Sequence는 유일한 값을 순서대로 생성하는 DB 오브젝트이다.(기본키를 관리하는 테이블이라고 생각하자.)
  • IDENTITY 방식과 다르게 Insert 하기 전에 시퀀스를 조회해서 PK 값을 알 수 있기 때문에 쓰기 지연이 가능하다.
  • 시퀀스를 미리 선점하여 여러 JVM에서 동작해도 기본키가 충돌하지 않는다.
  • Oracle, PostgreSQL, DB2, H2 데이터베이스에서 사용할 수 있다.

 

작동 방식

  1. JPA가 Entity의 PK가 필요할 때, DB 시퀀스 오브젝트에서 현재 값을 가져온다.
    • 예: 현재 시퀀스 값이 1 이라고 가정
  2. allocationSize 만큼 DB 시퀀스 오브젝트의 현재 값을 증가시켜 업데이트 한다.
    • 예: allocationSize가 50이면 시퀀스의 현재값을 50만큼 증가시켜 51 로 업데이트
  3. JPA는 할당된 범위(1~50) 의 값을 메모리에 보관해두고, 필요할 때 꺼내서 사용한다.
    • DB 호출 횟수를 줄일 수 있어 성능 최적화 가능

 

3번의 Insert 쿼리를 진행할 때 JPA 쿼리 결과

  • allocationSize가 1일 경우 메모리에 할당된 PK 값이 하나뿐이므로 Insert를 진행할 때 마다 시퀀스를 조회하여 PK를 할당해준다.
  • allocationSize가 50일 경우 할당된(1~50)까지 시퀀스를 메모리에 꺼내서 PK에 할당한다.

 

 

@SequenceGenerator 속성

속성 설명 기본값
name 식별자 생성기 이름 필수
sequenceName DB에 등록되어 있는 시퀀스 이름 hibernate_sequence
initialValue DDL 생성 시 초기값 지정 1
allocationSize 시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용) 50
catalog, schema DB 스키마, 카탈로그 이름  

 

 

사용 예시

@SequenceGenerator(
        name = "member_seq_generator",
        sequenceName = "member_seq",
        initialValue = 1,
        allocationSize = 50
)
@Entity
public class Member {
    @Id
    @GeneratedValue(
            strategy = GenerationType.SEQUENCE,
            generator = "member_seq_generator"
    )
    private Long id;
    private String username;

    // 생성자 및 Setter 등
}

 

DDL 자동 생성 결과(H2 Database)

 

c. TABLE 전략

  • 키 생성 전용 테이블을 하나 만들어서 DB 시퀀스를 흉내내는 전략
  • 테이블을 이용하는 방식이기 때문에 모든 DB에 적용 가능한 방법이다.
  • 테이블로부터 시퀀스를 할당받기 때문에 추가적으로 Update 쿼리가 발생한다는 점에서 시퀀스 전략보다 성능은 좋지 않다. (시퀀스를 지원하지 않는 DB에서 사용하는게 좋을 것 같다.) 

작동 방식

  • 시퀀스 전략과 동작방식은 유사하다.
  • 시퀀스를 할당 받을 때 추가적으로 Update 쿼리가 실행된다는 점에서 차이가 있다. 

TABLE 전략과 SEQUENCEPA 전략 쿼리 결과 차이

  • TABLE 전략은 현재 시퀀스 값을 조회한 다음 allocationSize만큼 증가한 값만큼 UPDATE 쿼리를 실행한다.
  • SEQUENCE 전략은 단순 시퀀스만 조회를 하기 때문에 UPDATE 쿼리를 실행하지 않는다.

 

@TableGenerator 속성

속성 설명 기본값
name 식별자 생성기 이름 필수
table 키생성 테이블 명 hibernate_sequences
pkColumnName 시퀀스 컬럼명 sequence_name
valueColumnNa 시퀀스 값 컬럼명 next_val
pkColumnValue 키로 사용할 값 이름 엔티티 이름
initialValue 초기값, 마지막으로 생성된 값이 기준 0
allocationSize 시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용) 50
catalog, schema DB 카탈로그, 스키마 이름   
uniqueConstraint 유니크 제약 조건  

 

 

사용 예시

@TableGenerator(
        name = "member_seq_generator",
        table = "sequence_table",
        pkColumnName = "member_seq",
        valueColumnName = "member_next_val",
        allocationSize = 50
)
@Entity
public class Member {
    @Id
    @GeneratedValue(
            strategy = GenerationType.TABLE,
            generator = "member_seq_generator"
    )
    private Long id;
    private String username;
    
    // 생성자 및 Setter 등 ..
}

 

DDL 자동 생성(H2 Database)

 

d. AUTO 전략

  • DB 방언에 따라 자동으로 IDENTITY, SEQUENCE, TABLE 전략을 선택한다.
  • DB를 변경해도 코드를 따로 수정할 필요가 없다.
  • 키 생성 전략이 정해지지 않은 개발 초기 단계에 사용하면 편리하다.
  • 실제 운영 환경에서는 권장하지 않는다.