본 포스팅은 인프러의 JPA 기본편을 수강하고 정리하는 내용입니다.
기본 키 매핑 어노테이션
@Id
@GeneratedValue
1
2
3
4
5
6
@Entity
class Product {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
...
}
기본 키 매핑 방법
- 직접 할당:
@Id
만 사용 - 자동 생성(
@GeneratedValue
)IDENTITY
: 데이터베이스에 위임, MYSQLSEQUENCE
: 데이터베이스 시퀀스 오브젝트 사용, ORACLE- @SequenceGenerator 필요
TABLE
: 키 생성용 테이블 사용, 모든 DB에서 사용- @TableGenerator 필요
AUTO
: 방언에 따라 자동 지정, 기본값
IDENTITY 전략 - 특징
- 기본 키 생성을 데이터베이스에 위임
- 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용
- (예: MySQL의 AUTO_ INCREMENT)
- JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL 실행
- AUTO_ INCREMENT는 데이터베이스에 INSERT SQL을 실행한 이후에 ID 값을 알 수 있음
- DB에 엔티티가 저장된 후에야 id를 알 수 있다보니 1차 캐시에 id값은 null이 되게됨
- IDENTITY 전략은 em.persist() 시점에 즉시 INSERT SQL 실행하고 DB에서 식별자를 조회
- jdbc드라이버에 insert 쿼리를 실행 후 내부적으로 id값을 리턴받는게 별도로 있다. JPA가 이를 사용하여 실행한다.
- 그래서 모아서 insert 하는게 IDENTITY 전략에선 불가능하다. (단점)
- 하지만 버퍼링해서 write하는게 크게 메리트가 있진 않다.
1
2
3
4
5
6
7
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
}
SEQUENCE 전략 - 특징
- 데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트(예: 오라클 시퀀스)
- 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
@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;
...
}
JPA가 persist()를 호출할때 시퀀스 전략일 경우 db에서 먼저 sequence를 불러와서 엔티티에 세팅한다. 실제 커밋하는 시점에 버퍼에 쌓인 쿼리를 한 번에 실행한다.
Note: 매번 네트워크를 왔다갔다해서 성능상 좋지 않나라는 고민을 할 수 있다.
allocationSize
를 속성을 통해 최적화를 할 수 있다. 예를 들어, allocationSize를 50으로 지정할 경우 한 번 호출할때 DB에는 시퀀스 값을 50 증가시킨다. 그리고 메모리에 그 50만큼 가지고 있으면서 쓰는 방식이다. 이를 통해 네트워크를 통해 시퀀스를 매번 받아오는 이슈를 줄일 수 있다.
1
2
3
em.persist(member1); // DB SEQ = 1 | 1 , DB SEQ = 51 | 1
em.persist(member2); // DB SEQ = 51 | 2
em.persist(member3); // DB SEQ = 51 | 3
@SequenceGenerator - 속성
주의!: allocationSize 기본값 = 50
TABLE 전략
- 키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략
- 장점: 모든 데이터베이스에 적용 가능
- 단점: 성능(테이블을 직접 사용하다보니 락이 걸릴 수도 있기에..)
1
2
3
4
5
create table MY_SEQUENCES (
sequence_name varchar(255) not null,
next_val bigint,
primary key ( sequence_name )
)
1
2
3
4
5
6
7
8
9
10
11
12
@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;
...
}
@TableGenerator - 속성
allocationSize
의 경우 시퀀스 옵션과 동일하다고 보면 된다. 한 번에 50개를 DB테이블에 올려놓고 메모리에서 50개를 쓰는 방법이다. 웹 서버 열대가 동시에 호출하더라도 자기가 숫자를 확보하고 동작하기 때문에 문제는 없다. (동시성 문제X)
Note: 운영에선 Table 전략을 사용하기 부담스러움. 관례를 따르는 경우가 많음
권장하는 식별자 전략
기본 키 제약 조건
: null 아님, 유일,변하면 안된다
.- 미래까지 이 조건을 만족하는 자연키는 찾기 어렵다. 대리키(대체키)를 사용하자.
- GenerateValue나 랜덤 값이나 비즈니스 value와 전혀 상관없는 값을 쓰는게 좋다.
- 예를 들어 주민등록번호도 기본 키로 적절하기 않다.
- 권장: Long형 + 대체키 + 키 생성전략 사용
- 먼 미래를 보고 십몇억이 넘어도동작해야하기에 int형보다 Long형 타입을 사용하자.