---
tags : jpa, quiz
---
JPA - Quiz 자동 키 생성전략시 IDENTITY, SEQUENCE 의 쓰기지연 동작 차이 (정답 및 해설)
===
## 문제 풀기 전 환경 설명
### 2가지 자동키 생성전략을 모두 사용함. SEQUENCE, IDENTITY
1. 테스트 환경 에서는 H2 DB를 사용하며, Key 생성전략이 `SEQUENCE` 로 설정 된다고 가정 하겠습니다.
2. 개발 환경 에서는 Mysql DB를 사용하며, Key 생성전략이 `IDENTITY` 로 설정 된다고 가정 하겠습니다.
> Boot 에서 @DataJpaTest 를 이용하면 H2 를 이용하며 SEQUENCE 전략이 채택 됩니다.
> 우리는 Mysql 에서 키 생성전략을 false 로 설정하여 IDENTITY 전략이 채택 됩니다. 이로 인해 AI 속성을 쓸수 있습니다.
SEQUENCE, IDENTITY 두 전략을 모두 사용하는 환경을 알아야 하는 이유는,
>1. SEQUENCE 전략이 JPA 의 스탠다드 설정 입니다.
>2. 때문에 향후를 위해서도 SEQUENCE 전략은 이해 해 두는것이 좋습니다.
>3. 하지만 우리는 Mysql 을 사용중이며 AI 속성을 버리기엔 성능상 단점이 생기므로 IDENTITY 설정을 사용 합니다.
>4. Mysql 을 쓰면서도 SEQUENCE 전략을 사용할수 있습니다.
>6. 두 전략을 비교 해 보면서 쓰기지연에 대해서 좀더 깊게 이해 할수 있습니다.
### Member 엔티티 준비
문제에 활용될 Member 엔티티는 다음과 같습니다.
```java=
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String name;
public Member(String name) {
this.name = name;
}
}
```
`@GeneratedValue` 에서 strategy 를 생략하면 AUTO 전략으로 구동 되는데, AUTO 전략은 위에 얘기한것과 같이 DB 벤더마다 다르게 동작하게 만들수 있습니다.
- H2 로 구동하면
`@GeneratedValue(strategy = GenerationType.SEQUENCE)` 로 설정 되고,
- Mysql로 구동하면
`@GeneratedValue(strategy = GenerationType.IDENTITY)` 로 설정 됩니다.
AUTO 전략 에 대한 정확한 설명은 [JPA 자동키 생성전략 GenerationType.AUTO
](https://hackmd.io/@bonjugi/rJMPlcwTN) 를 보시면 좋지만 문제를 푸는데는 지장 없으므로 이정도만 이해하고 넘어가도록 하겠습니다.
### MemberRepository
```java
public interface MemberRepository extends JpaRepository<Member, Long> {
}
```
아직 책의 진도상 영속화 하는방법은 EntityManager 를 직접 사용해서 em.persit(member) 메소드를 이용 하는법 밖에 안배웠지만, 어려워 하실것 없습니다.
repository.save(member) 로 영속화 할수 있으며, 동작에 큰 차이가 없으므로 문제를 푸는데는 지장 없습니다.
## 문제
아래 `문제코드`는 총 6개의 로그를 찍도록 해놨습니다.
`sql 로그 1,3,5 번` 3개와 `info 로그 2,4,6번` 3개 입니다.
```java=
@Test @Transactional
public void test() {
Member first = repository.save(new Member("first")); // 1. save 쿼리
log.info("first 를 저장합니다."); // 2. save 로깅
first.changeName("second"); // 3. second update 쿼리
log.info("second 로 변경합니다."); // 4. second 로깅
first.changeName("third"); // 5. third update 쿼리
log.info("third 로 변경합니다."); // 6. third 로깅
}
```
위 코드에 의해 출력되는 로그는 다음과 같습니다.
```
1. insert into .. (sql)
2. first 를 저장합니다.
3. update .. set name = 'second' (sql)
4. second 로 변경합니다.
5. update .. set name = 'third' (sql)
6. third 로 변경합니다.
```
하지만 쓰기지연 이 동작 하게 되면 위 로깅이 순서대로 찍히지 않게 됨을 예상할수 있습니다.
#### 문제. 로그의 순서를 SEQUENCE 전략 일때와 IDENTITY 전략 일때 2가지 모두 나열 해 보세요.
예를들면 이렇게 작성해 주시면 됩니다. (숫자만 써도 상관 없습니다.)
```
<답>
SEQUENCE :
2. first 를 저장합니다.
1. insert into .. (sql)
4. second 로 변경합니다.
3. update .. set name = 'second' (sql)
6. third 로 변경합니다.
5. update .. set name = 'third' (sql)
IDENTITY :
6. third 로 변경합니다.
4. second 로 변경합니다.
2. first 를 저장합니다.
3. update .. set name = 'second' (sql)
5. update .. set name = 'third' (sql)
1. insert into .. (sql)
<해설>
{써도되고 안써도 됩니다.}
```
### 정답
```
SEQUENCE : 2>4>6>1>5
IDENTITY : 1>2>4>6>5
```
### 해설
#### H2
@Transactional 으로 관리되고 있다면, 영속성컨텍스트 의 스냅샷과 비교하고 변경사항의 쿼리들을 `쿼리저장소(가칭)` 에 저장 합니다.
이렇게 저장된 쿼리들은 begin~commit 직전까지 보관하고 있다가 마지막에 flush 를 발생 시키면서 한번에 쿼리들을 execute 합니다.
때문에 `모든 sql 로그 1,3,5번` 은, `모든 info 로그 2,4,6번` 이후에 발생 하는것을 확인 할 수 있습니다.
단, `3. update .. set name = 'second' (sql)` 은 생략된것을 볼수 있습니다.
이유는 영속성 컨텍스트가 스냅샷 비교시 변경사항을 first->second->third 히스토리를 모두 갖고있는것이 아니라, 마지막에 third 로 변경된것만을 알고 있어도 되기 때문에 second 로의 변경사항은 무시 되었기 때문 입니다.
#### Mysql
H2 때와 공통적인 원리는 `모든 info 로그 2,4,6번` 번이 먼저 발생하고, `모든 sql 로그 1,3,5번` 가 나중에 발생 하는것 입니다.
차이점으로는 `1. insert into .. (sql)` insert 쿼리가 가장 먼저 출력됩니다.
이유는, 엔티티를 영속성 컨텍스트에 보관하기 위해서 @ID 는 필수인데, AI 속성 때문에 실제로 DB에 레코드가 저장되기 전엔 id 가 생성되지 않기 때문 입니다.
때문에 id를 할당받기 위해 어쩔수없이 save 시점에 바로 쿼리를 날리게 됩니다.