본문 바로가기
JPA Study

JPA 자동생성전략 3가지방법 ( IDENTITY, SEQUENCE, TABLE )

by xogns93 2024. 9. 11.

JPA에서는 기본 키를 자동으로 생성하는 다양한 전략을 제공합니다. 그중 대표적인 세 가지 자동 생성 전략은 IDENTITY, SEQUENCE, TABLE입니다. 각 전략은 데이터베이스의 동작 방식에 따라 다르게 동작하며, 적절한 자동 생성 전략을 선택하는 것이 중요합니다.

 

1. IDENTITY 전략

  • IDENTITY 전략은 데이터베이스에 의존하여 기본 키를 자동으로 생성합니다.
  • 주로 MySQL이나 SQL Server와 같은 데이터베이스에서 사용되며, 데이터베이스의 AUTO_INCREMENT 기능을 통해 기본 키가 생성됩니다.
  • 즉, 엔티티를 영속성 컨텍스트에 추가할 때 바로 쿼리가 실행되고, 자동 증가된 값이 기본 키로 설정됩니다.

예시:

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)  // IDENTITY 전략 사용
    private Long id;

    private String name;
}
  • 장점: 설정이 간단하며, 데이터베이스가 자동으로 키를 생성.
  • 단점: 엔티티를 영속성 컨텍스트에 추가할 때마다 쿼리가 즉시 실행되어야 하기 때문에, 다른 전략에 비해 성능이 떨어질 수 있음.

 

2. SEQUENCE 전략

  • SEQUENCE 전략은 데이터베이스 시퀀스(Sequence) 객체를 사용하여 기본 키를 생성합니다.
  • 주로 Oracle, PostgreSQL과 같은 데이터베이스에서 사용됩니다. 시퀀스는 데이터베이스에서 독립적인 객체로, 연속적인 숫자를 반환합니다.
  • @SequenceGenerator 어노테이션을 사용하여 시퀀스 이름과 기타 속성을 지정할 수 있습니다.

예시:

@Entity
@SequenceGenerator(name = "user_seq", sequenceName = "user_sequence", allocationSize = 1)  // 시퀀스 생성 설정
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq")  // SEQUENCE 전략 사용
    private Long id;

    private String name;
}
  • @SequenceGenerator: name은 제너레이터의 이름을 지정하고, sequenceName은 데이터베이스에서 실제 사용하는 시퀀스 객체의 이름을 지정합니다. allocationSize는 한 번에 할당할 시퀀스 값의 개수를 설정합니다.
  • 장점: 엔티티의 키를 미리 배치(batch)로 가져와 쿼리 성능을 높일 수 있음.
  • 단점: 시퀀스를 지원하지 않는 데이터베이스에서는 사용할 수 없음.

 

3. TABLE 전략

  • TABLE 전략은 기본 키를 생성하기 위해 별도의 키 생성 테이블을 사용합니다.
  • 모든 데이터베이스에서 사용할 수 있으며, 별도의 테이블을 만들어 키 값을 관리합니다.
  • TABLE 전략은 시퀀스를 지원하지 않는 데이터베이스에서도 사용할 수 있지만, 성능은 다른 전략에 비해 떨어질 수 있습니다.

예시:

@Entity
@TableGenerator(
    name = "user_gen",  // 테이블 제너레이터 이름
    table = "key_generator",  // 키를 생성하는 테이블 이름
    pkColumnName = "gen_name",  // 키 생성용 테이블에서 사용할 PK 컬럼
    valueColumnName = "gen_value",  // 키 값이 저장될 컬럼
    pkColumnValue = "user_id",  // 테이블에 저장될 키 식별자
    allocationSize = 1  // 키 값을 증가시키는 단위
)
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "user_gen")  // TABLE 전략 사용
    private Long id;

    private String name;
}
  • @TableGenerator: 키를 저장하고 관리하는 별도의 테이블을 설정할 수 있습니다.
    • table: 키를 저장할 테이블의 이름.
    • pkColumnName: 테이블에서 기본 키로 사용할 컬럼.
    • valueColumnName: 키 값을 저장할 컬럼.
    • pkColumnValue: 생성되는 키의 이름.
  • 장점: 시퀀스 기능이 없는 데이터베이스에서도 사용할 수 있음.
  • 단점: 성능이 상대적으로 느림. 별도의 테이블에 키 값을 저장하고 관리해야 하기 때문에, 테이블에 접근할 때 성능 저하가 발생할 수 있음.

 

자동 생성 전략 선택 기준

  • IDENTITY: MySQL, SQL Server와 같은 AUTO_INCREMENT를 지원하는 데이터베이스에서 사용. 키를 자동으로 생성하고 바로 삽입하는 구조.
  • SEQUENCE: Oracle, PostgreSQL과 같이 시퀀스를 지원하는 데이터베이스에서 사용. 키 생성이 데이터베이스의 시퀀스 객체에 의존.
  • TABLE: 모든 데이터베이스에서 사용할 수 있지만, 성능이 중요하다면 권장되지 않음. 시퀀스 기능이 없는 데이터베이스에서 주로 사용.

정리

  1. IDENTITY: 기본 키를 데이터베이스에서 자동 생성(AUTO_INCREMENT)하는 방식. 즉시 INSERT 쿼리를 실행.
  2. SEQUENCE: 데이터베이스 시퀀스를 사용하여 기본 키를 생성. 미리 키를 가져올 수 있어서 성능이 좋음.
  3. TABLE: 별도의 테이블을 만들어 기본 키를 생성. 시퀀스가 없는 데이터베이스에서도 사용 가능하지만, 성능은 상대적으로 떨어질 수 있음.

 

각 전략은 데이터베이스 종류와 요구 사항에 맞춰 선택하는 것이 중요합니다.

 


 

**테이블 전략(TABLE 전략)**은 여러 테이블이 하나의 프라이머리 키를 공유하고, 이를 위해 별도의 테이블을 사용하여 기본 키 값을 관리하는 방식입니다. 이 전략은 시퀀스를 지원하지 않는 데이터베이스에서도 사용할 수 있으며, 테이블을 통해 기본 키 값을 관리하여 충돌을 방지합니다. 특히 동시성 문제를 처리할 때 FOR UPDATE같은 락을 사용하여 키 값을 안전하게 증가시키는 방법을 사용할 수 있습니다.

상황 설명:

  • 여러 테이블이 공통된 프라이머리 키 시퀀스를 공유해야 할 때, 테이블 전략을 사용하면 각 테이블에서 고유한 ID를 생성하지 않고, 하나의 공통된 테이블을 통해 프라이머리 키 값을 증가시킬 수 있습니다.
  • 이를 통해 ID 중복을 피하고 통합된 키 관리를 할 수 있습니다.

테이블 전략 설정

  1. 키를 관리하는 별도의 테이블 생성: 먼저, 데이터베이스에 키를 관리하는 테이블을 생성해야 합니다. 이 테이블은 각 테이블에 고유한 프라이머리 키 값을 제공하는 역할을 합니다.

예시 테이블 생성:

CREATE TABLE key_generator (
    gen_name VARCHAR(50) NOT NULL,  -- 키의 이름 (테이블 이름 등을 저장)
    gen_value BIGINT NOT NULL,      -- 현재 키 값
    PRIMARY KEY (gen_name)
);
  • gen_name: 어떤 테이블의 키를 관리하는지 식별할 수 있는 이름입니다.
  • gen_value: 해당 테이블에서 사용할 마지막 프라이머리 키 값을 저장합니다. 새로운 프라이머리 키가 필요할 때 이 값을 증가시켜 사용합니다.
  1. JPA에서 테이블 전략 설정

JPA에서는 @TableGenerator 어노테이션을 사용하여 테이블 전략을 정의할 수 있습니다. 이 어노테이션은 기본 키를 관리할 테이블과 키 값을 관리하는 방법을 정의합니다.

예시 JPA 설정:

@Entity
@TableGenerator(
    name = "shared_id_gen",           // 제너레이터의 이름
    table = "key_generator",          // 키를 관리하는 테이블
    pkColumnName = "gen_name",        // 테이블의 PK 컬럼 이름
    valueColumnName = "gen_value",    // 키 값이 저장되는 컬럼
    pkColumnValue = "shared_id",      // 사용할 테이블(또는 엔티티)의 이름
    allocationSize = 1                // 한 번에 증가시킬 키 값 (기본 1)
)
public class EntityA {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "shared_id_gen")
    private Long id;

    private String name;
}

@Entity
@TableGenerator(
    name = "shared_id_gen",           // 동일한 제너레이터 사용
    table = "key_generator",
    pkColumnName = "gen_name",
    valueColumnName = "gen_value",
    pkColumnValue = "shared_id",      // 동일한 키를 공유하는 테이블
    allocationSize = 1
)
public class EntityB {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "shared_id_gen")
    private Long id;

    private String description;
}
  • name: shared_id_gen이라는 이름으로 키를 관리하는 제너레이터를 설정합니다. 이 이름은 다른 엔티티에서도 같은 키 제너레이터를 사용하도록 설정할 수 있습니다.
  • table: key_generator라는 별도의 테이블에서 키 값을 관리합니다.
  • pkColumnValue: 여러 테이블이 동일한 ID 시퀀스를 공유해야 하므로 pkColumnValue에 동일한 값을 지정합니다.
  • allocationSize: 기본 키 값이 얼마나 증가할지 설정합니다. 1로 설정하면 매번 하나씩 증가합니다.

 

동시성 문제 해결 (FOR UPDATE)

여러 테이블이 동일한 프라이머리 키 값을 공유하고 키 값을 증가시키는 상황에서는 동시성 문제가 발생할 수 있습니다. 즉, 두 개 이상의 트랜잭션이 동시에 새로운 키 값을 가져오려고 하면 충돌이 발생할 수 있습니다. 이를 방지하기 위해 데이터베이스 락을 사용하여 동시성 문제를 해결할 수 있습니다.  

FOR UPDATE를 사용한 락:

FOR UPDATE 구문은 데이터베이스에서 특정 행을 업데이트하거나 삭제하기 위해 락을 거는 구문입니다. 이 락은 다른 트랜잭션이 해당 데이터를 읽거나 수정하는 것을 방지합니다.

SQL 예시:

SELECT gen_value FROM key_generator WHERE gen_name = 'shared_id' FOR UPDATE;

위 쿼리는 gen_value에 대해 을 걸어, 다른 트랜잭션이 동시에 접근하지 못하도록 합니다. 이후 새로운 키 값을 증가시키고, 그 값으로 기본 키를 할당할 수 있습니다.

JPA에서의 사용:

JPA에서는 기본적으로 FOR UPDATE를 직접 사용하는 것이 아니라, 트랜잭션 관리를 통해 동시성 문제를 해결할 수 있습니다. 하지만 Native Query를 사용해 FOR UPDATE와 같은 데이터베이스 락을 적용할 수도 있습니다.

Query query = entityManager.createNativeQuery(
    "SELECT gen_value FROM key_generator WHERE gen_name = 'shared_id' FOR UPDATE");

정리

  • **테이블 전략(Table Generation)**을 사용하면 여러 테이블이 하나의 공통된 프라이머리 키 시퀀스를 공유할 수 있습니다.
  • 이를 위해 키 값을 관리하는 별도의 테이블을 설정하고, JPA에서 이를 기반으로 자동 키 생성을 관리합니다.
  • 동시성 문제를 해결하기 위해 **FOR UPDATE**를 사용하여 키 값을 가져오는 동안 다른 트랜잭션이 동일한 키에 접근하지 못하도록 막을 수 있습니다.

이 전략을 사용하면 여러 테이블이 동일한 프라이머리 키 값을 공유하면서도 안전하게 키 값을 증가시킬 수 있는 방법을 제공합니다.

'JPA Study' 카테고리의 다른 글

서브쿼리 (Subquery)  (1) 2024.09.23
이너 조인(Inner Join)과 레프트 조인(Left Join)  (0) 2024.09.20
JPA가 제공하는 주요 CRUD 기능  (0) 2024.09.10
JDBC와 JPA의 관계  (0) 2024.09.09
JPA(Java Persistence API)  (1) 2024.08.08