Spring/JPA

JPA 기본키 매핑

묠니르묘묘 2022. 2. 27. 22:00

 

기본키(primary key) 매핑하는 방법은 2가지로 직접 할당자동 생성이 있다.

직접 할당은 엔티티에 @Id 어노테이션만 사용해서 직접 할당한 것이다.

자동 생성은 엔티티에 @Id와 @GeneratiedValue를 추가하고 원하는 키 생성 전략을 선택하면 된다.

직접 할당과 자동 생성 예시

위 사진처럼 빨간색 부분만 작성하면 직접 할당, 노란색 부분 전체를 작성하면 자동 생성인 것이다.

자동 생성같은 경우에는 MySQL의 AUTO_INCREMENT 같은 기능으로 생성된 값을 기본키로 사용하는 것이다.

(e.g. 1씩 증가하는 숫자값)

 

직접 할당

기본키를 애플리케이션에서 직접 할당한다.

 

자동 생성

대리키 사용 방식이다.

  • IDENTITY : 기본키 생성을 DB에 위임한다.
  • SEQUENCE : DB 시퀀스를 사용해서 기본키를 할당한다.
  • TABLE : 키 생성 테이블을 사용한다.
  • AUTO : 선택한 DB에 따라 위 전략 IDENTITY, SEQUENCE, TABLE 중 하나를 자동으로 선택한다. (기본값)

자동 생성 전략이 다양한 이유는 데이터베이스 각각 차이점이 있기 때문이다.

이 직접 할당과 자동 생성의 예시를 설명하겠다.

 

 

기본키 직접 할당 전략

Member member = new Member();
member.setId("1"); // 기본키 직접 할당
EntityManager.persist(member);

기본키 직접 할당 전략은 @Id 어노테이션만 엔티티에 적용한 것이다.

그리고 위 코드처럼 애플리케이션에서 기본키를 직접 할당하여 엔티티 매니저로 persist()를 한다.

 

 

 

기본키 자동 생성 전략 - IDENTITY

기본키 생성을 DB에 위임하는 전략이다.

MySQL, PostgreSQL, SQL Server, DB2에서 사용한다.

(MySQL의 AUTO_INCREMENT)

JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL 실행하는데 이 전략은 DB에 먼저 INSERT SQL을 실행해야 기본키를 알 수 있어서 em.persist() 시점에 즉시 INSERT SQL 실행하고 DB에서 식별자를 조회하게 된다.

@Entity
public class Member {
	@Id
 	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;

위 코드를 쓰면 다음 SQL문으로 DB를 생성하게 된다.

DB 생성 SQL문

이 전략을 쓰면 Id값은 비워두고 저장하면 알아서 값을 채워준다.

 

 

 

기본키 자동 생성 전략 - SEQUENCE

유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트이다.

이 시퀀스를 사용해서 기본키를 생성하게 된다.

시퀀스를 지원하는 Oracle, PostgreSQL, DB2, H2 Database에서 사용할 수 있다.

SEQUENCE 전략을 사용한 엔티티 예시

@SequenceGenerator를 사용해서 "MEMBER_SEQ_GENERATOR"라는 시퀀스 생성기를 등록했다.

그리고 이 시퀀스 이름을 "MEMBER_SEQ"로 지정했는데 JPA는 이 시퀀스 생성기를 실제 DB의 "MEMBER_SEQ" 시퀀스와 매핑한다.

 

@GeneratedValue를 사용해서 SEQUENCE 자동 생성 전략으로 타입을 정하고, generator로 방금 등록한 시퀀스 생성기를 선택했다.

이렇게 하면 id 식별자 값은 저 시퀀스 생성기가 할당하게 되는 것이다.

 

IDENTITY 전략과 비슷하지만 내부 동작 방식은 다르다.

시퀀스 전략은 em.persist()를 호출할 때 먼저 DB 시퀀스를 사용해서 식별자를 조회한다.

그리고 조회한 식별자를 엔티티에 할당한 후에 엔티티를 영속성 컨텍스트에 저장한다.

이후 트랜잭션을 커밋해서 플러시가 일어나면 엔티티를 DB에 저장하게 된다.

IDENTITY 전략은 먼저 엔티티를 DB에 저장하고 식별자를 조회해서 엔티티의 식별자에 할당하는 것이었다.

 

@SequenceGenerator 속성

SequenceGenerator 속성

매핑할 DDL은 다음과 같다.

create sequence [sequenceName]
start with [initialValue] increment by [allocationSize]

여기서 allocationSize 기본값은 50이다.

JPA가 기본으로 생성하는 DB시퀀스는 create sequence 시퀀스이름 start with 1 increment by 50 이므로 시퀀스를 호출할 때마다 값이 50씩 증가한다.

기본값이 50인 이유는 최적화 때문이다. DB 시퀀스 값이 하나씩 증가하도록 설정되어 있으면 이 값을 반드시 1로 설정해야 한다.

 

 

다음과 같이 코드를 실행해보자.

그러면 다음과 같이 실행되게 된다. (엔티티가 있다는 가정하에)

동작 결과

1. DB 시퀀스 생성

2. DB 생성

3. persist() 시점에 DB 시퀀스에서 식별자 조회

4. 트랜잭션 커밋 시점에 플러시로 INSERT SQL을 DB에 보내서 저장

 

이렇게 실행 동작을 예시로 살펴봤듯이 시퀀스 전략은 시퀀스를 통해서 식별자를 조회하는 추가 작업이 필요하다.

따라서 DB와 2번 통신하게 된다.

JPA는 시퀀스에 접근하는 횟수를 줄이기 위해 @SequenceGenerator의 allocationSize를 사용하는 것이다.

이 값이 50이면 시퀀스를 한 번에 50 증가 시킨 다음에 1~50까지는 메모리에서 식별자를 할당한다.

그리고 51이 되면 시퀀스 값을 100으로 증가시킨 다음 51~100까지 메모리에서 식별자를 할당한다.

DB에 직접 접근해서 데이터를 등록할 때 시퀀스가 많이 증가한다는 점과 INSERT 성능이 중요하지 않으면 1로 설정해도 된다.

 

 

 

 

기본키 자동 생성 전략 - TABLE

키 생성 전용 테이블을 하나 만들어서 DB 시퀀스를 흉내내는 전략이다.

모든 데이터베이스에 적용이 가능하지만 성능이 나쁘다.

 

create table MY_SEQUENCES (
	sequence_name varchar(255) not null,
	next_val bigint,
	primary key ( sequence_name )
)

TABLE을 사용하려면 위 처럼 키생성 용도로 사용할 테이블을 만들어야 한다.

사용 예시를 살펴보자.

@TableGenerator로 테이블 키 생성기를 등록한다.

그리고 생성한 MY_SEQUENCES 테이블을 키 생성용 테이블로 매핑했다.

그리고 @GeneratedValue의 generator 속성으로 방금 만든 테이블 키 생성기를 지정했다.

이러면 id 식별자 값은 MEMBER_SEQ_GENERATOR 테이블 키 생성기가 할당한다.

 

위 DDL을 보면 Member 테이블이 만들어지고, 키 생성 전용 테이블인 MY_SEQUENCES 만들어지고,

키 생성 전용 테이블로 매핑했다.

여기서 Member 객체를 만들어서 저장하게되면 키 생성 전용 테이블을 먼저 조회하고 트랜잭션 커밋 시점에 INSERT SQL이 보내진다.

 

TABLE 전략은 값을 조회하면서 SELECT 사용 후 다음 값을 증가시키기 위해 UPDATE를 한번 더 사용하는 것이 SEQUENCE 전략보다 한 번 더 DB와 통신하는 차이점이 있다.

 

@TableGenerator 속성

@TableGenerator 속성

 

 

기본키 자동 생성 전략 - AUTO

데이터베이스 방언에 따라 위 셋의 전략중 하나를 자동으로 선택한다.

(e.g. Oracle이면 SEQUENC, MySQL이면 IDENTITY를 사용한다.)

@GeneratedValue의 기본값은 AUTO이기에 아무런 속성없이 사용해도 된다.

@Entity
@public class Member {
	@Id
	@GeneratedValue
	private Long id;
}

 

 

 

 


자바 ORM 표준 JPA 프로그래밍 / 김영한 지음 / 에이콘출판주식회사 출판

자바 ORM 표준 JPA 프로그래밍 - 기본편 / 김영한 / 인프런 강의