영속성 컨텍스트의 이점
1) 1차 캐시
2) 동일성(identity) 보장
3) 트랜잭션을 지원하는 쓰기 지연(transcational write-behind)
4) 변경감지(Dirty Checking)
5) 지연 로딩(Lazy Loading)
1. 1차 캐시(엔티티 조회)
Member member = new Member();
member.setId("member1");
member.setNAme("memberA");
em.persist(member);
Member findMember = em.find(Member.class, "member1");
Member findMember2 = em.find(Member.class, "member2");
위의 findMember는 member 객체가 영속 상태이기 때문에 DB에 접근하지 않고도 데이터를 캐시에서 가져올 수 있다.
하지만 findMember2는 비영속 상태이기 때문에 DB에서 직접 데이터를 가져와야 한다.
단, 1차 캐시는 데이터베이스 한 트랜잭션 안에서만 효과가 있기 때문에 큰 성능 이점은 가지기 어렵다.
그래도 비지니스 로직이 굉장히 복잡한 경우에는 도움된다.(성능보단 컨셉이 주는 이점이 현업에서 있다.)
Member findMember = em.find(Member.class, "member2");
Member findMember2 = em.find(Member.class, "member2");
member2 라는 키를 가진 Member 객체가 DB에 존재할 때,
처음 find는 DB에 직접 조회를 하지만 두번째 find는 이미 한번 조회를 통해 영속성 컨텍스트가 관리하고 있기 때문에
DB를 조회하지 않고 1차 캐시에서 값을 가져온다.
2. 영속 엔티티의 동일성 보장
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a=b); // true 출력
JPA는 영속 엔티티의 동일성을 보장해준다.
마치 자바 컬렉션에서 같은 레퍼런스의 객체를 비교할 때 true가 나오는 것과 같다.
이게 가능한 이유는 같은 트랜잭션 내에서 영속성 컨텍스트가 1차 캐시에 있는 데이터를 가져갈 때,
반복 가능한 읽기 등급의 트랜잭션 격리 수준을 DB가 아닌 애플리케이션 차원에서 제공하기 때문이다.
(이는 후에 더 자세히 설명하겠다.)
3. 트랜잭션을 지원하는 쓰기 지연(엔티티 등록)
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경 시 트랜잭션을 반드시 시작해야 함
trasaction.begin();
em.persist(memberA);
em.persist(memberB);
//아직 INSERT 쿼리를 DB에 보내지 않음
transaction.commit();
//커밋하면서 쿼리를 DB로 보냄
persist할 때마다 쿼리를 DB로 보내지 않고 1차 캐시엔 엔티티를, 쓰기 지연 SQL 저장소엔 INSERT 쿼리를 쌓아둔다.
그러다가 commit을 시행시키면 JPA가 flush(INSERT 쿼리를 DB로 보냄)한다.(remove도 commit시점에 내보냄)
이 지연 쓰기를 통해 얻을 수 있는 이점이 있다.
버퍼링과 같은 기능으로, 쿼리문들을 하나로 묶어서 DB로 한 번에 보낼 수 있어 성능이 향상된다.
(Mybatis보다 이런점 좋음)
추가정보. JPA는 내부적으로 reflection을 쓰기 때문에 동적으로 객체를 생성해야 할 때가 많다.
따라서 반드시 기본 생성자가 있어야한다.(없으면 오류 발생)
4. 변경 감지(엔티티 수정)
EntityManager em - emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();
Member memberA = em.find(Member.class, "memberA");
memberA.setUsername("hi");
memberA.setAge(10);
transaction.commit();
위 코드는 find를 통해 영속 엔티티를 조회한 뒤, 해당 객체를 수정하였다.
하지만 persist나 update같은 별도의 명령을 주지 않은 상태이다.
그런데 신기하게 commit과 동시에 update문이 자동으로 실행된다.
이유는 JPA가 엔티티의 값이 변경되는 것을 감지(Dirty Checking)하기 때문이다.
1차 캐시에는 키값인 @Id, Entity, 스냅샷이 저장되어 있다.여기서 스냅샷이란 맨 처음 영속성 컨텍스트에 들어왔을 때의 상태로JPA는 트랜잭션이 commit되는 시점에 Entity랑 스냅샷을 비교한다.그런데 둘이 다른 상태이면 이 변화를 감지하여 쓰기 지연 SQL 저장소에 저장시키고,commit이 되어 DB에 저장되는 것이다.
5. 지연로딩
이건 나중에 공부해보겠다.
'JPA' 카테고리의 다른 글
[JPA] 필드와 컬럼 매핑 (0) | 2023.07.15 |
---|---|
[JPA] 객체와 테이블 매핑 (0) | 2023.07.15 |
[JPA] 준영속 상태 (0) | 2023.07.13 |
[JPA] 플러시 (0) | 2023.07.13 |
[JPA] 영속성 컨텍스트 (0) | 2023.07.13 |