-
JPA 기본키 매핑 전략개발/JPA 2022. 7. 20. 12:21반응형
JPA가 제공하는 데이터베이스 기본키 생성 전략은 다음과 같습니다.
- 직접할당 : 기본 키를 애플리케이션에서 직접 할당한다.
- 자동생성 : 대리 키 사용 방식
- IDENTITY : 기본 키 생성을 데이터베이스에 위임ㅎ나다.
- SEQUENCE : 데이터베이스 시퀀스를 사용해서 기본 키를 할당한다.
- TABLE : 키 생성 테이블을 사용한다.
자동 생성 전략이 이렇게 다양한 이유는 데이터베이스 벤더마다 지원하는 방식이 다르기 떄문입니다. 예를 들어 오라클 데이터베이스는 시퀀스를 제공하지만 MySQL은 시퀀스를 제공하지 않습니다.
따라서 SEQUENCE나 IDENTIFY 전략은 사용하는 데이터베이스에 의존합니다. TABLE 전략은 키 생성용 테이블을 하나 만들어두고 마치 시퀀스처럼 사용하는 방법입니다. 이 전략은 테이블을 활용하므로 모든 데이터베이스에서 사용할 수 있습니다.
1. 기본 키 직접 할당 전략
기본 키를 직접 할당하려면 다음 코드와 같이 @Id로 매핑하면 됩니다.
@ID @Column(name="id") private String id;
@Id 적용 가능 자바 타입은 다음과 같습니다.
- 자바 기본형(primitive type)
- 자바 래퍼(Wrapper)형
- String
- java.util.Date
- java.sql.Date
- java.math.BigDecimal
- java.math.BigInteger
기본 키 직접 할당 전략은 em.persist()로 엔티티를 저장하기 전에 애플리케이션에서 기본 키를 직접 할당하는 방법입니다.
Board board = new Board(); board.setId("id1"); // 기본 키 직접 할당 em.persist(board);
2. IDENTITY 전략
IDENTITY는 기본 키 생성을 데이터베이스에 위임하는 전략입니다. 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용합니다. 예를들어 MySQL의 AUTO_INCREMENT 기능은 데이터베이스가 기본 키를 자동으로 생성해줍니다.
개발자가 엔티티에 직접 식별자를 할당하면 @Id 어노테이션만 있으면 되지만 그렇지 않은 경우에는 @GeneratedValue 어노테이션을 사용하고 식별자 생성 전략을 선택해야 합니다. IDENTITY 전략을 사용하려면 @GeneratedValue의 strategy 속성 값을 GenerationType.IDENTITY로 지정하면 됩니다. 이 전략을 사용하면 JPA는 기본 키 값을 얻어오기 위해 데이터베이스를 추가로 조회합니다.
@Entity public class Board { @Id @GeneratedValue(strategy = GgenerationType.IDENTITY) private Long id; ... }
private static void logic(EntityManager em) { Board board = new Board(); em.persist(board); System.out.println("board.id = " + board.getId()); } // 출력: board.id = 1;
※ 엔티티가 영속 상태가 되려면 식별자가 반드시 필요합니다. 그런데 IDENTITY 식별자 생성 전략은 엔티티를 데이터베이스에 저장해야 식별자를 구할 수 있으므로 em.persist()를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달됩니다. 따라서 이 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않습니다.
3. SEQUENCE 전략
데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 데이터베이스 오브젝트입니다. 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; ... }
우선 사용할 데이터베이스 시퀀스를 매핑해야 합니다.
@SequenceGenerator를 사용해서 BOARD_SEQ_GENERATOR라는 시퀀스 생성기를 등록했습니다. 그리고 sequenceName 속성의 이름으로 BOARD_SEQ를 지정했는데 JPA는 이 시퀀스 생성기를 실제 데이터베이스의 BOARD_SEQ 시퀀스와 매핑합니다.다음으로 키 생성 전략을 GenerationType.SEQUENCE로 설정하고 generator = "BOARD_SEQ_GENERATOR"로 방금 등록한 시퀀스 생성기를 선택했습니다. 이제부터 id 식별자 값은 BOARD_SEQ_GENERATOR 시퀀스 생성기가 할당합니다.
private static void logic(EntityManager em) { Board board = new Board(); em.persist(board); System.out.println("board.id = " + board.getId()); } // 출력: board.id = 1;
시퀀스 사용 코드는 IDENTITY 전략과 같지만 내부 동작 방식은 다릅니다. SEQUENCE 전략은 em.persist()를 호출할 때 먼저 데이터베이스 시퀀스를 사용해서 식별자를 조회합니다. 그리고 조회한 식별자를 엔티티에 할당한 후에 엔티티를 영속성 컨텍스트에 저장합니다. 이후 트랜잭션을 커밋해서 플러시가 일어나면 엔티티를 데이터베이스에 저장합니다. 반대로 이전에 설명했던 IDENTITY 전략은 먼저 엔티티를 데이터베이스에 저장한 후에 식별자를 조회해서 엔티티의 식별자에 할당합니다.
@SequenceGenerator
속성 기능 기본값 name 식별자 생성기 이름 필수 sequenceName 데이터베이스에 등록되어 있는 시퀀스 이름 hibernate_sequence initialValue DDL 생성 시에만 사용됨, 시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정한다. 1 allocationSize 시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용됨) 50 catalog, schema 데이터베이스 catalog, schema 이름 ※SequenceGenerator.allocationSize의 기본값이 50인 것에 주의해야 합니다. JPA가 기본으로 생성하는 데이터베이스 시퀀스는 create sequence[sequenceName] start with 1 increment by 50 이므로 시퀀스를 호출할 때마다 값이 50씩 증가합니다. 기본값이 50인 이유는 최적화때문인데, 데이터베이스 시퀀스 값이 하나씩 증가하도록 설정되어 있으면 이 값을 반드시 1로 설정해야 합니다.
※SEQUENCE 전략과 최적화
SEQUENCE 전략은 데이터베이스 시퀀스를 통해 식별자를 조회하는 추가 작업이 필요합니다. 따라서 데이터베이스와 2번 통신합니다.
1) 식별자를 구하려고 데이터베이스 시퀀스를 조회한다.
EX) SELECT BOARD_SEQ.NEXTVAL FORM DUAL
2) 조회한 시퀀스를 기본 키 값으로 사용해 데이터베이스에 저장ㅎ나다.
EX) INSERT INTO BOARD...
JPA는 시퀀스에 접근하는 횟수를 줄이기 위해 @SequnceGenerator.allocaitonSize를 사용합니다. 간단히 설명하자면 여기에 설정한 값만큼 한 번에 키숸스 값을 증가시키고 나서 그만큼 메모리에 시퀀스 값을 할당합니다. 예를 들어 allocationSize 값이 50이면 시퀀스를 한번에 50 증가시킨 다음 51~1000까지 메모리에서 식별자를 할당합니다.
이 최적화 방법은 시퀀스 값을 선점하므로 여러 JVM이 동시에 동작해도 기본 키 값이 충돌하지 않는 장접이 있습니다. 반면에 데이터베이스에 직접 접근해서 데이터를 등록할 때 시퀀스 값이 한번에 많이 증가한다는 점을 염두해두어야 합니다. 이런 상황이 부담스럽고 INSERT 성능이 중요하지 않으면 allocationSize의 값을 1로 설정하면 됩니다.
4. TABLE 전략
TABLE 전략은 키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내내는 전략입니다. 이 전략은 테이블을 사용하므로 모든 데이텅베이스에 적용할 수 있습니다.
TABLE 전략을 사용하려면 먼저 키 생성 용도로 사용할 테이블을 만들어야 합니다.
create table MY_SEQUENCES ( sequence_name varchar(255) not null, next_val bigint, primary key (sequence_name) )
sequence_name 컬럼을 시퀀스 이름으로 사용하고 next_val 컬럼을 시퀀스 값으로 사용합니다. 참고로 컬럼의 이름은 병경할 수 있는데 여기서 사용한 것이 기본값입니다.
@Entity @TableGenerator( name = "BOARD_SEQ_GENERATOR", table = "MY_SEQUENCES", pkColumnValue = "BOARD_SEQ", allocationSize = 1) public class Board { @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "BOARD_SEQ_GENERATOR") private Long id; .... }
@TableGenerator를 사용해서 테이블 키 생성기를 등록합니다. 여기서는 BOARD_SEQ_GENERATOR라는 이름의 테이블 키 생성기를 등록하고, MY_SEQUENCES 테이블을 키 생성용 테이블로 매핑했습니다. 다음으로 TABLE 전략을 사용하기 위해 GenerationType.TABLE을 선택했습니다. 그리고 @GeneratedValue.generator에 테이블 키 생성기를 지정했습니다. 이제부터 id 식별자 값은 BOARD_SEQ_GENERATOR 테이블 키 생성기가 할당합니다.
private static void logic(EntityManager em) { Board board = new Board(); em.persist(board); System.out.println("board.id = " + board.getId()); } // 출력: board.id = 1;
TABLE 전략은 시퀀스 대신에 테이블을 사용한다는 것만 제외하면 SEQUENCE 전략과 내부 동작방식이 동일합니다.
MY_SEQUENCE 테이블을 보면 @TableGenerator.pkColumnValue에서 지정한 "BOARD_SEQ"가 컬럼명으로 추가된 것을 확인할 수 있습니다. 이제 키 생성기를 사용할 때마다 next_val 컬럼 값이 증가합니다. 참고로 MY_SEQUENCES 테이블에 값이 없으면 JPA가 값을 INSERT 하면서 초기화하므로 값을 미리 넣어둘 필요는 없습니다.
@TableGenerator
속성 기능 기본값 name 식별자 생성기 이름 필수 table 키생성 테이블명 hibernate_sequences pkColumnName 시퀀스 컬럼명 sequence_name valueColumnName 시퀀스 값 컬럼명 next_val pkColumnValue 키로 사용할 값 이름 엔티티 이름 initialValue 초기 값, 마지막으로 생선된 값이 기준 0 allocationSize 시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용됨) 50 catalog, schema 데이터베이스 catalog, schema 이름 uniqueConstraints(DDL) 유니크 제약 조건을 지정할 수 있음 ※TABLE 전략과 최적화
TABLE 전략은 값을 조회하면서 SELECT 쿼리를 사용하고 다음 값으로 증가시키기 위해 UPDATE 쿼리를 사용합니다. 이 전략은 SEQUENCE 전략과 비교해서 데이터베이스와 한 번 더 통신하는 단점이 있습니다. TABLE 전략을 최적화 하려면 @TableGenerator.allocationSize를 사용하면 됩니다. 이 값을 사용해서 최적화 하는 방법은 SEQUENCE 전략과 같습니다.
5. AUTO 전략
GenerationType.AUTO는 선택한 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택합니다. 예를 들어 오라클을 선택하면 SEQUENCE를, MySQL을 선택하면 IDENTITY를 사용합니다.
@Entity public class Board { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; ... }
@GeneratedValue.strategy의 기본값은 AUTO 입니다. 따라서 다음과 같이 사용해도 결과는 같습니다.
@Id @GeneratedValue private Long id;
AUTO 전략의 장점은 데이터베이스를 변경해도 코드를 수정할 필요가 없다는 것입니다. 특히 키 생성 전략이 아직 확정되지 않은 개발 초기단계시 편리하게 사용할 수 있습니다.
AUTO를 사용할 때 SEQUENCE나 TABLE 전략이 선택되면 시퀀스나 키 생성용 테이블을 미리 만들어 두어야 합니다. 만약 스키마 자동 생성 기능을 사용한다면 하이버네이트가 기본값을 사용해서 적절한 시퀀스나 키 생성용 테이블을 만들어 줍니다.
참고 : 자바 ORM 표준 JPA 프로그래밍(김영한 저)
반응형'개발 > JPA' 카테고리의 다른 글
@Table (0) 2022.07.19 @Entity (0) 2022.07.19 영속성과 영속성컨택스트 (0) 2022.07.13 Entity 생명주기 (0) 2022.07.05