--- 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 시점에 바로 쿼리를 날리게 됩니다.