기본 키 매핑에는 크게 직접 할당과 자동 생성 2가지로 분류할 수 있다.
직접 할당은 기본 키를 애플리케이션에서 직접 할당한다. 자동 생성은 대리 키 사용 방식을 사용하는데 데이터베이스에 따라 IDENTITY, SEQUENCE, TABLE 3가지 방법이 있다.
자동 생성 전략이 다양한 이유는 데이터베이스 벤더마다 지원하는 방식이 다르기 때문이다. 예를 들어 오라클 데이터베이스는 시퀀스를 제공하지만 MySQL은 시퀀스를 제공하지 않는다.
대신에 MySQL은 기본 키 값을 자동으로 채워주는 AUTO_INCREMENT 기능을 제공한다. 따라서 사용하는 데이터베이스에 따라 생성 전략을 다르게 사용할 수 있다.
기본 키를 직접 할당하려면 다음 코드와 같이 @Id로 매핑하고 em.persist()로 엔티티를 저장하기 전에 애플리케이션에서 기본 키를 직접 할당하면 된다.
@Id
@Column(name = "id")
private String id;
.
.
.
Board board = new Board();
board.setId("id1"); // 기본 키 직접 할당
em.persist(board);
@Id 적용 가능한 자바 타입은 다음과 같다.
IDENTITY는 기본 키 생성을 데이터베이스에 위임하는 전략이다. 주로 MySQL, PostgreSQL, SQL Server DB2에서 사용한다. 예를 들어 MySQL의 AUTO_INCREMENT 기능은 데이터베이스가 기본 키를 자동으로 생성해준다.
IDENTITY 전략은 데이터베이스에 값을 저장하고 나서야 기본 키 값을 구할 수 있을 때 사용한다. 개발자가 엔티티에 직접 식별자를 할당하면 @Id 어노테이션만 있으면 되지만 지금처럼 식별자가 자동 생성되는 경우에는 @GeneratedValue 어노테이션을 사용하고 식별자 생성 전략을 선택해야 한다. IDENTITY 전략을 사용하려면 @GeneratedValue의 strategy 속성 값을 GeneratinType.IDENTITY로 지정하면 된다.
다음 코드는 IDENTITY를 매핑한 예제이다.
@Entity
public class Board{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
}
다음은 IDENTIY를 사용한 예제이다.
Board board = new board();
em.persist(board);
데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트이다. SEQUENCE 전략은 이 시퀀스를 사용해서 기본 키를 생성한다. 이 전략은 시퀀스를 지원하는 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용할 수 있다.
시퀀스 전략을 사용하려면 먼저 시퀀스를 생성해야 한다.
CREATE TABLE BOARD (
ID BIGINT NOT NULL PRIMARY KEY,
DATA VARCHAR(255)
)
// 시퀀스 생성
CREATE SEQUENCE BOARD_SEQ START WITH 1 INCREMENT BY 1;
다음은 시퀀스를 매핑한 예제이다.
@Entity
@SequenceGenerator(
name = "BOARD_SEQ_GENERATOR",
sequenceName = "BOARD_SEQ",
initialValue = 1, allocationSize = 1)
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "BOARD_SEQ_GENERATOR")
private Long id;
...
}
다음은 시퀀스를 사용한 예제이다.
Board board = new board();
em.persist(board);
시퀀스 사용 코드는 IDENTITY 전략과 같지만 내부 동작 방식은 다르다. SEQUENCE 전략은 em.persist()를 호출하기 전에 먼저 데이터베이스 시퀀스를 사용해서 식별자를 조회한다. 그리고 조회한 식별자를 엔티티에 할당한 후에 엔티티를 영속성 컨텍스트에 저장한다. 이후 트랜잭션을 커밋해서 플러시가 일어나면 엔티티를 데이터베이스에 저장한다.
반대로 IDENTITY 전략은 먼저 엔티티를 데이터베이스에 저장한 후에 식별자를 조회해서 엔티티의 식별자에 할당한다.
다음은 @SequenceGenerator의 속성이다.
| 속성 | 기능 | 기본값 |
|---|---|---|
| name | 식별자 생성기 이름 | 필수 |
| sequenceName | 데이터베이스에 등록되어 있는 시퀀스 이름 | hibernate_sequences |
| initialValue | DDL 생성 시에만 사용됨. 시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정한다. | 1 |
| allocationSize | 시퀀스 한 번 호출에 증가하는 수(성능 최저화에 사용됨) | 50 |
| catalog
schema | 데이터베이스 catalog, schema 이름 | |
TABLE 전략은 키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 칼럼을 만들어 데이터베이스 시퀀스를 흉내내는 전략이다. 이 전략은 테이블을 사용하므로 모든 데이터베이스에 적용할 수 있다.
TABLE 전략을 사용하려면 먼저 키 생성 용도로 사용할 테이블을 만들어야 한다.
CREATE TABLE MY_SEQUENCES (
SEQUENCE_NAME VARCHAR(255) NOT NULL,
NEXT_VAL BIGINT,
PRIMARY KEY (SEQUENCE_NAME)
)
sequence_name 칼럼을 시퀀스 이름으로 사용하고 next_val 칼럼을 시퀀스 값으로 사용한다.
다음은 TABLE 전략 매핑 코드이다.
@Entity
@TableGenerator(
name = "BOARD_SEQ_GENERATOR",
table = "MY_SEQUENCES",
pkColumnValue = "BOARD_SEQ", allocationSize = 1)
public class Board {
@Id
@GeneratedValue(strategy = GeneraionType.TABLE,
generator = "BOARD_SEQ_GENERATOR")
private Long id;
}
다음은 TABLE 사용 코드이다.
Board board = new Board();
em.persist(board);
TABLE 전략은 시퀀스 대신에 테이블을 사용한다는 것만 제외하면 시퀀스 전략과 내부 동작방식이 같다.
다음은 @TableGenerator의 속성이다.
| 속성 | 기능 | 기본값 |
|---|---|---|
| name | 식별자 생성기 이름 | 필수 |
| table | 키생성 테이블명 | hibernate_sequences |
| pkColumnName | 시퀀스 칼럼명 | sequence_name |
| valueColumnName | 시퀀스 값 칼럼명 | next_val |
| pkColumnValue | 키로 사용할 값 이름 | 엔티티 이름 |
| initialValue | 초기 값, 마지막으로 생성된 값이 기준이다. | 0 |
| allocationSize | 시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용됨) | 50 |
| catalogschema | 데이터베이스 catalog, schema 이름 | |
| uniqueCaonstraints(DDL) | 유니크 제약 조건을 지정할 수 있다. |
데이터베이스의 종류도 많고 기본 키를 만드는 방법도 다양하다. GenerationType.AUTO는 선택한 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택한다.
AUTO 전략의 장점은 데이터베이스를 변경해도 코드를 수정할 필요가 없다는 것이다. 특히 키 생성 전략이 아직 확정되지 않은 개발 초기 단계나 프로토타입 개발 시 편리하게 사용할 수 있다.
AUTO를 사용할 때 SEQUENCE나 TABLE 전략이 선택되면 시퀀스나 키 생성용 테이블을 미리 만들어 두어야 한다. 만약 스키마 자동 생성 기능을 사용한다면 하이버네티으가 기본값을 상요해서 적절한 시퀀스나 키 생성용 테이블을 만들어 줄 것이다.
영속성 컨텍스트는 엔티티를 식별자 값으로 구분하므로 엔티티를 영속 상태로 만들려면 식별자 값이 반드시 있어야 한다. em.persist()를 호출한 직후에 발생하는 일을 식별자 할당 전략별로 정리하면 다음과 같다.