JPA에서 가장 중요한 2가지!
1. 객체와 관계형 데이터베이스 매핑하기
2. 영속성 컨텍스트
영속성 컨텍스트가 뭔데?
컨텍스트니까 환경, 문맥 뭐 이런 뜻인가?
영속성 컨텍스트는 "엔티티를 영구 저장하는 환경"이라는 뜻이다.
JPA는 EntityManager.persist(entity);를 통하여 db에 저장하는 게 아닌 영속성 컨텍스트에 저장한다.
내가 이해한 방식대로 쉽게 설명하자면
영속성 컨텍스트는 쿼리 창고이다.
엔티티 매니저는 쿼리 창고를 관리하는 관리자라고 생각하면 쉽다.
쿼리 창고를 관리하는 관리자는 늘 한 명이다. 1:1 관계
엔티티의 생명주기
- 비영속(new/transient)
영속성 컨텍스트와 전혀 관계가 없는 새로운 상태 - 영속(managed)
영속성 컨텍스트에 관리되는 상태 - 준영속(detached)
영속성 컨텍스트에 저장되었다가 분리된 상태 - 삭제(removed)
삭제된 상태
비영속
//비영속
Member member = new Member();
member.setId("member1");
member.setName("회원1");
그냥 우리가 아는 객체를 생각하면 된다.
member는 영속 컨텍스트와 연관이 없다.
영속
//비영속상태
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
//영속상태가 된다.
em.persist(member);
영속 상태가 된다는 것은 영속 컨텍스트에 의해 관리된다는 뜻이다.
즉 쿼리 창고에 들어가서 관리자가 관리를 해준다.
관리자가 뭘 어찌 관리를 해준다는 걸까?
em.persist(member)에 의하여 new로 만들어진 멤버의 insert쿼리가 만들어진다.
하지만 바로 쿼리를 날리지 않고 쿼리 창고에 대기한다.
그리고 트랜잭션을 커밋하는 순간 쿼리창고에 있는 쿼리들을 모두 날린다.
커밋을 하면 관리자와 쿼리 창고는 사라진다...
준영속, 삭제
em.detach(member);
em.remove(member);
쿼리 창고에 잡혀있던 멤버가 탈출하여 준영속 상태가 된다.
그 후 remove로 삭제된다.
영속성 컨텍스트의 이점
- 1차 캐시
- 동일성(identity) 보장
- 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
- 변경 감지(Dirty Checking)
- 지연 로딩(Lazy Loading)
엔티티 조회, 1차 캐시

영속성 컨텍스트는 엔티티를 조회하기 전에 1차 캐시 먼저 뒤져본다.
1차 캐시에 있다면 1차 캐시를 반환하고 없다면 DB에서 조회해온다.
캐시 기능으로 아주 살짝 성능이 향상될지도 모른다.
왜 모르냐면 엔티티 매니저는 트랜잭션 단위로 생성/제거가 이뤄지기 때문에 1차 캐시가 살아있어서 유용하게 쓰일 시간이 별로 길지 않다.
영속 엔티티의 동일성 보장
Member a = em.find(Member.class,"member1");
Member b = em.find(Member.class,"member1");
System.out.println(a == b); //true
1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공한다.
엔티티 등록 ( 트랜잭션을 지원하는 쓰기 지연)
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit();//[트랜잭션] 커밋
엔티티 매니저는 데이터 변경 시 트랜잭션을 시작해야 한다.
memberA와 memberB의 쿼리는 쿼리 창고에 보내지고 정보가 1차 캐시에 저장된다.
커밋하는 시점에 Insert쿼리가 날아간다.
엔티티 수정 (변경 감지)
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(); // [트랜잭션] 커밋
처음에 만든 memberA의 이름을 hi로 바꾸었다.
쿼리 창고에 있는 쿼리를 수정해줘야 하는 거 아닐까??... 땡!!!!
업데이트를 해줘야 하는 것은 맞다. 하지만 그것은 관리자의 몫이다.
1차 캐시에는 회원의 pk값인 아이디와 엔티티가 저장되어 있다.
처음 영속 상태가 되었을 때 들어갔을 텐데 각 값들은 스냅샷을 가지고 있다.
디비에서 값을 읽어온 시점의 값을 저장해 두었다가 바뀐 부분이 있으면 귀신같이 알아채 업데이트 쿼리를 쿼리 창고에 만들어둔다.

플러시 flush
위 그림에서 작게 보이는 flush..
flush는 밀어내기와 같다.
영속성 컨텍스트의 변경사항을 데이터베이스에 반영한다.
즉, 쿼리 창고의 쿼리들을 디비에게 날린다.
플러시 발생
- 변경 감지
- 수정된 엔티티 쓰기 지연 SQL 저장소에 등록
- 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송
참고로 플러시를 한다고 해서 1차 캐시가 지워지진 않는다.
오직 쓰기 지연 sql 저장소에 있는 쿼리들이 반영될 뿐이다. (영속성 컨텍스트를 비우지는 않음)
영속성 컨텍스트를 플러시 하는 방법
1. em.flush() - 직접 호출
2. 트랜잭션 커밋 - 플러시 자동 호출
3. JPQL 쿼리 실행 - 플러시 자동 호출
플러시의 모드 옵션

준영속 상태
- 영속 상태였다가 준영속 상태로 바뀐 상태
- 영속 상태의 엔티티가 영속성 컨텍스트에서 분리(detached)된 것을 말한다.
- 영속성 컨텍스트가 제공하는 기능을 사용 못함.
준영속 상태로 만드는 방법
- em.detach(entity)
특정 엔티티만 준영속 상태로 전환 - em.clear()
영속성 컨텍스트를 완전히 초기화(통째로 지움) - em.close()
영속성 컨텍스트를 종료
'Spring' 카테고리의 다른 글
싱글톤 컨테이너 (2) | 2022.10.04 |
---|---|
스프링 컨테이너와 스프링 빈 (0) | 2022.10.03 |
1. JPA 실습 - 프로젝트 설정 및 JPA 기초 개념 (1) | 2022.09.30 |
JPA가 뭘까? (0) | 2022.09.29 |
Spring-OOP와 스프링 (1) | 2022.09.25 |