도입
지난 시간에는 필드와 칼럼을 매핑하여 보았다. 이번에는 매핑된 칼럼중에서 Primary key를 지정하는 방법을 알아보도록 하겠다.
Primary Key란 기본키를 의미한다. 기본키의 성질은 테이블 내에서 유일하고 null값이 없어야하며 변경되면 안된다. 따라서 테이블에서 데이터들을 유일하게 구분하는데 사용이된다. 이번 시간에는 이러한 기본 키를 매핑하는 방법을 알아보도록 하자.
2022.01.10 - [Tech-Stack & Language/JPA] - JPA로 필드와 칼럼 매핑하는 annotation 알아보기
기본키 매핑 방법
기본키를 매핑하는 것도 동일하게 어노테이션을 사용해서 매핑할 수 있다. @Id 와 @GeneratedValue라는 두개의 어노테이션을 사용해주면 된다. 기본키를 할당하는 방법은 DB가 자동으로 해줄 수도 있고 데이터를 가지고 개발자가 직접 할당해 줄 수도 있다. 직접할당해줄 때는 사람들의 주민등록번호처럼 변경되지 않고 유일한 정보를 사용해주어야한다. 테이블이 조인될 때 기본키를 외래키로 조인을 해주기 때문에 기본키는 변경될 경우 굉장히 리스크가 크기 때문에, 절대 변경되지 않는 값으로 할당해주어야 한다.
직접 할당
Primary Key를 직접 할당해주는 경우는 @Id만 Primary Key에 붙여서 사용해주면 된다. 이 방법을 적용하기 위해서는 해당 칼럼이 절대로 변경되서는 아니하고, null값이 들어오면 안되며, 유일해야한다. 대표적으로 주민등록번호가 있다. 그런데, 직접 할당을 하는 것은 그렇게 좋은 방법이 아니다. 왜냐하면 기본키의 조건 중 변경이 되면 안되는 조건을 지키기가 어렵기 때문이다. 만약 주민등록번호와 같이 절대 변경이 되지 않을 것이라고 판단되는 정보로 기본키를 사용하여도, 추후 개인정보를 관리하는 차원의 법안이 변경되어 주민번호를 소지할 수 없게 될 경우 그 테이블에 새로운 Primary key를 찾아줘야하는 난해한 상황이 올 수 도 있다. 따라서 직접 특정 데이터의 값이 아니라 순수히 Id 값만 가지는 key를 사용하는것을 권장한다.
자동으로 부여
일부 의미있는 칼럼을 ID로 직접할당하는 방법이 추후 유지보수에 굉장히 큰 문제를 일으킬 수 있다는 것을 알았으니, 아무 의미없는 값을 생성하여 key로 부여하여야한다. 그런데 아무 의미 없는 값을 부여하는데 굳이 개발자가 매번 Id를 DB에서 보면서 사용하지 않은 key값을 찾아서 넣어주는 번거로운 과정을 할 필요는 없다. 따라서 @GeneratedValue를 사용하여 자동으로 ID를 부여하게 만들어주면 된다.
@GeneratedVallue는 Id값을 만들어주는 4가지 생성전략이 있다.
- IDENTITY
- SEQUENCE
- TABLE
- AUTO
4가지 방법의 특징을 알아보도록 하겠다.
IDENTITY
@Entity
public class Member{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
IDENTITY는 INSERT SQL때 DB가 직접 ID값을 계산하여 넣어주는 방법이다. 이전에 알아보았던 영속성 컨텍스트를 떠올려보자. 영속성 컨텍스트에서는 Entity Manager가 persist를 수행해주면 생성된 객체를 영속성 컨텍스트에서 사용해주고 트랜잭션이 종료되는 시점에 버퍼링 처리를 하여 INSERT SQL을 날려준다고 하였다. IDENTITY전략은 INSERT SQL이 날라갈 때 ID를 알 수 있다고 하였는데, 영속성 컨텍스트로 관리를 할 때는 ID가 필요하다. 굉장히 모순적인 상황이 발생하였고 하이버네이트는 이를 해결하기 위하여 IDENTITY전략일 경우에는 persist가 되면 바로 INSERT SQL을 날려주게 동작한다. 즉 트랜잭션이 커밋되는 상황에서 버퍼링으로 모아서 INSERT SQL을 모날리는게 제한이 된다.
SEQUENCE
@Entity
@SequenceGenerator(
name = "MEMBER_SEQ_GENERATOR",
sequenceName = "MEMBER_SEQ",
initialValue = 1,
allocationSize = 50
)
public class Member{
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "MEMBER_SEQ_GENERATOR")
private Long id;
}
시퀀스는 DB의 Object로 유일한 값을 순서대로 생성하는 역할을 한다. DB의 시퀀스에는 next가 존재하며, 시퀀스의 다음 값을 의미한다.
SEQUENCE전략은 DB의 Object인 시퀀스를 사용해서 ID를 넣어주는 방법이다. 위에서 보는 것 처럼 @SequenceGenerator를 사용하여 시퀀스를 생성하고 그 것을 사용하게 된다.
Entity Manager가 persist를 해주면 잠시 DB의 Sequence Object에게 가서 next값을 받아서 Id를 받아서 사용해주면 되므로 INSERT SQL을 모아서 날릴 수 있다. 하지만, 성능적인 문제가 생긴다. 바로 persist를 할 때마다 항상 DB의 Sequence Object에게 가서 next값을 받아와야하는 것이다. 매번 DB에 요청을하고 가져오는 것은 네트워크상 굉장히 많은 요구를 하게 될 수 있다. 따라서, allocationSize를 정하여 한번 DB에서 요청해올 때 그 사이즈 만큼을 가져와 사용하는 방법을 활용한다. 즉 1번 key값을 저장하려고할 때 1번 key 값 뿐만 아니라 50번 key까지 받아서, 트랜잭션 상에서 공유하며 또 persist가 발생했을 때 DB로 이동하지 않고 과거에 받아온 key의 다음 값을 사용해준다. 이렇게 되면 서버가 두대 이상일 경우 동시성 문제가 발생할 수 있지 않을까 생각이 들게 된다. 이는 1번 서버가 50 까지의 키를 가지고 있다면, 2번 서버가 요청했을 때는 51번부터 주는 식의 방법을 사용하여 해결한다고 한다. 따라서 사용할 때는 동시성 문제를 고려하지 않고 사용해도 된다.
TABLE
Table 전략은 키를 생성해주는 테이블을 하나 생성하여 사용하는 전략이다. DB의 시퀀스를 흉내내듯이 테이블이 키값을 생성하게 된다.
create table MY_SEQUENCES(
sequence_name varchar(255) not null,
next_val bigint,
primary key ( sequence_name)
)
이렇게 테이블을 하나 생성하여 이 테이블을 이용해 키 값으로 역할을 시켜준다.
@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;
}
이렇게 사용하게 될 경우 다른 테이블에서도 이 테이블을 key로 사용할 수 있다는 장점이 있지만(?) 성능적으로 떨어지기 때문에 잘 사용하지 않는 방법이다.
AUTO
Auto 전략은 위 3가지 전략중 DB의 방언에 맞추어 적절하게 자동으로 적용해주는 방법이다.
마무리
DB의 key 값을 매핑하는 방법과 자동으로 할당하는 전략들을 알아보았다. 키값을 사용할 때는 데이터의 특정 값이 아닌 대체키(아무 의미가 없는 값)을 사용해주어야 하고, 자동으로 키를 생성해주는 전략을 사용해주는 것이 좋다.
이 글은 김영한님의 강의 "자바 ORM 표준 JPA 프로그래밍"를 공부한 후 작성한 글 입니다.
'Back-end > JPA' 카테고리의 다른 글
JPA 상속관계 매핑하기(+ @MappedSuperclass) (0) | 2022.01.16 |
---|---|
JPA 연관관계 매핑하기 (0) | 2022.01.15 |
JPA로 필드와 칼럼 매핑하는 annotation 알아보기 (0) | 2022.01.10 |
JPA로 객체와 테이블 매핑하기 (0) | 2022.01.07 |
JPA 영속성 컨텍스트란? (0) | 2022.01.03 |