JPA를 이용함에 있어서 가장 먼저 해야할 것은 엔티티로 사용할 객체와 데이터베이스 테이블을 정확하게 매핑하는 것이다.
매핑에 필요한 기본 어노테이션
@Entity
, @Table
@Id
@Column
@OneToMany
, @ManyToOne
JPA를 통해 데이터베이스 테이블과 매핑할 엔티티 클래스는 @Entity
어노테이션을 필수로 붙여주어야 한다.
@Entity 어노테이션이 붙어있는 클래스는 JPA가 관리한다.
@Entity
public class Member{
//...
long id;
}
@Entity
public class Member{
@Id
@GeneratedValue(strategy = GenerationType.INDENTITY)
Long id;
}
@GeneratedValue(strategy = GenerationType.INDENTITY)
기본키 생성을 데이터베이스가 관리하기 때문에 em.persist()시 엔티티에는 id==null
인 상태이다.
그렇기 때문에 em.persist()를 하여도 영속성 컨텍스트의 1차캐시에 저장되지 않는다.
하지만 IDENTITY 전략만 예외적으로 em.persist()시 바로 INSERT SQL을 생성하여 데이터베이스에 전달하여 해당 엔티티의 id를 조회하여 엔티티에 적용 후 1차 캐시에 저장
하게 된다.
@Entity
@SequenceGenerator(name = "MEMBER_SEQ_GENERATOR", sequenceName="MEMBER_SEQ")
public class Member{
@Id
@GeneratedValue(
strategy = GenerationType.SEQUNECE,
generator = "MEMBER_SEQ_GENERATOR")
)
Long id;
}
@GeneratedValue(strategy = GenerationType.SEQUNECE, generator = "[식별자 생성기 이름]")
@SequenceGenerator(name="[식별자 생성기 이름]", sequenceName="[데이터베이스 시퀀스 이름]")
id값을 데이터베이스 시퀀스(sequenceName)에서 가져온다.
SEQUENCE전략도 id==null이기 때문에 1차 캐시에 해당 엔티티를 저장할 수 없다. 하지만 SEQUENCE전략은 em.persis()를 호출하기 전 데이터베이스의 SEQUENCE에서 기본값을 가져온다(hibernate: call next value for "[데이터베이스 시퀀스 이름]"
).
그 후 엔티티를 데이터베이스 SEQUENCE에서 가져온 시퀀스를 기본키로 적용 후 em.persist()가 수행되기 때문에 1차 캐시에 저장되어 영속상태가 될 수 있다.
IDENTITY전략과 다르게 em.persist()가 호출될 때 id값이 있으므로 쓰기 지연이 가능하다.
allocationSize의 default가 50인 이유
SEQUENCE전략은 데이터베이스 SEQUENCE에서 id를 받아와 영속상태로 저장하게 되는데, 이는 실질적으로 두번의 데이터베이스와의 통신이 있어야 한다. 그렇기 때문에 성능상 이슈가 발생할 수 있다.
이를 해결하기 위해 allocationSize를 사용한다. 이는 allocationSize가 50일 때, 데이터베이스 SEQUENCE와 1번 통신했을 때 메모리상에 50개를 저장해두고(SEQUENCE에는 51로 갱신된다.) 시퀀스가 필요할 때 데이터베이스 SEQUENCE와의 통신 대신에 메모리에 있는 시퀀스를 사용
하게된다.
메모리상의 기본키를 모두 사용하게 되었을 때, 데이터베이스 SEQUENCE와 통신하여 시퀀스를 갱신하여 사용하게 된다.
allocationSize를 너무 크게 잡게되면 중간에 애플리케이션을 중지하게 되면 사용되지 않는 시퀀스가 삭제가 되기 때문에 사용되지 않는 값들이 버려지게 된다.
@Entity
@TableGenerator(name = "MEMBER_SEQ_GENERATOR", table="MEMBER_SEQ")
public class Member{
@Id
@GeneratedValue(
strategy = GenerationType.TABLE,
generator = "MEMBER_SEQ_GENERATOR")
)
Long id;
}
@Entity
public class Member{
@Id
@GeneratedValue(
strategy = GenerationType.AUTO)
)
Long id;
}
@GeneratedValue(strategy = GenerationType.AUTO)
자연키는 환경에 따라서 값이 변경될 수 있다.(개인정보 보호에 의해서 주민번호의 저장이 의무가 되지 않는다면 null이 가능해진다.)
그렇기 때문에 기본키 제약 조건을 모두 만족하는 대리키를 기본키로 선택
하는 것이 좋다.
Long
+ 대리키
+ 기본키 생성 전략
int 타입보다 더 많은 정수를 표현할 수 있다.
자연키보다 기본키 제약 조건을 만족하는 조건을 가지고 있다.
데이터베이스에 맞는 기본키 생성 전략을 사용해야한다.(IDENTITY, SEQUENCE ...)
https://gmlwjd9405.github.io/2019/08/12/primary-key-mapping.html
https://velog.io/@conatuseus/%EC%97%94%ED%8B%B0%ED%8B%B0-%EB%A7%A4%ED%95%91-2-msk0kq84v5