JPA로 테이블과 엔티티를 매핑할 때, @Id와 @Column 어노테이션을 활용하여 테이블의 PK를 매핑시킬 수 있다.
public class CustomerEntity {
@Id
@Column(name = "customer_id")
private Long customer_id;
}
하지만 식별자로 사용될 값을 일일히 수동으로 넣어줘야 하는 불편함이 있는데, 이는 @GeneratedValue로 해결할 수 있다.
@GeneratedValue
식별자 값을 자동 생성 시켜줄 수 있다.
@GeneratedValue에는 4가지 옵션이 존재한다.
@GenerationType. AUTO
hibernate.dialect에 설정된 DB 방언 종류에 따라, 하이버네이트가 자동으로 전략을 선택하게끔 위임한다.
데이터베이스 방언이 궁금하다면 -> 데이터베이스 방언 알아보기
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "customer_id")
private Long customer_id;
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
*주의 : 하이버네이트를 무조건 믿어서는 안 된다!
MySQL의 경우, AUTO로 설정하면 당연히 IDENTITY 전략을 취할 것이라고 생각하거나,
추후 DBMS 변경을 고려하여 AUTO로 사용하는 경우가 있는데, 버전에 따라 선택되는 전략이 달라질 수 있다.
결론은 최대한 AUTO를 쓰지 말고 직접 DBMS에 맞는 전략을 지정해 주는 것이 좋다.
Hibernate 5부터 MySQL에서의 Generation.AUTO는 IDENTITY가 아닌 TABLE을 기본 시퀀스 전략으로 가져간다.
@GenerationType. IDENTITY
기본 키 생성을 데이터베이스에 위임한다.
데이터베이스에서 자동으로 값이 증가한다.
주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용한다.
IDENTITY 전략은 영속성 컨텍스트를 공부해야 잘 이해가 된다! -> 영속성 컨텍스트 알아보기
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "customer_id")
private Long customer_id;
IDENTITY 전략은 em.persist()로 객체를 영속화시키는 시점에 insert 쿼리가 DB 전송되고, 거기서 반환받은 식별자 값을 가지고 1차 캐시에 엔티티를 등록시켜 관리한다.
MySQL의 경우 AUTO_INCREMENT를 활용하여 생성한다.
영속성 컨텍스트로 엔티티를 관리하려면, 1차 캐시에 Id 값을 Key 값으로 들고 있어야 한다.
그런데 JPA 입장에서는 DB에 insert SQL를 실행하기 전엔 AUTO_INCREMENT 되는 값을 알 수 없다!!
그의 해결방안으로, persist() 시점에 insert 쿼리가 실행되는 것이다.
따라서, 이 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다!!
성능 문제
IDENTITY 전략을 사용하면, 엔티티를 저장하기 전에는 ID 값을 알 수 없다.
따라서 트랜잭션 내에서 여러 번 엔티티를 저장하는 경우에도 매번 DB에서 값을 찾아서 업데이트해야 하기 때문에 성능이 저하될 수 있다.
@GenerationType. SEQUENCE
데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 데이터베이스 오브젝트이다.
SEQUENCE 전략은 이 시퀀스를 사용해서 기본 키를 생성한다.
이는 시퀀스를 지원하는 오라클, PostgreSQL, H2 데이터베이스에서 사용한다.
@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;
}
시퀀스 전략을 사용하기 위해서는 데이터베이스 시퀀스를 매핑해야 한다.
@SequenceGenerator를 사용하여 시퀀스 생성기를 등록한 후, @GeneratedValue의 generator 속성으로 "MEMBER_SEQ_GENERATOR"시퀀스 생성기를 선택한다.
@SequenceGenerator 속성
속성 | 설명 | 기본값 |
name | 식별자 생성기 이름 | 필수 |
sequenceName | 데이터베이스에 등록되어 있는 시퀀스 이름 | hibernate_sequence |
initialValue | DDL 생성 시에만 사용된다. 시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정한다. |
1 |
allocationSize | 시퀀스 한 번 호출에 증가하는 수. (성능 최적화에 사용됨) 데이터베이스 시퀀스 값이 하나씩 증가하도록 설정되어 있으면, 이 값을 반드시 1로 설정해야한다. |
50 |
catalog,schema | 데이터베이스 catalog, schema 이름 |
IDENTITY와 SEQUENCE의 차이점
IDENTITY 전략
persist() -> 엔티티를 DB에 저장 -> 식별자 조회 -> 엔티티에 할당 -> 영속성 컨텍스트에 저장
SEQUENCE 전략
persist() -> 식별자 조회(시퀀스 사용) -> 엔티티에 할당 -> 영속성 컨텍스트에 저장 -> (트랜잭션 커밋) -> 엔티티를 DB에 저장
@GenerationType. TABLE
TABLE 전략은 키 생성 전용 테이블을 하나 만들고, 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내 내는 전략이다.
SEQUENCE 전략과 매우 흡사하여, 시퀀스 대신 테이블을 사용하는 것 이외에는 내부 동작방식이 똑같다.
이 전략은 테이블을 사용하기 때문에, 시퀀스를 지원하지 않는 데이터베이스에서도 사용할 수 있다!
하지만 성능적인 손해가 있어서 잘 쓰지 않는다.
@Entity
@TableGenerator(
name = "BOARD_SEQ_GENERATOR",
table = "MY_SEQUENCE",
pkColumnValue = "BOARD_SEQ",
initialValue = 1,
allocationSize = 50
)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.TABLE,
generator = "BOARD_SEQ_GENERATOR")
@Column(name = "MEMBER_ID")
private Long id;
}
@TableGenerator 속성
속성 | 기능 | 기본값 |
name | 식별자 생성기 이름 | 필수 |
table | 키 생성 테이블명 | hibernate_sequences |
pkColumnName | 시퀀스 컬럼명 | sequence_name |
valueColumnNAme | 시퀀스 값 컬럼 명 | next_val |
pkColumnValue | 키로 사용할 값 이름 | 엔티티 이름 |
initialValue | 초기 값. 마지막으로 생성된 값이 기준 | 0 |
allocationSize | 시퀀스를 한 번 호출할 때 증가하는 수 | 50 |
catalog, schema | 식별자 생성기의 catalog,schema 이름 | |
uniqueConstraints(DDL) | 유니크 제약 조건을 지정할 수 있다. |
확실히 @SequenceGenerator와 많이 유사한 것을 볼 수 있다.
나는 JPA와 MySQL을 사용하기 때문에, @GeneratedValue의 GenerationType으로 IDENTITY를 설정했다.
기본값인 AUTO는 못 미더우니, 상황에 맞게 전략을 선택하여 사용하자!
틀린 부분이 있다면 지적해 주시면 감사하겠습니다.
Reference
[JPA] 기본 키(Primary Key)매핑 - @Id, @GeneratedValue
[Spring boot] JPA 기본 키 생성 전략(AUTO, IDENTITY, SEQUENCE, TABLE, UUID
JPA 기본키 생성 전략, @GeneratedValue 사용시 주의점
'Server' 카테고리의 다른 글
[Tomcat] The origin server did not find a current representation for the target resource or is not willing to disclose that one exists. 404 에러 해결 (0) | 2024.09.23 |
---|---|
[ssh] not a directory 해결, ssh란? (0) | 2024.09.21 |
[JPA] 영속성 컨텍스트 #2 (0) | 2024.08.08 |
[SpringBoot] Entity 생성하기 / JPA 간단한 예제 (0) | 2024.08.07 |
[JPA] cannot drop table '' referenced by a foreign key constraint '' on table 오류 해결 (0) | 2024.08.05 |