실무 프로젝트를 하면서 기존에 Node 계열에서는 거의 안보이던 기본 키 생성 전략이라는 개념이 보이게 되면서 혼란스러워서 따로 정리를 해서 개념을 익혀둘 필요성을 느꼈다.
현재 실무 프로젝트에서는 기본키 자동 생성 전략으로 IDENTITY
를 사용하고 있다.
별도로 Spring을 공부하지 않고 처음으로 백엔드로 Spring을 사용하면서 굉장히 낯설었지만, 일이 주어진 이상 할 수 밖에 없기에 차근차근 하나씩 진행을 해보았다.
하지만 IDENTITY
를 사용하지 않으면 Entity 같의 관계를 맺는 경우에 에러가 발생하는 것을 보면서 이상함을 느꼈다.
IDENTITY
전략을 사용하면 기본적으로 1번 Table에서 id가 1인 row를 생성하게 되면 다음 2번 Table에서는 id가 2인 row가 생성이 된다.
이게 시간이 지나면 지날수록 row가 많아질탠데 이러한 상태가 되는 점이 조금 이상하긴 하지만 제일 중요한 관계가 맺어지지 않기에 어쩔 수 없지 진행을 했다.
https://devjem.tistory.com/40 이 블로그로 부터 원인을 알아냈다.
hibernate 설정 값으로use-new-id-generateor-mappings: false
을 넣어서 사용하면 해당 문제가 해결 된다고 한다.
기본 키 속성 유형에 따라 값(IDENTITY, TABLE, SEQUNCE 중에서)을 결정한다.
@Id
// @GeneratedValue(strategy = GenerationType.AUTO) or
@GeneratedValue
private Long id;
@Id
// @GeneratedValue(strategy = GenerationType.AUTO) or
@GeneratedValue
private UUID id;
기본 키 생성을 데이터베이스에 위임하는 전략이다.
흔히 DDL로 기본 키를 AUTO INCREAMENT
명령어로 해당 컬럼을 정의하는 방식이다.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
해당 전략은 Entity를 생성 후 save()
메소드를 통해 INSERT
후에 식별자를 조회하여 반환해서 해당 결과를 받아 객체에 담아낸다.
즉, INSERT
쿼리를 날린 뒤에 id가 생성 된다고 이해하면 된다.
INSERT
쿼리를 날린 후에 추가적인 작업이 필요하다면 save()를 하고 난 뒤에 반환 된 값을 가지고 이후에 로직으로 동작을 해야 한다.어떠한 값이 나올지는 DB가 키를 생성하다보니 INSERT
된 이후에 id을 알 수 있게 되어서 다른 Entity와 관계를 동시에 맺은 채로 배치 INSERT
가 불가능하다.
DB Sequence Object를 사용한다.
DB Sequence는 유일한 값을 순서대로 생성하는 특별한 DB Object.
테이블마다 Sequence Object를 따로 관리하고 싶으면 @SequenceGenerator
에sequenceName
속성을 추가한다.
예를 들면, A Table
, B Table
, C Table
이렇게 세 개의 테이블이 존재할 때 A Table
, C Table
은 절대 id 값이 중복 되면 안되고 B Table
은 중복이 되어도 상관이 없는 경우라고 가정하면
A Table
과 C Table
은 동일한 sequenceName
으로 설정하고 B Table
은 별도의 sequenceName
을 설정해 사용하면 된다.
@SequenceGenerator(
name = "TEST_ENTITY_SEQ_GENERATOR", // 필수값
sequenceName = "TEST_ENTITY_SEQ", // 기본값 : hibernate_sequence
initialValue = 1, // 기본값 : 1
allocationSize = 1 // 기본값 : 50)
public class TestEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "TEST_ENTITY_SEQ_GENERATOR")
private Long id;
}
IDENTITY
전략과 동일하게 DB에 INSERT
되고 나서 id 값을 알 수 있다.
하지만 IDENTITY
전략과 다른 이유는 sequenceName
으로 부터 현재 id 값을 받아와서 다음 값을 미리 알 수 있다는 점이다.(이게 어떻게 동작하는지 직접 코드를 작성하면서 알아봐야 할 것 같다.)
SEQUENCE
전략은 Sequence
를 매번 DB에서 네트워크를 통해 가져오기 때문에 성능 저하가 일어 날 수 있다.
따라서 @SequenceGenerator
어노테이션에서는 추가적으로 allocationSize
라는 속성 값이 있다.
이는 기본값이 50이다.
allocationSize
는 이론적으로는 더 큰 수로 설정할수록 성능이 좋아진다고 하지만, 서버를 중단하는 시점에 사용하지 않은 sequence 값이 날라가기 때문에 이때 DB 전달 되지 않은 값들은 날라가는 현상이 생기게 된다.
따라서 50~100 사이로 설정하는 것을 권장한다고 한다.
DB에 해당 서비스 로직이 네트워크 비용을 얼마나 많이 소모하고 있는지 모니터링을 하고 이를 개선해야할 타이밍에 allocationSize
를 조절하는 것이 좋을 것 같다.
빠른 네트워크 회선을 사용하는 경우 비용이 증가가 되는 DB 서비스들이 있다보니 이러한 경우라면 서비스 유지 비용 절감에 매우 좋을 것 같다.
키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략
@SequenceGenerator(
name = "TEST_ENTITY_SEQ_GENERATOR",
table = "SEQUENCE_MANAGER",
pkColumnValue = "",
allocationSize = 1 // 기본값 : 50)
public class TestEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "TEST_ENTITY_SEQ_GENERATOR")
private Long id;
}
모든 데이터베이스에서 해당 기능을 지원한다.
Table 전략에서 allocationSize
를 1을 초과하는 값을 설정하게 되면 웹 서버를 여러개 띄울 경우에는 sequence가 맞지 않는 문제가 발생할 수 있지 않을까라고 할 수 있지만, 이는 서버 별로 순차적으로 사용하여 값이 올라가 있을거라고 한다.(직접 사용해보지 않았기 때문에 어떻게 동작할지 예상이 되지 않는다.)