참조한 강의 : Spring & Hibernate For Beginners (www.udemy.com/course/spring-hibernate-tutorial/)
이번에는, 영속성 컨텍스트(Persistence Context) 와 Entity LifeCycle 이 뭔지 알아본다.
- Persistence Context
앞선 포스팅에서 Entity Class 에 대해서 언급한 적이 있었다.
Entity Class 는 DB 의 테이블과 매핑되는 클래스를 일컫는말이다.
이 Entity Class 들은 EntityManager 라는것에 의해서 관리되는데,
유저로부터 특정한 Entity 에 대한 요청이 들어올때마다,
EntityManagerFactory 라는것에서 각 Entity 에 대한 EntityManager 를 붙여준다.
EntityManager 에 의해 관리되는 각 Entity 들은, Persistence Context 라는곳에 담기게 되어
DB 와 연결되기 위한 영속성을 부여받는 작업을 받게되고, 영속성이 부여되면, Connection Pool 과 연결되어 DB 에 Transaction Commit 되게 된다.
즉, Persistence Context 는, DB 와 연결되기전 Entity 들을 담아서 영속성을 부여해서 DB 에 넣거나 혹은 DB 로 부터 테이블을 가져와서 관련된 Entity 와 매핑을 하는, Entity 를 관리하는 논리적인 어떤 Layer 라고 볼 수 있다.
Entity 들에 대한 영속성 관리를 담당하는 1차 캐시인것이다.
Persistence Context 에는 아래와 같이 두가지 타입이 존재한다
1) Transaction-scoped Persistence Context
이 영속성 컨텍스트 타입은 트랜잭션에 종속된 형태로, 트랜잭션이 종료되었을때, 엔티티들이 영속성 컨텍스트에 담기게되고, persistent storage 로 옮겨가게되는 방식이다.
영속성 컨텍스트의 기본 타입은 바로 이 Transaction-scoped Persistence Context 를 의미한다.
사용시에는 타입을 지정하지 않은 어노테이션인 @PersistenceContext 를 쓰면 된다.
@PersistenceContext
private EntityManager entityManager;
|
cs |
2) Extended-scoped Persistence Context
이 타입의 영속성 컨텍스트는, 영속성 컨텍스트내에 다수의 트랜잭션이 들어갈 수 있는 형태로, 트랜잭션 없이 엔티티에 persist 할 수 있지만, 트랜잭션이 없으면 flush 는 할 수 없는 형태이다.
이 타입을 사용할때는 @PersistenceContext 에 타입값으로 EXTENDED 를 붙이면 된다.
@PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;
|
cs |
- Entity LifeCycle
Entity 의 생명주기는 위와 같이 4가지 상태가 존재한다
1) New / Transient
: 엔티티 클래스의 인스턴스만 생성한 상태로, 영속성이 전혀 부여되지 않은 상태이다.
이 인스턴스에 save 또는 persist 를 시도하면, 해당 인스턴스가 영속성 컨텍스트에 의해서 관리되는 상태로 접어들게 된다.
2) Persistent / Managed
: 영속성 컨텍스트에 의해서 관리되는 상태로, 이 상태에서 rollback 을 해서 관리되기 이전 상태로 되돌릴 수 있고, 트랜잭션 commit 을 시도하면 detached 상태가 되고 (detached 상태는 영속성 컨텍스트에서 분리된 상태임을 의미함) DB 로 트랜잭션 커밋되게 된다.
refresh 는 DB 의 상태와 영속성 컨텍스트 간의 상태를 동기화 시키기 위해서 쓰이는 연산이다.
3) Detached
: 영속성 컨텍스트에서 분리된 상태로, 더 이상 해당 엔티티가 영속성 컨텍스트에 의해서 관리되지 않는 상태이다.
여기서 다시 영속성 컨텍스트가 관리하게 하도록 넘기려면 merge 를 사용해야 한다.
4) Removed
: 영속성 컨텍스트에서 엔티티를 삭제한 상태로, DB 에서도 해당하는 엔티티가 삭제된다.
- Persistence Context 가 왜 필요한가
1) 1차 캐시
: Cache 란 자주 쓰는 데이터를 미리 복사해둔 임시 저장소를 말한다.
캐시를 사용하는 이유는, 자주 쓰는 데이터들을 더 빠르게 접근할 수 있기 때문이다.
자주 쓰는 데이터들을 DB 에서 데이터를 조회할때 마다 DBMS 를 거쳐서 요청하는 것 보다는,
캐시에 담아뒀다가 조회하는것이 더 빠른 성능을 보여준다.
영속성 컨텍스트를 사용하는 이유 중 하나는, 바로 DB 에 가서 매칭되는 엔티티가 있는지 찾기보다는
1차 캐시에서 해당하는 엔티티가 있는지 찾아보면 굳이 DB 까지 가지 않더라도 캐시를 통해서 조회가 되기 때문이다.
2) 동일성 보장
: 영속성 컨텍스트에서 엔티티들을 관리하기 때문에, 같은 엔티티들에 대해서 여러번 요청이 들어와도 똑같은 것을 참조시켜줘서 같은 엔티티를 쓰고 있음을 보장해준다.
3) 쓰기 지연
: persist 를 사용할때 바로, SQL 을 DB 로 날리는게 아니라, 트랜잭션 작업이 모두 끝마쳐지는 commit 시점에서 한번에 날린다.
위 예시 그림에서
처음에 persist 가 발생되서, 영속성 컨텍스트에 엔티티가 담기게 되면 (1차 캐시에 저장)
영속성 컨텍스트 내부에 있는 쓰기 지연 SQL 저장소에 쿼리들을 쌓아 놓는다.
DB 에 바로 두는게 아니라, 기다린다음, 트랜잭션이 모두 끝나는 시점에 commit 을 할때, 한번에 DB 에 모든 저장된 쿼리가 들어간다.
이때 SQL 저장소에 담긴 쿼리들이 DB 에 모두 날라가게 하는 작업을 flush 라 한다.
즉 트랜잭션 commit 을 하는 시점에 flush 가 수행되서 DB 로 쿼리가 전부 이동된 뒤에, commit 이 수행되는것.
- 예시 코드 (출처 : ict-nroo.tistory.com/130)
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
// 엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // 트랜잭션 시작
em.persist(memberA);
em.persist(memberB);
// 이때까지 INSERT SQL을 데이터베이스에 보내지 않는다.
// 커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // 트랜잭션 커밋
|
cs |
4) 변경 감지 (Dirty Check)
: 엔티티에 어떤 변화가 일어나면 영속성 컨텍스트에서 Dirty 라는 마크를 부여해서 변화된 사항을 감지한다
- 예시 코드 (출처 : ict-nroo.tistory.com/130)
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // 트랜잭션 시작
// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
// 영속 엔티티 수정
memberA.setUsername("nj");
memberA.setAge(27);
//em.update(member) 또는 em.persist(member)로 다시 저장해야 하지 않을까?
transaction.commit(); // 트랜잭션 커밋
|
cs |
setter method 를 통해서 엔티티의 데이터를 바꾸더라도, 별도의 작업을 하지 않아도(update, persist 등) 변경사항이 저장되는 이유는,
영속성 컨텍스트 내부에는 스냅샷이란게 존재하는데, commit() 이나 flush() 가 일어날때, 엔티티에 저장된 값과 스냅샷을 비교해서 변경된 부분이 존재하면, UPDATE SQL 문을 이용해서 DB 와 연동시키기 때문이다.
Dirty Check 시에 변경사항을 찾아내서 UPDATE 할때, 모든 필드를 업데이트 하도록 기본설정 되어 있다.
전체 필드를 업데이트 하는대신 변경된 일부 필드만 업데이트 하려면, @DynamicUpdate 어노테이션을 사용하면 변경 된 부분만 UPDATE 할 수 있도록 설정할 수 있다.
(참조) Dirty Checking
여기까지 영속성 컨텍스트와 엔티티 생명주기에 대해서 알아봤다.
다음은, Hibernate ORM 을 통한 관계 매핑 방법에 대해 알아본다
- References)
1. Persistence Context : www.baeldung.com/jpa-hibernate-persistence-context
2. Hibernate Entity LifeCycle : www.baeldung.com/hibernate-entity-lifecycle
3. Cache : mangkyu.tistory.com/69
4. Transactional Write-Behind : ict-nroo.tistory.com/130
5. Flush : gmlwjd9405.github.io/2019/08/07/what-is-flush.html
6. Dirty Checking : jojoldu.tistory.com/415
'Spring' 카테고리의 다른 글
Spring Framework - 연관 관계 매핑 (0) | 2021.02.13 |
---|---|
Spring Framework - Eager, Lazy Loading (0) | 2021.02.11 |
Spring Framework - CRUD Using Hibernate ORM (0) | 2021.02.04 |
Spring Framework - Hibernate ORM (0) | 2021.02.02 |
Spring Framework - Validation (0) | 2021.02.01 |