오늘은 JPA를 공부하면서, DB의 전략에 따라 @Id가 부리는 마법(?)에 대해서 조금 다뤄보려고 합니다.
em.persist
저희는 지금까지 em.persist()를 통해서 비영속 상태의 엔티티를 영속 상태로 변환한다는 것을 알았습니다. 또한, 우리가 em.persist()를 하는 순간에는 DB에 바로 반영이 되지 않고 커밋되는 시점에서야 비로소 DB에 저장된다는 것을 익히 알고 있을 것입니다.
하지만 이 것은 틀렸습니다.
예를 들어서, 반례를 들어보죠. 저희가 아래와 같은 Member 엔티티를 구성했다고 해봅시다.
@Entity
@Getter
public class Member {
@Id @GeneratedValue
private Long Id;
private String name;
}
여기에서 PK는 Id가 되고, 이 Id는 DB에서 자동으로 정해주기 때문에, 저희는 단지 이름만 전달해주면 됩니다. 그렇다면 저희가 커밋하기 이전에, member.getId()를 호출하게 되면 어떻게 될까요?? 아래의 상황을 가정해봅시다.
Member member = new Member();
member.setName("Hello JPA");
em.persist(member);
System.out.println(member.getId());
만약 이러 코드가 있다면, NullPointException이 터질까요?? 그렇지 않습니다. 그럼 어떻게 JPA는 사용자에게 받은 null값을 대체할까요?? 실제로 어떤 Id가 들어올지는 DB에서 결정할텐데 말이죠.
이 이유를 알기 위해서는 우선, 각 DB마다 갖고있는 전략을 파악해야합니다.
IDENTITY 전략
MySQL과 같은 DB는 IDENTITY 전략을 사용합니다. MySQL에는 auto_increment라는 기능이 있습니다. 이러한 기능을 이용해서 DB에서는 null값이 들어와도, 자동으로 숫자를 부여하게 되는데요, JPA는 이러한 기능을 활용합니다. @Id가 붙은 엔티티를 persist할 때, 1차 캐시에 저장하지 않고 바로 DB에 쿼리를 날립니다. 그렇게 DB에 Id가 매핑이 된 채로 저장된다면, 그 이후에 select를 통해서 DB에서 해당 엔티티를 가져오고 그 가져온 값을 1차 캐시에 저장합니다. 이렇게 되면 JPA는 Id의 값을 알 수 있게 되는 것이죠.
하지만 단점으로는, 쓰기 지연이 불가능하다는 단점이 있습니다.
SEQUENCE 전략
시퀀스 전략은 보통 H2, Oracle과 같은 DB에서 사용하는 전략입니다. DB 내에서 시퀀스라는 항목을 갖고 있는 것을 확인하실 수 있습니다.
그럼 이 전략은 어떻게 Id를 가져올 수 있을까요?? 앞서 말한 시퀀스라는 항목이 사실 이 데이터베이스에서 다음 Id값이 무엇인지를 나타내게 됩니다. 따라서 이 전략에서는 DB에 INSERT문을 넣지 않고, sequence의 다음 값을 call 하여 알 수 있습니다. (사실 이 시퀀스의 동작 방식도 되게 특이하고 재밌는데, 너무 길어질 것 같으니 여기서 다루지 않겠습니다.)
실제로 IDENTITY 전략에서 소개한 코드를 시퀀스 전략 DB에 사용하면, INSERT문이 나가지 않고, call을 통해서 시퀀스의 다음 값을 읽어오는 로그를 보실 수 있습니다.
따라서 이 시퀀스 전략은, 쓰기 지연을 보장할 수 있다는 장점이 있습니다.
저희가 어느 데이터베이스를 사용하는 지에 따라 사용할 수 있는 전략이 다르지만, 보통은 AUTO라는 값으로 설정되어있어 알아서 DB의 전략에 맞게 사용됩니다. 이러한 과정을 알고, 프로젝트를 진행하거나 JPA를 공부하면 더 재밌고 유익할 것 같습니다.😀
'JPA' 카테고리의 다른 글
[JPA] 변경 감지와 병합 (0) | 2024.01.15 |
---|---|
[JPA] 복합키 등록하기 (0) | 2023.08.18 |
@Transactional의 선언 위치 (0) | 2023.08.17 |
DTO, DAO란? (DAO와 Repository의 차이) (0) | 2023.08.10 |
임베디드 타입(Embedded Type)이란? (0) | 2023.08.08 |