일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 백준 16719
- java 기술면접
- 파이썬
- 백준 16235
- 프로그래머스
- java
- sql 기술면접
- with recursive
- spring security
- JPA
- MySQL
- spring oauth
- 백준
- Coroutine
- Spring Boot
- 백준 16236
- 웹어플리케이션 서버
- re.split
- Spring
- 백준 17779
- MSA
- 백준 19238
- 프로래머스
- spring cloud
- Kotlin
- JVM
- springboot
- 백준 파이썬
- 백준 17626
- 백준 15685
- Today
- Total
시작이 반
[JPA]영속성 본문
이전에 영속성에 대해서 이런거구나 하고 넘어갔는데
막상 누가 물어보면 대답을 못했다... 때문에 이번에 정리해보고 넘어가보자
영속성 컨텍스트란?
영속성 컨텍스트란 엔티티를 영구 저장하는 환경 이라는 뜻이다.
어플리케이션과 데이터베이스 사이에 객체를 보관하는 가상의 데이터베이스 같은 역할이라고 한다.
EntityManager에 의해서 관리되는 상태가 바로 영속성 상태이다.
영속성의 상태는 3가지 이다.
- 영속(Managed) : 영속성 컨텍스트에 저장된 상태
- 준영속(Detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태
- 삭제(Removed) : 삭제된 상태
EntityManager entityManager = new EntityManager();
Test test = new Test(); // 비영속
entityManager.persist(test); // 영속
entityManager.detach(test); // 엔티티를 영속성 컨텍스트에서 분리하여 준영속 상태로 만든다.
entityManager.clear(); // 영속성 컨텍스트를 비워도 관리되던 엔티티는 준영속 상태가 된다.
entityManager.close(); // 영속성 컨텍스트를 종료해도 관리되던 엔티티는 준영속 상태가 된다.
entityManager.remove(test); // 삭제
영속성 컨텍스트 특징
영속성 컨텍스트는 엔티티를 식별자 값으로 구분하기 때문에 식별자 값이 반드시 있어야한다.
트랜잭션을 커밋하면 영속성 컨텍스트에 새로 저장된 엔티티를 데이터베이스에 반영한다.
(커밋 하는 시점에 내부적으로 flush()가 호출된다. 아래 설명하겠다.)
영속성 컨텍스트를 사용하면 다음과 같은 이점이 있다.
- 1차 캐시
- 쓰기지연
- 변경감지
- 지연로딩
1차 캐시
영속성 컨텍스트 내부에는 캐시가 있다.
영속 상태의 엔티티를 이 캐시에 저장하게 된다. 1차 캐시의 키는 식별자 값이고 값은 엔티티 인스턴스이다.
조회 흐름을 생각해보자.
- 1차 캐시에서 해당 엔티티를 찾는다.
- 있으면 메모리에 있는 1차 캐시에서 엔티티를 조회한다.
- 없으면 데이터베이스에서 조회한다.
- 조회한 데이터로 엔티티를 생성해 1차 캐시에 저장한다. (영속화)
- 조회한 엔티티를 반환한다.
em.find(Member.class, "member1");
em.find(Member.class, "member2");
어찌됐든 DB에서 조회는 1번만 이뤄지기때문에 성능을 향상시킬 수 있다.
여기서 캐시에서 찾은 엔티티와 DB에서 찾은 엔티티의 동일성이 보장된다.
쓰기지연
Transaction이 시작되는 시점부터 끝나는 시점까지 SQL을 모아 놓고 종료되는 시점에 commit이 호출됨과 동시에 모아둔 쿼리를 한번에 보낸다.
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction(); //엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야한다.
transaction.begin(); // 트랜잭션 시작
em.persist(memberA);
em.persist(memberB);
//persist함수를 호출했다 해도 바로 insert 쿼리문을 보내지 않는다.
//쓰기 지연 저장소에 쌓아놓는다.
//커밋을 하면 insert 문을 날린다.
transaction.commit(); //트랜잭션 커밋
커밋을 하게되면 flush()문이 호출된다.
flush가 호출되면 데이터베이스에 반영된다.
여기서 JPA의 흔히 Repository라고 하는 인터페이스에 save()라는 함수가 있다. 이 함수는 insert를 담당하는데 그러면 persist가 호출이 되는 것이라고 생각할 것이다.
save의 구현체를 봐보자.
@Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
새로운 상품이면 persist가 호출되는 것이 맞지만 merge도 호출이 된다.
그러면 merge는 뭘까?
merge는 준영속 상태의 객체로 덮어 씌우는 방식이다.
merge는 언제 호출이 될까?
엔티티의 식별자 값이 이미 세팅이 되어있을때 호출이 된다. 이미 세팅이 되어있어서 DB에 존재하는 데이터로 간주하고 insert를 하지 않고 update를 할 항목이 있는지 select를 한다.
select값이 없다면 insert쿼리문이 실행된다.
변경감지
JPA를 엔티티로 수정하고 싶으면 엔티티를 조회해서 데이터를 변경하면 된다.
- 트랜잭션을 커밋하면 엔티티 매니저 내부에서 먼저 flush가 호출된다.
- flush가 호출되면 1차캐시에 저장된 엔티티와 스냅샷을 비교하여 변경된 엔티티를 찾는다. (스냅샷은 최초로 1차캐시에 들어온 상태를 저장한 것이다.)
- 변경된 엔티티가 있으면 쓰기 지연 저장소에 저장한다.
- flush로 반영한다.
- 커밋을 한다.
지연로딩
지연로딩이란 해당 엔티티에 연관관계가 존재할 경우 연관 관계의 데이터를 바로 조회하는 것이 아닌 필요한 시점에 조회하게 된다.
참고
'Programming > JPA' 카테고리의 다른 글
[JPA]OSIV (0) | 2021.06.23 |
---|---|
[JPA] One To Many에서 join fetch ( 페이징 ) (1) | 2021.06.07 |
[JPA] 1 + n 조회, x To One에서 join fetch (0) | 2021.05.29 |
[JPA] xToMany - ManyToX (0) | 2021.05.29 |
[JPA] 변경감지와 병합(merge) (1) | 2021.05.27 |