jpa, PK가 auto increment인데, PK 값을 강제로 지정하고 싶을 때
목차
일반적인 JPA의 id 컬럼 설정 방법
JPA에서 mysql과 같이 auto increment PK를 사용하는 경우 아래처럼 작성합니다.1
2
3
4
5
6
7
8
9
"products") (name =
public class Product {
(strategy = GenerationType.IDENTITY)
private Long id;
// .. 기타등등 필드
}
이러면 entity 생성시 위 id에 뭘 안넣어도 JPA가 auto increment 다음 값을 가져와서 entity에 값을 잘 설정해줍니다.
만약 이런 경우라면..?
우리가 어떤 두 시스템을 통합한다고 가정합시다.
라이브 중인 서비스는 이미 이 통합작업을 고려하여 auto increment 값이 10000000번 부터 시작된 프로젝트입니다.
마이그레이션 할 데이터들의 PK는 1번부터 시작하며 100만건 남짓 입니다.
따라서 라이브중인 시스템과 마이그레이션 할 대상의 시스템의 PK는 겹치지 않습니다.
이 두 시스템의 데이터를 합쳐야 합니다.
요약하면, 마이그레이션 데이터의 PK를 유지한채 현 라이브중인 시스템으로 가져와야 된다는 의미입니다.
아래처럼 하면 쉽게 될것 같지만.. 과연 그럴까요?1
2
3
4
5
6
public void migrate(Long originalPK) {
Product p = new Product();
p.setId(originalPK); // 가져올 데이터베이스의 PK값을 강제로 id필드에 할당
productRepository.save(p);
}
결론은, 안됩니다. auto increment 컬럼과 매핑된 @GeneratedValue(strategy = GenerationType.IDENTITY)
이 속성의 @Id 필드에는요.
자동으로 auto increment 다음 값을 가져와서 넣어버립니다.
해결 방법
많은 삽질을 해본 결과 아래와 같은 답을 찾았습니다. IdGenerator를 커스터마이징 하는 방법입니다.1
2
3
4
5
6
7
8
9
10
11
"products") (name =
public class Product {
"productId", strategy = "com.mingpd.jpa.ProductIdGenerator") (name =
"productId") // 위 name과 같은 이름을 써줍시다. (generator =
// @GeneratedValue(strategy = GenerationType.IDENTITY) // identity 전략은 사용하지 않습니다.
private Long id;
// .. 기타등등 필드
}
이제 ProductIdGenerator를 아래처럼 만들어봅시다.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25import java.io.Serializable;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentityGenerator;
import com.minpd.domain.Product;
public class ProductIdGenerator extends IdentityGenerator {
public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
if (object instanceof Product) {
Product p = (Product) object;
return p.getId() == null ? super.generate(session, object) : p.getId();
} else {
throw new RuntimeException("Product entity가 아니에요.");
}
/*
// 이것도 됨
Serializable id = session.getEntityPersister(null, object).getClassMetadata().getIdentifier(object, session);
return id != null ? id : super.generate(session, object);
*/
}
}
IdentityGenerator 클래스를 상속받아 만들었는데요. 이놈이 뭐하는애냐면, PK 생성이 IDENTITY 전략일 때 불리는 애 입니다.
쉽게 말해, 현재 Product 엔티티의 id가 null일때만 auto increment를 이용하겠다는 의미입니다.
마치며
사실 이 방법이 올바른건진 잘 모르겠으나, 일단 되긴 됩니다. 그럼 됐죠 뭐..