2023. 02.07 12:00 복습 시작
기본 키 매핑
기본키를 애플리케이션에서 직접 할당하는 대신에 데이터베이스가 생성해주는 값을 사용하려면 어떻게 매핑해야 할까?
오라클의 시퀀스 오브젝트라던가 아니면 MySQL의 AUTO_INCREMENT 같은 기능을 사용해서 생성된 값을 기본 키로 사용하려면 어떻게 해야 할까?
데이터베이스마다 기본 키를 생성하는 방식이 서로 다르므로 이 문제를 해결하기는 쉽지 않다.
JPA는 이런 문제를 어떻게 해결하는지 알아보자.
- 직접 할당 : @Id만 사용
- 자동 생성 (@GeneratedValue)
- IDENTITY : 기본 키 생성을 데이터베이스에 위임. (MySQL)
- SEQUENCE : 데이터베이스 시퀀스 오브젝트 사용. (ORACLE)
- @SequenceGenerator 필요
- TABLE : 키 생성용 테이블 사용, 모든 DB에서 사용
- @TableGenerator 필요
- AUTO : 방언에 따라 자동 지정, 기본값
IDENTITY 전략 특징
- 기본 키 생성을 데이터베이스에 위임한다.
- 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용 (MySQL의 AUTO_INCREMENT)
- JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL 실행.
- AUTO_INCREMENT는 데이터베이스에 INSERT SQL을 실행한 이후에 ID 값을 알 수 있다.
- IDENTITY 전략은 em.persist() 시점에 즉시 INSERT SQL 실행하고 DB에서 식별자를 조회
- @GeneratedValue의 strategy 속성 값을 GenerationType.IDENTITY로 지정하면 된다.
영속성 컨텍스트에서 영속 상태이기 위해서는 식별자 값이 필요하다.
식별자 값은 DB PK(ID)와 매핑된다. IDENTITY 전략은 DB에 기본 키(PK) 생성을 위임한다.
우리가 저번 시간에 배운 대로 em.persist를 호출해도 바로 쿼리가 날라가 플러시를 하고 DB에 트랜잭션이 커밋되지 않는다.
이제 생각해보면 IDENTITY 전략인 엔티티가 em.persist를 통해 영속 상태가 되면 기본 키 생성을 DB에 위임해서 식별자 값을 가지고 있지 않는 상황이 발생한다.
그러면 영속성 컨텍스트 내부의 영속 상태로 있으려면 테이블의 기본 키와 매핑되는 식별자가 반드시 필요한데, 이게 가능할까??
그래서 IDENTITY 전략만 em.persist가 호출되면 바로 DB에 INSERT 쿼리를 날린다.
그리고 JPA 가 내부적으로 DB가 생성한 기본키(ID)를 엔티티로 가져온다.
이렇게 해서 영속성 컨텍스트에서 관리를 받을 수 있다.
em.persist()를 호출하면 바로 쿼리가 가므로 이 전략은 쓰기 지연이 동작하지 않는다.
SEQUENCE 전략 특징
데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트 (예 : 오라클 시퀀스)
오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용
시퀀스 매핑 코드를 확인해보자.
@Entity
@SequenceGenerator(
name = "MEMBER_SEQ_GENERATOR",
sequenceName= "MEMBER_SEQ", //매핑할 데이터베이스 시퀀스 이름
initialValue = 1, allocationSize = 1)
public class Member{
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "Member_SEQ_GENERATOR")
private Long id;
...
}
시퀀스 사용 코드는 IDENTITY 전략과 같지만 내부 동작 방식은 다르다.
SEQUENCE 전략은 em.persist()를 호출할 때 먼저 데이터베이스 시퀀스를 사용해서 식별자를 조회한다.
그리고 조회한 식별자를 엔티티에 할당한 후에 엔티티를 영속성 컨텍스트에 저장한다.
이후 트랜잭션을 커밋해서 플러시가 일어나면 엔티티를 데이터베이스에 저장한다.
반대로 이전에 설명했던 IDENTITY 전략은 먼저 엔티티를 데이터베이스에 저장한 후에 식별자를 조회해서 엔티티의 식별자에 할당한다.
@SequenceGenerator
속성 | 기능 | 기본값 |
name | 식별자 생성기 이름 | 필수 |
sequenceName | 데이터베이스에 등록되어 있는 시퀀스 이름 | hibernate_sequence |
initialValue | DDL 생성 시에만 사용됨. 시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정한다. | 1 |
allocationSize | 시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용됨) 데이터베이스 시퀀스 값이 하나씩 증가하도록 설정되어 있으면 이 값을 반드시 1로 설정해야 한다. |
50 |
catalog, schema | 데이터베이스 catalog, schema 이름 |
SEQUENCD 전략과 최적화
SEQUENCE 전략 데이터베이스 시퀀스를 통해 식별자를 조회하는 추가 작업이 필요하다. 따라서 DB와 2번 통신한다.
- 식별자를 구하려고 데이터베이스 시퀀스를 조회한다.
- 조회한 시퀀스를 기본 키 값으로 사용해 데이터베이스에 저장한다.
JPA는 시퀀스에 접근하는 횟수를 줄이기 위해 @SequenceGenerator.allocationSize를 사용한다.
간단히 설명하자면 여기에 설정한 값만큼 한 번에 시퀀스 값을 증가시키고 나서 그만큼 메모리에 시퀀스 값을 할당한다.
예를 들어 allocationSize 값이 50이면 시퀀스를 한 번에 50 증가시킨 다음에 1~50까지는 메모리에서 식별자를 할당한다.
그리고 51이 되면 시퀀스 값을 100으로 증가시킨 다음 51 ~ 100까지 메모리에서 식별자를 할당한다.
이 최적화 방법은 시퀀스 값을 선점하므로 여러 JVM이 동시에 동작해도 기본 키 값이 충돌하지 않는 장점이 있다.
반면에 데이터베이스에 직접 접근해서 데이터를 등록할 때 시퀀스 값이 한 번에 많이 증가한다는 점을 염두해야 한다.
이런 상황이 부담스럽고 INSERT 성능이 중요하지 않으면 allocationSize의 값을 1로 설정하면 된다.
TABLE 전략 특징
@Entity
@TableGenerator(
name = "MEMBER_SEQ_GENERATOR",
table = "MY_SEQUENCES",
pkColumnValue = "MEMBER_SEQ", allocationSize = 1)
public class Member{
@Id
@GeneratedValue(strategy = GenerationType.TABLE,
generator = "MEMBER_SEQ_GENERATOR")
private Long id;
}
TABLE 전략은 키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내 내는 전략이다. 이 전략은 테이블을 사용하므로 모든 데이터베이스에 적용할 수 있다.
@TableGenerator - 속성
속성 | 설명 | 기본값 |
name | 식별자 생성기 이름 | 필수 |
table | 키생성 테이블명 | hibernate_sequences |
pkColumnName | 시퀀스 컬럼명 | sequence_name |
valueColumnNa | 시퀀스 값 컬럼명 | next_val |
pkColumnValue | 키로 사용할 값 이름 | 엔티티 이름 |
initialValue | 초기 값. 마지막으로 생성된 값이 기준이다. | 0 |
allocationSize | 시퀀스 한 번 호출에 증가하는 수 (성능 최적화에 사용된다.) |
50 |
catalog, schema | 데이터베이스 catalog, schema 이름 | |
uniqueConstraints(DDL) | 유니크 제약 조건을 지정할 수 있다. |
권장하는 식별자 전략
- 기본 키 제약 조건 : null 아님, 유일, 변하면 안 된다.
- 미래까지 이 조건을 만족하는 자연 키는 찾기 어렵다. 대리 키(대체키)를 사용하자.
- 대리 키란 비즈니스의 관련 없는 임의로 만들어진 키. 오라클 시퀀스, auto_increment
- 권장 : Long + 대체키 + 키 생성 전략 사용
참고자료
자바 ORM 표준 JPA 프로그래밍 - 김영한
자바 ORM 표준 JPA 프로그래밍 강의 - https://www.inflearn.com/course/ORM-JPA-Basic/dashboard
2023. 02.07 12:50 복습 및 정리 마무리
댓글