JPA에서 제일 중요하다고 생각되는 주제중 하나이다. 실제 JPA가 동작하는것과 관련이 있고 그래서 강의를 들으면서 개념을 확실하게 정리해본다.
영속성 컨텍스트(PersistenceContext)란?
영속성 컨텍스트는 엔티티를 영구 저장하는 환경이라는 뜻이다. 영속성 컨텍스트는 애플리케이션과 DB 사이에서 객체를 보관하는 가상의 DB 역할을 한다. 엔티티 매니저(EntityManager)를 통해 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리하게 된다.
엔티티 생명주기
- 비영속 (new/transient) : 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
- 영속 (managed) : 영속성 컨텍스트에 관리되는 상태
- 준영속 (detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태
- 삭제 (removed) : 삭제된 상태
// 객체를 생성한 상태 (비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
EntityManager em = emf.createEntityManager();
em.geTransaction().begin();
// 객체를 저장한 상태 (영속)
em.persist(member);
// 회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member);
// 객체를 삭제한 상태 (삭제)
em.remove(member);
영속성 컨텍스트의 이점
1. Entity 조회, 1차 캐시
- 영속성 내부에는 1차 캐시가 존재한다. 영속 상태의 엔티티를 이곳에 저장하기 때문에 만약 엔티티를 조회했을 때 1차 캐시에 엔티티가 존재한다면 DB를 찾아보지 않아도 된다. 말 그대로 캐시로써의 기능과 장점을 가지고 있다.
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
// 1차 캐시에 저장됨
em.persist(member);
// 1차 캐시에서 조회
Member findMember = em.find(Member.class, "member1");
// 데이터베이스에서 조회
Member findMember2 = em.find(Member.class, "member2");
2. 영속 엔티티의 동일성 보장
1차 캐시로 반복 가능한 읽기(Repeatable read) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공해준다.
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); // 동일성 비교 true
3. 엔티티 등록 트랜잭션을 지원하는 쓰기 지연
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
// 엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋
em.persist()로 객체를 영속성 컨텍스트에 저장하고 sql쿼리를 모아둔다. (DB에 insert 쿼리를 바로 날리지 않는다.)
모아둔 sql 쿼리들은 flush 될 때(영속성 컨텍스트의 변경내용을 DB에 반영) 한번에 날리게 된다.
이를 쓰기 지연이라고 한다.
4. 엔티티 수정 - 변경 감지
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] 시작
// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
//em.update(member) 이런 코드가 있어야 하지 않을까?
transaction.commit(); // [트랜잭션] 커밋
영속 엔티티를 조회해서 데이터를 수정한 후 em.update(member)와 같은 업데이트를 하는 코드가 필요하다고 생각할 수 있다. 하지만, 영속성 컨텍스트 내에서 스냅샷과 엔티티를 비교해 변경을 감지해서 알아서 update 쿼리를 날려준다.
* 여기서 update 쿼리도 쓰기 지연이 될 수 있다.
5. 지연 로딩
간단하게 정리하자면 지연로딩은 연관 관계 매핑이 되어있는 엔티티를 조회했을 때, 우선 프록시 객체를 저장해 두었다가 해당 데이터가 필요할 때 쿼리를 날려서 실제 데이터를 가져오는 기능이다.
프록시와 지연로딩은 더 자세하게 따로 포스팅할 예정이다.
자료 출처 : 김영한님의 [ 자바 ORM 표준 JPA 프로그래밍 - 기본편] 강의
'백엔드 개발 > Spring&JPA' 카테고리의 다른 글
[Spring Data JPA] p6spy 커스텀 포맷, 로그 파일, 로그 레벨 설정 (0) | 2024.08.29 |
---|---|
[Spring Data JPA] Spring boot 3에 p6spy 적용하기 (4) | 2024.08.28 |
[Spring Data JPA] 로그인 기능 구현(3) - JWT 적용 (0) | 2024.06.28 |
[Spring Data JPA] 로그인 기능 구현(2) - Password Encode (0) | 2024.06.25 |
[Spring Data JPA] 로그인 기능 구현 (1) | 2024.06.24 |