영속성 컨텍스트
- Persistence를 한글로 번역하면 영속성, 지속성 이라는 뜻
- Persistence를 객체의 관점으로 해석해 보자면 ‘객체가 생명(객체가 유지되는 시간)이나 공간(객체의 위치)을 자유롭게 유지하고 이동할수 있는 객체의 성질’을 의미
- Entity 객체를 효율적으로 쉽게 관리하기 위해 만들어진 공간
- 개발자들이 직접 SQL을 작성하지 않아도 JPA를 사용하여 DB에 데이터를 저장하거나 조회할 수 있으며 수정, 삭제 가능
- 이러한 일련의 과정을 효율적으로 처리하기 위해 JPA는 영속성 컨텍스트에 Entity 객체들을 저장하여 관리하면서 DB와 소통
영속성 컨텍스트의 기능
1차 캐시
- 영속성 컨텍스트는 내부적으로 캐시 저장소를 가지고 있음
- 저장하는 Entity 객체들이 1차 캐시 즉, 캐시 저장소에 저장된다고 생각하면 됨
- 캐시 저장소는 Map 자료구조 형태로 되어있음
- key에는 @Id로 매핑한 기본 키 즉, 식별자 값을 저장
- value에는 해당 Entity 클래스의 객체를 저장
- 영속성 컨텍스트는 캐시 저장소 Key에 저장한 식별자값을 사용하여 Entity 객체를 구분하고 관리함
Entity 저장
- em.persist(memo); 메서드가 호출되면 memo Entity 객체를 캐시 저장소에 저장
Entity 조회
- 캐시 저장소에 조회하는 Id가 존재하지 않은 경우
- 캐시 저장소 조회
- DB SELECT 조회 후 캐시 저장소에 저장
- em.find(Memo.class, 1); 호출 시 캐시 저장소를 확인 한 후 해당 값이 없다면 DB에 SELECT 조회 후 해당 값을 캐시 저장소에 저장하고 반환
- DB에서 데이터를 조회만 하는 경우에는 데이터의 변경이 발생하는 것이 아니기 때문에 트랜잭션이 없어도 조회가 가능
2. 캐시 저장소에 조회하는 Id가 존재하는 경우
- em.find(Memo.class, 1); 호출 시 캐시 저장소에 식별자 값이 1이면서 Memo Entity 타입인 값이 있는지 조회
- 값이 있다면 해당 Entity 객체를 반환
'1차 캐시' 사용의 장점
- DB 조회 횟수를 줄임
- **'1차 캐시'**를 사용해 DB row 1개 당 객체 1개가 사용되는 것을 보장 (객체 동일성 보장)
Entity 삭제
- 삭제할 Entity를 조회한 후 캐시 저장소에 없다면 DB에 조회해서 저장
2. em.remove(entity);
- em.remove(memo); 호출 시 삭제할 Entity를 DELETED 상태로 만든 후 트랜잭션 commit 후 Delete SQL이 DB에 요청
쓰기 지연 저장소(ActionQueue)
flush()
- flush 메서드는 영속성 컨텍스트의 변경 내용들을 DB에 반영하는 역할을 수행
- 쓰기 지연 저장소의 SQL들을 DB에 요청하는 역할을 수행
- 트랜잭션을 설정하지 않고 플러시 메서드를 호출하면 no transaction is in progress 메시지와 함께 TransactionRequiredException 오류가 발생
변경 감지(Dirty Checking)
- 영속성 컨텍스트에 저장된 Entity가 변경될 때마다 Update SQL이 쓰기 지연 저장소에 저장된다면 하나의 Update SQL로 처리할 수 있는 상황을 여러번 Update SQL을 요청하게 되기 때문에 비효율적이게 됨
JPA에서는 Update를 처리하는 방법
- JPA는 영속성 컨텍스트에 Entity를 저장할 때 최초 상태(LoadedState)를 저장
- 트랜잭션이 commit되고 em.flush(); 가 호출되면 Entity의 현재 상태와 저장한 최초 상태를 비교
- 변경 내용이 있다면 Update SQL을 생성하여 쓰기 지연 저장소에 저장하고 모든 쓰기지연 저장소의 SQL을 DB에 요청
- 마지막으로 DB의 트랜잭션이 commit 되면서 반영됨
- 따라서 변경하고 싶은 데이터가 있다면 먼저 데이터를 조회하고 해당 Entity 객체의 데이터를 변경하면 자동으로 Update SQL이 생성되고 DB에 반영됨
- 이러한 과정을 변경 감지, Dirty Checking이라고 함
EntityManager
- 영속성 컨텍스트에 접근하여 Entity 객체들을 조작하기 위해서는 EntityManager가 필요
- EntityManager는 이름 그대로 Entity를 관리하는 관리자
- 개발자들은 EntityManager를 사용해서 Entity를 저장하고 조회하고 수정하고 삭제할 수 있음
- EntityManager는 EntityManagerFactory를 통해 생성하여 사용 가능
EntityManagerFactory
- EntityManagerFactory는 일반적으로 DB 하나에 하나만 생성되어 애플리케이션이 동작하는 동안 사용됨
- EntityManagerFactory를 만들기 위해서는 DB에 대한 정보를 전달해야 함
트랜잭션
- DB 데이터들의 무결성과 정합성을 유지하기 위한 하나의 논리적 개념
- DB의 데이터들을 안전하게 관리하기 위해서 생겨난 개념
- 가장 큰 특징은 여러 개의 SQL이 하나의 트랜잭션에 포함될 수 있다는 점
- 이때, 모든 SQL이 성공적으로 수행이 되면 DB에 영구적으로 변경을 반영하지만 SQL 중 단 하나라도 실패한다면 모든 변경을 되돌림
- JPA는 DB의 이러한 트랜잭션 개념을 사용하여 효율적으로 Entity를 관리함
JPA의 트랜잭션
- 영속성 컨텍스트에 Entity 객체들을 저장했다고 해서 DB에 바로 반영 되지는 않음
- DB에서 하나의 트랜잭션에 여러 개의 SQL을 포함하고 있다가 마지막에 영구적으로 변경을 반영하는 것 처럼 JPA에서도 영속성 컨텍스트로 관리하고 있는 변경이 발생한 객체들의 정보를 쓰기 지연 저장소에 전부 가지고 있다가 마지막에 SQL을 한번에 DB에 요청해 변경을 반영함
- JPA에서 이러한 트랜잭션의 개념을 적용하기 위해서는 EntityManager에서 EntityTransaction을 가져와 트랜잭션을 적용할 수 있습니다.
- EntityTransaction et = em.getTransaction();
- 해당 코드를 호출하여 EntityTransaction을 가져와 트랜잭션을 관리할 수 있음
- et.begin();
- 트랜잭션을 시작하는 명령어
- et.commit();
- 트랜잭션의 작업들을 영구적으로 DB에 반영하는 명령어
- et.rollback();
- 오류가 발생했을 때 트랜잭션의 작업을 모두 취소하고, 이전 상태로 되돌리는 명령어
Spring의 트랜잭션
- @Transactional 애너테이션을 클래스나 메서드에 추가하면 쉽게 트랜잭션 개념을 적용할 수 있음
- 메서드가 호출되면, 해당 메서드 내에서 수행되는 모든 DB 연산 내용은 하나의 트랜잭션으로 묶임
- 이때, 해당 메서드가 정상적으로 수행되면 트랜잭션을 커밋하고, 예외가 발생하면 롤백함
- 클래스에 선언한 @Transactional은 해당 클래스 내부의 모든 메서드에 트랜잭션 기능을 부여함
- 이때, save 메서드는 @Transactional 애너테이션이 추가되어있기 때문에 readOnly = true 옵션인 @Transactional을 덮어쓰게 되어 readOnly = false 옵션으로 적용됨
- readOnly = true 옵션
- 트랜잭션에서 데이터를 읽기만 할 때 사용됨
- 이 속성을 사용하면 읽기 작업에 대한 최적화를 수행할 수 있음
- 만약, 해당 트랜잭션에서 데이터를 수정하려고 하면 예외가 발생하기 때문에 주의해야함
- readOnly = true 옵션
영속성 컨텍스트와 트랜잭션의 생명주기
- 스프링 컨테이너 환경에서는 영속성 컨텍스트와 트랜잭션의 생명주기가 일치함
- 트랜잭션이 유지되는 동안은 영속성 컨텍스트도 계속 유지가 되기 때문에 영속성 컨텍스트의 기능을 사용할 수 있음
'🗂️ Study > 🌿Spring' 카테고리의 다른 글
인증(Authentication) & 인가(Authorization) (0) | 2024.05.28 |
---|---|
Query Methods (0) | 2024.05.21 |
Entity (0) | 2024.05.21 |
JPA (0) | 2024.05.21 |
IoC Container & Bean (0) | 2024.05.21 |