개발을 하다보면 DB연결을 통해 자바 객체를 데이터베이스에 저장하곤 한다.
이 과정에서 PK를 개발자가 직접 지정하지 않는다.
보통 아래와 같은 방식으로 pk외의 다른 컬럼에 대한 값을 지정하고 save를 하는 시점에서 자동으로 PK가 결정되게 한다.
@Builder
public Property(String zipCode, String roadNameAddress, String landLotNameAddress) {
this.zipCode = zipCode;
this.roadNameAddress = roadNameAddress;
this.landLotNameAddress = landLotNameAddress;
}
기본 키는 기본 키 제약조건에 의해 아래와 같은 조건을 만족해야한다.
기본 키 제약조건
- null이 들어가면 안된다.
- 유일한 (unique) 한 값이어야함
- 변하는 값이면 안된다. (불변)
이러한 이유 때문에 아래와 같이 Entity 클래스 자체에 builder를 쓰는 것 또는 Entity에 setter를 사용하는 것을 지양한다.- Entity에 setter를 쓰는 경우 → id를 변화시킬 수 있음
- Entity 자체에 builder를 쓰는 경우 → id 값을 개발자가 설정할 수 있음.
따라서, 주민등록번호 또는 이메일과 같은 비즈니스 로직과 관련된 값을 PK로 사용하면 안된다.
어떠한 이유에서든 나중에 변할 수 있으니, 이러한 값들과 별개로 별도의 대체키를 사용해야한다.
이러한 대체키를 생성하는 전략은 3가지가 있는데 이에 관한 글을 정리해볼까 한다.
데이터베이스의 PK 생성 전략
- IDENTITY전략
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "property_id") private Long id;
이 전략을 사용하면 서버에선 생성한 객체의 PK 값을 null로 비워두고 전송한다.create table property ( property_id bigint auto_increment -- 별도의 생성 전략이 명시됨 primary key, property_land_lot_name_address varchar(255) null, property_road_name_address varchar(255) null, property_zipcode varchar(255) null );
JPA와 함께 사용하는 경우따라서 기본적으론 영속성 컨텍스트 (1차캐시)에 값을 보존하다 트랜잭션이 종료되는 시점에 필요한 쿼리를 DB에 전달한다.즉, 트랜잭션이 커밋되는 시점이 아닌, 영속성 컨텍스트에 들어가는 순간에 insert 쿼리가 실행된다.- 하지만, IDENTITY 전략을 사용하는 경우엔 트랜잭션이 종료되지 않더라도 save() ( em.persist() )메서드가 실행되는 시점에 DB에 insert 쿼리를 전달한다.
- JPA는 Hibernate의 Transactional Write Behind(트랜잭션이 커밋될때까지 쿼리를 모아뒀다가 한번에 실행) 전략을 지원한다.
- 데이터베이스가 해당 컬럼을 생성할 때 값을 하나씩 증가시키며 PK를 배정한다.
- 아래와 같은 자바 코드로 PK를 생성하면 Table 생성 시 PK 전략을 auto_increment 로 설정한다.
- Sequence 전략 : 적당히 순서를 보존
서버는 해당 값 내에서 생성할 객체에 PK를 배정하고 배정된 데이터를 DB에 insert 한다.
DB에서 별도의 Sequence 값을 보존하고 있다가 allocationSize 만큼 값을 서버 메모리로 받아온다.
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE ,generator = "PROPERTY_SEQ_GENERATOR" ) @Column(name = "property_id") private Long id;
과정- call next value for ~~ SEQ 를 통해 allocationSize만큼 서버로 할당 가능한 PK의 값을 전달한다.
- sequence가 8이고 allocationSize가 50이면 8 ~ 57까지 PK를 할당한다.
- 할당한 값을 배정하다가 57을 넘게 되면 다시 call next value ~ 를 통해 메모리로 값을 불러온다.
- (반복)
(추가로 MySQL에선 이 전략을 지원하지 않는다.) - @SequenceGenerator( name = "PROPERTY_SEQ_GENERATOR", sequenceName = "PROPERTY_SEQ", initialValue = 1, allocationSize = 50 // 한번에 가져오는 할당할 PK를 )
- Table 전략
- 키 생성 전용 테이블을 만들어서 Sequence 전략과 유사하게 사용한다.
- 운영 환경에선 Table을 따로 관리해야하기 때문에 잘 사용하지 않는다고 한다.
@TableGenerator( name = "PROPERTY_SEQ_GENERATOR", table = "SEQUENCE_TABLE", pkColumnName = "sequence_name" , valueColumnName = "PROPERTY_SEQ", initialValue = 1, allocationSize = 200000 )
- AUTO
MySQL에서 어떤 전략을 사용하면 거기에 맞춰서, 다른 DB에서 다른 전략을 사용하면 그 전략에 맞춰서 사용하는 방식이다.전략은 위에서 언급한 IDENTITY, SEQUENCE, TABLE 셋 중 하나이다.- @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "property_id") private Long id;
- 특정한 전략을 직접 설정하는 것이 아닌, 실제 사용하는 데이터베이스의 방언에 맞춰서 기본 전략을 채택하는 방법이다.
'BE > 개발일지' 카테고리의 다른 글
다량의 데이터 삽입 시간 줄이기 (0) | 2024.05.15 |
---|---|
OAuth를 통한 인증의 문제와 OIDC (0) | 2023.10.13 |