- JPA 등록, 기본 키 생성 전략2023년 06월 19일
- starryeye
- 작성자
- 2023.06.19.:07
반응형이번엔 JPA 의 등록 과정에 대해 알아보겠다.
JPA 의 등록 과정은 크게 2단계를 따른다.
1. 영속성 컨텍스트에 엔티티 등록
-> 엔티티를 영속 상태로 변경
2. 플러시 과정을 거쳐 영속성 컨텍스트에 존재하는 엔티티를 DB 에 반영
-> 쓰기 지연 SQL 저장소의 SQL 을 DB 에 보낸다.
이제 자세하게 알아보겠다..
먼저 위 사진을 한번 보자..
persist 메서드를 호출하면 해당 엔티티에 대해서..
insert 문이 생성되어 쓰기 지연 SQL 저장소에 저장되고
1차 캐시에 저장되는 두개의 작업이 이루어진다
코드를 통해 이해해보자..
addMember 메서드를 호출하면 어떤일이 발생하는지 알아보겠다.
1. @Transactional 어노테이션이 적용된 메서드이므로 수동 커밋 모드로 진행되며..
메서드 내부 로직들은 하나의 트랜잭션 (영속성 컨텍스트) 로 동작하게 된다.
2. 엔티티 매니저에 persist 메서드로 비영속 상태의 Member 엔티티를 영속 상태로 변경한다.
-> 1차 캐시에 식별자와 엔티티의 인스턴스를 저장한다.
-> 쓰기 지연 SQL 저장소에 해당 엔티티를 DB 에 등록할 SQL 을 적재한다.
3. 해당 메서드가 종료되고 플러시, 트랜잭션 커밋이 수행된다.
4. 플러시 단계에서는 변경 감지가 동작하는데 현재는 수정된 엔티티가 없으므로..
변경 감지는 동작하지 않고, 쓰기 지연 SQL 저장소의 쿼리(insert) 가 DB에 전송된다.
<참고>
플러시는 다음의 절차를 따른다.
1. 변경 감지(Dirty Check) 가 동작해서 1차 캐시에 존재하는
엔티티와 스냅샷을 비교하여 수정된 엔티티를 찾는다.
2. 수정된 엔티티는 수정 쿼리를 만들어 쓰기 지연 SQL 저장소에 등록한다.
3. 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송한다.
실행 결과
지금 부터는 엔티티 식별자 생성 방식을 MySQL 의 Auto increment 방식인
@GeneratedValue(strategy = GenerationType.IDENTITY) 이라 생각하고 보자.위에서 설명한 등록 과정에 따르면..
플러시 단계에서 쓰기 지연 SQL 저장소의 쿼리가 DB 로 전달 되기 때문에..
쿼리는 Change an entity from non-persistent state to persistent state.. 로그 보다 늦게 찍혀야한다고 생각할 수 있지만..
실제 실행 결과에서는 쿼리가 로그보다 빨리 찍힌 것을 볼 수 있다..
그 이유를 알기 위해..기본키(식별자) 생성 전략에 대해 알아보자..
직접 할당 전략
Application 에서 직접 기본 키를 생성하고 할당한 후, DB 에 적재한다.
<참고>
Spring Data JPA 에서 직접할당 전략을 사용하고 save 메서드로 엔티티 등록을 하면..식별자가 존재하는 상태에서 isNew 메서드를 호출 하므로.. merge 메서드가 동작해버린다..
-> Persistable 을 상속받아서 isNew 메서드를 오버라이딩 해주는게 좋다.
자동 생성 전략
데이터 베이스가 기본 키를 생성하도록 한다.
- IDENTITY : 기본 키 생성을 데이터 베이스에 위임한다.
- SEQUENCE : 데이터 베이스 시퀀스를 사용하여 기본 키를 할당한다.
- TABLE : 키 생성 테이블을 별도로 두고 이를 활용하여 기본 키를 생성한다.
데이터베이스 밴더에 따라 자동 생성 방식의 전략은 다르다.
-> MySQL 은 Auto Increment 로 IDENTITY 방식, Oracle 은 시퀀스를 사용한다.
JPA 에서의 자동 생성 전략을 구체적으로 알아보겠다.
IDENTITY 전략
기본키 생성을 데이터 베이스에 위임하는 전략이다.
대표적으로 MySQL 에서 사용된다.
MySQL 에서 Auto Increment 기능은 데이터베이스가 기본 키를 자동으로 생성해준다.
데이터 베이스에 데이터를 저장할 때, PK 를 비워두면 데이터 베이스가 값을 채워준다.
따라서, IDENTITY 전략은 PK 를 DB 에서 할당 해주므로..
데이터를 저장하고 나서야 PK 를 알 수 있다.
그러면, Application 에서 해당 키를 알려면..
데이터베이스에 데이터를 삽입(insert) 하고
추가로 해당 데이터를 조회(select) 해야한다고 생각할 수 있다.
-> JDBC3 에서 삽입과 키 조회를 동시에 해주는 메서드를 제공해준다.
이를 이용하면 insert 쿼리 한번의 통신으로 가능하다.
<주의 사항>
영속 상태의 엔티티는 식별자로 동일성을 보장해주기 때문에,
영속 상태의 엔티티는 식별자가 반드시 필요하다.
따라서, IDENTITY 식별자 생성 전략을 사용하면..
비 영속 상태의 엔티티를 저장할 때, 데이터베이스에 삽입을 해야 식별자를 구할 수 있으므로..
persist 메서드를 호출하는 즉시 등록 쿼리가 데이터베이스에 전달된다.
-> 즉, 등록에 대해 쓰기 지연이 동작하지 않는다.
-> 그래서, 아까 실행 결과 로그에서 Change.. 로그 보다 삽입 쿼리가 먼저 찍힌 것이다.
<주의 사항>
IDENTITY 식별자 생성 전략에서
쓰기 지연이 동작하지 않는다는 말은 등록에 한정한 것이며..
수정, 삭제에 대해서는 쓰기 지연이 동작한다.
또한, 해당 등록 쿼리만 즉시 전달되는 것이며, 플러시와는 무관하다.
<참고>
JPA 에서는 플러시의 쓰기 지연을 활용하여 아래 옵션을 주면.. JDBC 가 제공하는 SQL 배치 기능을 사용할 수 있다.
- hibernate.jdbc.batch_size=50
(@BatchSize 는 확인 필요)
- rewriteBatchedStatements=true
예를 들면..
동일한 쿼리(값은 다를 수 있음)의 persist 메서드를 여러번 호출하면..
(JPA 의 persist * N, Spring Data JPA 의 save * N / saveall)
쓰기 지연에 의해 SQL 배치가 수행되어
여러 개의 쿼리가 하나의 쿼리로 합쳐져서 DB 로 전달된다.(bulk insert, bulk batch)
하지만, IDENTITY 전략을 사용한다면..
해당 insert 쿼리에 대한 쓰기 지연은 동작하지도 않을 것이고..
기본 키를 얻어내야 하므로 엔티티 하나하나에 대한 insert 쿼리가 전달된다.
SEQUENCE 전략
데이터 베이스 시퀀스는 유일한 값을 순서대로 생성하는 데이터 베이스 오브젝트이며,
이 시퀀스를 이용해 기본 키를 생성한다.
시퀀스 전략은..
persist 메서드를 호출하면, 시퀀스로 부터 식별자를 얻는다.(조회)
반환된 식별자로 해당 엔티티를 영속 상태로 만든다.
이 후, 플러시에 의해 쓰기 지연 SQL 저장소에 있는 쿼리가 DB 로 전달 될 때
DB 에 저장한다.
-> 즉, 쓰기 지연이 동작할 수 있다.
그러나, 시퀀스 전략은 위와 같이 식별자를 구하는 과정에서 DB 로 통신이 일어나게 되는데..
JPA 는 이를 최적화 하기 위해서 식별자를 구할 때 한번에 많은 식별자를 미리 받아서
가지고 있다가 필요할 때 하나씩 쓰게된다.
-> 시퀀스에 접근하는 횟수를 줄일 수 있게 되었다.
-> 식별자를 미리 받는다는 것은 식별자를 선점한다는 것이다. 다른 Application 에서 해당 식별자를 받을 수 없다.
TABLE 전략
테이블 전략은 시퀀스 전략과 비슷한데..
시퀀스 역할을 하는 테이블을 직접 만들어서 사용하는 방식이다.
반응형'Spring > DB, Cache 연동' 카테고리의 다른 글
Spring Data JPA (Hibernate), N + 1 Query Problem (0) 2024.02.20 Spring 과 JPA (0) 2024.02.14 JPA 변경 감지와 플러시 (0) 2023.06.15 JPA Entity Default Constructor (1) 2023.06.07 JPA Merge, Dirty Check (0) 2023.06.06 다음글이전글이전 글이 없습니다.댓글