Database/JPA / / 2024. 9. 10. 15:55

JPA 기본 키 생성 전략: IDENTITY, SEQUENCE, TABLE과 allocationSize의 이해

hibernate.hbm2ddl.auto는 Hibernate에서 데이터베이스 테이블과의 매핑을 자동으로 처리하는 설정 값이야. 이 속성은 애플리케이션이 실행될 때 Hibernate가 데이터베이스 스키마를 어떻게 처리할지 결정해 줘. 주로 다음과 같은 값들을 사용할 수 있어:

 

1. none (또는 빈 값): Hibernate가 아무것도 하지 않음. 데이터베이스 스키마에 영향을 미치지 않아.

2. validate: 애플리케이션 실행 시 Hibernate가 엔티티 클래스와 데이터베이스 테이블의 구조가 일치하는지 확인해. 일치하지 않으면 예외를 던져. 스키마를 수정하지 않아.

3. update: 데이터베이스에 존재하는 테이블을 엔티티 클래스에 맞게 업데이트해. 테이블이 없으면 생성하고, 변경된 컬럼이 있으면 수정하지만, 기존 데이터를 삭제하지 않음.

4. create: 애플리케이션 실행 시 기존 테이블을 모두 삭제하고, 엔티티에 맞춰 새로 생성해. 실행할 때마다 테이블이 새로 생성되기 때문에 데이터가 모두 지워져.

5. create-drop: create와 비슷하게 테이블을 새로 생성하지만, 애플리케이션이 종료되면 테이블을 삭제해.

6. drop: 엔티티에 맞는 테이블을 삭제해. 실행 중에 테이블이 없어져 버려.

 

보통 개발 환경에서는 update를 많이 쓰고, 프로덕션 환경에서는 validatenone을 사용하는 게 일반적이야. 왜냐면 프로덕션 환경에서는 데이터가 중요하니까 실수로 테이블을 삭제하거나 수정하지 않게 조심해야 하거든.

 

1. UNIQUE 제약조건의 의미

 

UNIQUE 제약조건은 테이블 내에서 하나의 컬럼(또는 컬럼 조합)의 값들이 중복되지 않도록 강제하는 기능이야. 즉, 해당 컬럼이나 컬럼 조합에 입력되는 값들은 서로 다른 값을 가져야 해. 데이터의 무결성을 유지하는 중요한 제약 중 하나야.

 

2. UNIQUE 제약조건의 사용 목적

 

데이터의 중복 방지: 예를 들어, 사용자 등록 시 이메일이 중복되지 않도록 하려면 UNIQUE 제약조건을 사용하면 돼.

데이터 무결성 유지: 특정 컬럼이 유일성을 가져야 하는 경우, 데이터의 일관성을 유지할 수 있어. 예를 들어, 제품의 일련번호나 유저의 이메일 주소 등은 중복되면 안 되는 값들이지.

 

3. UNIQUE와 PRIMARY KEY의 차이점

 

UNIQUE 제약조건과 PRIMARY KEY는 유사해 보이지만 몇 가지 차이점이 있어:

 

PRIMARY KEY: 하나의 테이블에서 오직 하나만 존재할 수 있고, 기본적으로 NOT NULL이 적용돼. 즉, 값이 반드시 있어야 하고 중복도 허용되지 않지.

UNIQUE: 하나의 테이블에서 여러 컬럼에 설정할 수 있고, NULL 값을 허용해. 단, NULL 값은 중복으로 취급되지 않아서 여러 개의 NULL 값이 들어갈 수 있어.

 

4. UNIQUE 제약조건을 설정하는 방법

 

(1) 단일 컬럼에 UNIQUE 제약조건 설정

 

단일 컬럼에 UNIQUE 제약조건을 설정하면 그 컬럼의 값들이 중복되지 않도록 제한해.

CREATE TABLE employees (
    id INT PRIMARY KEY,
    email VARCHAR(255) UNIQUE
);

 

여기서 email 컬럼에 UNIQUE 제약조건이 적용되었어. 따라서, 동일한 이메일을 가진 두 명의 직원은 등록할 수 없어.

 

(2) 다중 컬럼에 UNIQUE 제약조건 설정 (복합 유니크 제약조건)

 

여러 컬럼의 조합이 유일해야 할 때는 복합 UNIQUE 제약조건을 설정할 수 있어. 이를 통해 특정 컬럼의 개별 값이 아니라, 여러 컬럼의 조합이 고유해야 함을 보장해.

CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    product_id INT,
    user_id INT,
    UNIQUE (product_id, user_id)
);

 

이 예시에서는 product_iduser_id 컬럼의 조합이 고유해야 해. 즉, 같은 유저가 같은 제품을 두 번 주문할 수 없도록 제한해. 하지만 다른 사용자가 같은 제품을 주문하는 건 가능해.

 

(3) 기존 테이블에 UNIQUE 제약조건 추가

 

이미 생성된 테이블에 나중에 UNIQUE 제약조건을 추가할 수도 있어.

ALTER TABLE employees ADD CONSTRAINT unique_email UNIQUE (email);

 

이 명령어는 기존 employees 테이블에 email 컬럼에 대한 UNIQUE 제약조건을 추가하는 방법이야.

 

(4) 기존 UNIQUE 제약조건 삭제

 

만약 필요에 따라 UNIQUE 제약조건을 삭제하고 싶다면 아래와 같이 명령어를 사용할 수 있어.

ALTER TABLE employees DROP CONSTRAINT unique_email;

 

5. UNIQUE 제약조건 위반 시 발생하는 오류

 

만약 UNIQUE 제약조건이 걸린 컬럼에 중복된 값이 입력되면, 데이터베이스는 오류를 발생시켜. 예를 들어, email 컬럼에 이미 존재하는 값을 입력하려고 하면 오류 메시지가 나올 거야:

ERROR: duplicate key value violates unique constraint "employees_email_key"

 

이렇게 UNIQUE 제약조건을 위반할 경우, 데이터베이스는 해당 레코드를 삽입하거나 업데이트하는 것을 막아줘.

 

6. 실제 사용 시의 예

 

사용자 테이블을 하나 만들어 볼게. 여기서 사용자의 usernameemail이 중복되지 않도록 UNIQUE 제약조건을 설정해.

CREATE TABLE users (
    id INT PRIMARY KEY,
    username VARCHAR(50) UNIQUE,
    email VARCHAR(100) UNIQUE,
    password VARCHAR(100)
);

 

username: 이 컬럼은 사용자 이름을 나타내고, UNIQUE 제약조건이 걸려 있어서 동일한 사용자 이름을 가진 사용자를 중복해서 등록할 수 없어.

email: 이메일도 마찬가지로 UNIQUE 제약조건이 걸려 있어서 같은 이메일을 가진 두 사용자를 등록할 수 없어.

 

JPA에서 엔티티의 @Id 필드에 대해 기본 키를 자동으로 생성하는 방법은 여러 가지가 있는데, 그 중 대표적인 세 가지 방식이 IDENTITY, SEQUENCE, 그리고 TABLE이야. 각각의 방식은 데이터베이스에서 기본 키를 생성하는 방법을 결정해. 자세히 알아볼게!

 

1. IDENTITY 전략

 

IDENTITY 전략은 데이터베이스의 자동 증가(AUTO_INCREMENT) 기능을 사용해 기본 키를 생성하는 방식이야. 데이터베이스에서 기본 키를 직접 생성하고, JPA는 그 값을 받아와 사용할 수 있어.

 

특징

 

자동 증가: 데이터베이스 자체에서 기본 키 값을 생성하므로, 별도의 추가 작업 없이 각 레코드가 삽입될 때 자동으로 기본 키가 증가해.

즉시 삽입: 엔티티가 영속성 컨텍스트에 저장될 때, 데이터베이스에 즉시 삽입 쿼리가 실행돼. (보통 flush 단계에서 삽입되는데, IDENTITY의 경우는 바로 실행됨)

데이터베이스 종속적: 주로 MySQL, PostgreSQL 같은 데이터베이스에서 지원하고, 데이터베이스에 의존적인 방식이야.

 

예시

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

 

장점

 

설정이 간단하고, 데이터베이스에서 키를 자동으로 관리해주기 때문에 특별한 설정이 필요 없어.

 

단점

 

IDENTITY 전략은 즉시 삽입이 일어나기 때문에 배치 처리나 최적화 작업에서 불리할 수 있어. 대량의 데이터를 한 번에 삽입할 때 성능이 떨어질 수 있음.

 

2. SEQUENCE 전략

 

SEQUENCE 전략은 데이터베이스의 시퀀스(Sequence) 객체를 사용해 기본 키 값을 생성하는 방식이야. 시퀀스는 데이터베이스에서 고유한 숫자 값을 생성하는 객체로, 이를 통해 각 레코드의 기본 키가 중복되지 않도록 할 수 있어.

 

특징

 

시퀀스 사용: 시퀀스 객체에서 값을 가져와 기본 키로 사용해.

사전에 할당: JPA가 시퀀스에서 미리 ID 값을 가져와 메모리에 저장한 뒤, 엔티티가 저장될 때 그 값을 사용해. (대량 삽입 시 성능이 더 좋음)

데이터베이스 종속적: 주로 Oracle, PostgreSQL 같은 데이터베이스에서 지원하는 방식이야.

 

예시

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "member_seq")
@SequenceGenerator(name = "member_seq", sequenceName = "member_sequence", allocationSize = 1)
private Long id;

 

@SequenceGenerator를 통해 시퀀스를 정의해. sequenceName은 데이터베이스에서 사용할 시퀀스 이름이고, allocationSize는 한번에 미리 가져올 시퀀스의 개수를 지정해.

 

장점

 

성능이 좋음: 시퀀스를 미리 여러 개 가져와 메모리에 저장해 두기 때문에, 대량의 데이터를 삽입할 때 성능이 좋음. allocationSize 값을 설정하면 미리 여러 개의 시퀀스를 가져올 수 있어.

 

단점

 

시퀀스를 지원하지 않는 데이터베이스에서는 사용할 수 없어.

 

3. TABLE 전략

 

TABLE 전략은 별도의 키 생성 테이블을 만들어서 그 테이블을 사용해 기본 키를 생성하는 방식이야. 데이터베이스의 시퀀스나 자동 증가 기능을 사용할 수 없는 환경에서 주로 쓰여.

 

특징

 

키 생성용 테이블 사용: 별도의 테이블을 만들어서, 그 테이블에 고유 키 값을 저장하고, 새로운 레코드를 삽입할 때마다 그 테이블에서 값을 가져와 사용해.

모든 DB에서 사용 가능: 시퀀스나 자동 증가 기능이 없는 데이터베이스에서도 사용할 수 있어. 데이터베이스에 종속되지 않음.

 

예시

@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "member_table")
@TableGenerator(name = "member_table", table = "id_gen", pkColumnName = "gen_name", valueColumnName = "gen_value", allocationSize = 1)
private Long id;

 

@TableGenerator는 키 생성 테이블을 설정하는데, table은 키 생성 테이블 이름을 의미하고, pkColumnName은 테이블에서 사용할 키의 이름, valueColumnName은 키의 값을 저장할 컬럼이야.

 

장점

 

모든 데이터베이스에서 사용 가능: 시퀀스나 자동 증가를 지원하지 않는 DB에서도 사용 가능하다는 게 장점이야.

 

단점

 

성능이 떨어짐: 별도의 테이블에서 키 값을 가져오는 추가 작업이 필요하기 때문에, 성능이 다른 전략에 비해 떨어질 수 있어. 특히 대량 삽입 시 성능 차이가 클 수 있어.

 

정리

 

1. IDENTITY:

데이터베이스가 자동으로 기본 키를 생성.

간단하지만, 대량 삽입 시 성능이 떨어질 수 있음.

주로 MySQL, PostgreSQL에서 사용.

2. SEQUENCE:

데이터베이스 시퀀스 객체를 통해 기본 키를 생성.

대량 삽입 시 성능이 좋고, 미리 시퀀스 값을 가져와 효율적으로 사용할 수 있음.

주로 Oracle, PostgreSQL에서 사용.

3. TABLE:

별도의 키 생성 테이블을 사용해 기본 키를 생성.

데이터베이스에 상관없이 사용할 수 있지만, 성능이 다른 전략에 비해 떨어질 수 있음.

 

각 전략은 데이터베이스의 특성과 애플리케이션 요구 사항에 맞춰 선택하면 돼. 성능이 중요한 경우에는 SEQUENCE가 유리하고, 간단한 상황에서는 IDENTITY를 사용하는 것이 일반적이야. TABLE은 데이터베이스에 종속되지 않으면서도 유연하게 사용할 수 있지만 성능이 떨어지는 경우가 많아서 잘 쓰이지 않아.

 

Tip💡

왜 두 번째 생성된 값이 52인지allocationSize와 관련된 JPA의 시퀀스 처리 방식 때문에 그래.

 

1. allocationSize = 50의 의미

 

allocationSize = 50은 JPA가 한 번에 50개의 시퀀스 값을 미리 가져와서 사용하는 것을 의미해. 이 설정은 데이터베이스와의 통신을 줄이고 성능을 높이기 위해 쓰여.

 

2. JPA의 시퀀스 처리 방식

 

JPA는 다음과 같이 시퀀스 값을 관리해:

 

1. 첫 번째 엔티티가 생성되면, JPA는 데이터베이스에 시퀀스를 요청해. 그리고 1부터 50까지의 시퀀스 값을 한꺼번에 가져와.

2. 첫 번째 엔티티1이라는 시퀀스 값을 사용하게 돼.

3. JPA는 메모리에서 2부터 50까지의 값을 가지고 있다가, 필요할 때 사용해.

4. 두 번째 시퀀스 요청이 발생하면, JPA는 이미 가져온 범위 (1~50)의 값을 모두 사용했기 때문에, 다시 데이터베이스에 시퀀스를 요청해. 그러면 데이터베이스는 **51**이라는 시퀀스 값을 반환하게 돼.

하지만, JPA는 성능 최적화를 위해 다음 범위51부터 100까지의 시퀀스 값을 한 번에 가져와서 사용해.

 

3. 왜 51이 아닌 52부터 시작되는가?

 

JPA는 성능 최적화를 위해 시퀀스 값을 메모리에서 관리해. 그런데, JPA가 시퀀스 값을 할당할 때, allocationSize만큼의 범위를 할당받고, 첫 번째 값을 사용하지 않고 넘어가.

즉, allocationSize = 50일 때, **첫 번째 값(51)**은 내부적으로 버려지고, **두 번째 값(52)**부터 사용되는 거야. 그래서 두 번째 엔티티부터는 52가 사용되는 거지.

 

4. 왜 이런 방식이 적용되는가?

 

이 방식은 JPA의 성능 최적화를 위한 메커니즘 중 하나야. JPA가 ID 값을 미리 할당받아 사용하는 과정에서, 시퀀스의 첫 번째 값(즉, 할당받은 시퀀스 범위의 첫 번째 값)을 버리고, 두 번째 값부터 사용하는 방식을 취해.

 

이게 일부 데이터베이스와 JPA 간의 호환성 문제를 방지하기 위한 설계라고 생각하면 돼. 데이터베이스가 시퀀스를 반환할 때마다 값이 하나씩 증가하는 방식과, JPA가 메모리에서 값을 미리 관리하는 방식 사이의 차이를 최소화하기 위해 첫 번째 값을 사용하지 않는 거야.

 

정리:

 

첫 번째 시퀀스 요청 시 JPA는 1~50을 가져오고, 첫 번째 엔티티의 ID는 1이 돼.

두 번째 시퀀스 요청 시 JPA는 51~100을 가져오지만, **첫 번째 값(51)**을 사용하지 않고 52부터 사용하게 돼.

 

따라서, allocationSize를 설정한 경우에는 시퀀스의 첫 번째 값이 건너뛰어지고 두 번째 값부터 사용된다는 점을 기억하면 돼!

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유