반응형
영속성 컨텍스트
- 엔티티를 영구 저장하는 환경을 의미한다.
- 논리적인 개념이며 눈에 보이지 않는다.
- 엔티티 매니저를 통해서 영속성 컨텍스트에 접근할 수 있다.
- 예) EntityManager.persist(entity); 엔티티를 영속성 컨텍스트에 저장한다는 의미
엔티티의 생명주기
- 비영속(new/transient)
- 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
- 영속(managed)
- 영속성 컨텍스트에 관리되는 상태
- 1차 캐시에 올라간 상태
- 준영속(detached)
- 영속성 컨텍스트에 저장되었다가 분리된 상태
- 영속성 컨텍스트가 제공하는 기능을 사용하지 못한다.
- 삭제(removed)
- 삭제된 상태
- 예) 엔티티 생명주기
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//객체를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
//객체를 저장한 상태(영속)
em.persist(member); //특정 엔티티를 영속 상태로 전환
em.find(Member.class, "member1"); //엔티티 조회 시, 1차 캐시에 없으면 DB에서 조회 후 영속 상태로 관리
//엔티티를 영속성 컨텍스트에서 분리(준영속)
em.detach(member); // 특정 엔티티만 준영속 상태로 전환
em.clear(); //영속성 컨텍스트를 완전히 초기화
em.close(); //영속성 컨텍스트를 종료
//객체를 삭제한 상태(삭제)
em.remove(member);
tx.commit();
영속성 컨텍스트의 이점
- 1차 캐시
- 동일성(identity) 보장
- 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
- 변경 감지(Dirty Checking)
- 지연 로딩(Lazy Loading)
1차 캐시
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//객체를 생성한 상태(비영속)
Member member = new Member(); //Entity
member.setId("member1"); //@Id
member.setUsername("회원1");
//객체를 저장한 상태(영속)
em.persist(member); //1차 캐시에 저장됨
Member findMember = em.find(Member.class, "member1"); //1차 캐시에서 조회
Member findMember2 = em.find(Member.class, "member2"); //1차 캐시에 없는 상태, DB 조회 후 1차 캐시에 저장.
- 1차 캐시에서 바로 조회할 수 있다.
- 엔티티 조회 시, 먼저 영속 컨텍스트의 1차 캐시에서 일치하는 것이 있는지 조회한다.
- 1차 캐시에서 찾으면 그것을 조회 결과로 사용하고 별도의 DB 쿼리를 요청하지 않는다.
Member findMember = em.find(Member.class, "member1");
- 1차 캐시에서 못찾으면 DB를 통해 조회하고 그 결과를 1차 캐시에 저장한 후, 조회의 결과로 사용한다.
Member findMember2 = em.find(Member.class, "member2");
- 보통
Entitymanager
는 데이터베이스 트랜잭션 단위로 만들고 1회성으로 사용된다.(쓰레드에 안전하지 않다.) 생명주기가 짧기 때문에 1차 캐시의 이점을 크게 누리지 못한다.
- 애플리케이션 전체에서 공유하는 캐시는 2차 캐시라고 한다.
영속 엔티티의 동일성을 보장한다.
- 1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공한다.
Member member1 = em.find(Member.class, 103L);
Member member2 = em.find(Member.class, 103L);
System.out.println("result = " + (member1==member2)); // 동일성 비교 true
엔티티 등록 시, 트랜잭션을 지원하는 쓰기 지연(transactional write-behind) 가능
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // 트랜잭션 시작
Member memberA = new Member("memberA", "A");
Member memberB = new Member("memberB", "B");
em.persist(memberA);
em.persist(memberB);
transaction.commit(); // 트랜잭션 커밋
//커밋하는 순간 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 보낸다.(flush)
//그 후 실제 데이터베이스의 트랜잭션이 commit 된다.
- 쓰기 지연 SQL 저장소와 같은 중간 계층이 생김으로써 추가적인 성능 개선 포인트가 생겼다.(버퍼링)
- 만약, DB에 쿼리가 바로바로 전달되는 상황이라면 버퍼링과 같은 처리를 하기에는 매우 힘들 것이다.
- 배치 처리가 가능하다.
- 컴퓨터에서는 큰 배열을 한꺼번에 계산하는 것이 분할된 작은 배열을 여러 번 계산하는 것보다 빠르다.
- 한 번의 네트워크 통신으로 여러 개의 쿼리를 동시에 보내서 처리할 수 있다.
엔티티 수정 시, 변경 감지(Dirty Checking)할 수 있다.
- 트랜잭션이 커밋되는 시점에 flush가 발생한다.
- flush가 발생하면 우선 엔티티와 스냅샷을 비교한다.
- 최초로 읽어온 시점(최초로 영속성 컨텍스트 1차 캐시에 들어온 상태)의 엔티티를 스냅샷에 저장한다.
- 엔티티와 스냅샷을 비교하여 변경된 부분이 있다면 쓰기 지연 SQL 저장소에 update 쿼리를 만들어 저장한다.(변경 감지)
- 그리고 쓰기 지연 SQL 저장소에 있는 update 쿼리가 실제 db에 적용된다.
- 엔티티 삭제도 마찬가지로 위의 메커니즘 대로 동작한다.
플러시
- 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화한다.
- 플러시는 영속성 컨텍스트를 비우지 않는다. 즉, 플러시가 발생해도 1차 캐시의 내용은 그대로 유지된다.
- 플러시 메커니즘이 가능한 이유는 트랜잭션이라는 논리적인 작업 단위가 존재하기 때문이다.
- 트랜잭션이 커밋되기 직전에만 변경사항을 보내면 된다.
플러시 발생 시, 수행하는 동작
- 변경 감지
- 수정된 엔티티를 쓰기 지연 SQL 저장소에 등록
- 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송(등록, 수정, 삭제 쿼리)
영속성 컨텍스트에서 플러시가 수행되는 경우
em.flush()
- 직접 호출
트랜잭션 커밋
- 플러시 자동 호출
JPQL 쿼리를 실행
- 플러시 자동 호출
플러시 모드 옵션
- 기본 값을 변경하여 사용할 일은 거의 없다.
em.setFlushMode(FlushModeType.COMMIT)
FlushModeType.AUTO
- 커밋이나 쿼리를 실행할 때 플러시(기본 값)
FlushModeType.COMMIT
- 커밋할 때만 플러시
[참고자료]
반응형
'Java > ORM' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍] 영속성 전이 (0) | 2022.04.19 |
---|---|
[자바 ORM 표준 JPA 프로그래밍] 프록시와 연관관계 관리 (0) | 2022.04.15 |
[자바 ORM 표준 JPA 프로그래밍] 상속관계 매핑 (0) | 2022.04.06 |
[자바 ORM 표준 JPA 프로그래밍] 연관관계 매핑 (0) | 2022.04.03 |
[자바 ORM 표준 JPA 프로그래밍] 엔티티 매핑 (0) | 2022.03.31 |