반응형
영속성 컨텍스트
- "엔티티를 영구 저장하는 환경"이라는 뜻 (DB의 저장이라기보다, 영속성 컨텍스트에 저장한다는 의미)
- EntityManager.persist(entity); - 엔티티 매니저를 통해 영속성 컨텍스트에 접근
JPA 에서 가장 중요한 2가지
- 객체와 관계형 데이터베이스 매핑하기 (Object Relational Mapping)
- 영속성 컨텍스트
엔티티의 생명주기
- 비영속 (new/transient) - 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
// 객체만 생성한 상태 (비영속)
Member member = new Member();
member.set~
- 영속(managed) - 영속성 컨텍스트에 관리되는 상태 - DB에 저장되는게 아니다. 저장은 commit
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(member);
- 준영속(detached) - 영속성 컨텍스트에 저장되었다가 분리된 상태
em.detach(member);
- 삭제 (removed) - 삭제된 상태
em.remove(member);
영속성 컨텍스트의 이점
1차 캐시
- 조회하려는 값이 1차 캐시에 없으면 DB에서 조회 후 1차 캐시에 등록 후 반환하고, 값이 있으면 DB에 접근 하지 않고 1차 캐시에서 바로 꺼내온다.
- 하나의 트랜잭션 안에서만 영속성 컨텍스트가 생성되고 종료되기 때문에 정말 복잡한 애플리케이션이 아니라면 큰 차이가 없다. 여러 트랜잭션을 관리하는 것은 2차 캐시이다.
동일성(identity) 보장
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); //동일성 비교 true
- 1차 캐시로 REAPEATABEL READ 등급의 트랜잭션 격리 수준을 DB가 아닌 애플리케이션 차원에서 제공한다. 마치 자바의 컬렉션에서 값을 꺼낼 때 동일함을 보장해야한다는 것을 JPA에서 제공한다.
트랜잭션을 지원하는 쓰기 지연
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
transaction.commit(); // [트랜잭션] 커밋
//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
- JPA가 커밋하기 전까지 쌓아두었다가 커밋 시에 한꺼번에 적용 (hibernate.batch size로 설정 가능)
변경 감지 (Dirty Checking)
Member memberA = em.find(Member.class, "memberA");
memberA.setUsername("hi");
memberA.setAge(10);
transaction.commit();
- update 관련 쿼리 없이 set으로 수정이 가능하다.
- 트랜잭션을 커밋하는 순간 영속성 컨텍스트에서 flush() 가 호출
- 스냅샷과 비교해서 변경사항이 있으면 SQL 생성하여 flush , commit
- 예) 값을 바꾸면 set~ 만해주면 자동으로 변경해준다.
지연 로딩 (Lazy Loading)
- 지연 로딩 : 객체가 실제 사용될 때 로딩
- 처음에 member에 데이터만 갖고왔다가 team을 쓰려고할 때 team 데이터까지 가지고온다.
플러시
- 영속성 컨텍스트의 변경내용을 데이터베이스에 반영하는 것을 의미한다. 즉, 영속성 컨텍스트를 비우는 것이 아니라 변경 내용을 디비에 동기화 하는 것이다.
- 영속성 컨텍스트를 플러쉬하는 방법 : em.flush(), 트랜잭션 커밋, JPQL 쿼리 실행
em.flush()
try {
Member member = new Member(200L, "member200");
em.persist(member);
em.flush(); // 강제 호출
System.out.println("==========================="); // === 전에 쿼리문이 나간다.
tx.commit();
} catch (Exception e){
tx.rollback();
} finally {
em.close();
}
- 직접 쓸일은 없지만, 테스트할 때 사용한다.
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
// JPQL 실행
query = em.createQuery("select m from Member m", Member.class);
List<Member> members = query.getResultList();
- JPQL 실행 문 전까지는 영속성 컨텍스트에 담겨있다. JPQL문 부터는 해당 데이터를 조회해야하므로, 이때 flush()를 사용하여 DB에 모두 동기화 하고 JPQL을 동작할 수 있도록 한다.
플러시는 영속성 컨텍스트를 비우는 것이 아닌 변경 내용을 DB에 동기화 하는 것이다. 즉, 트랙잭션이 커밋하기 직전에만 동기화 한다고 생각하자.
em.setFlushMode(FlushModeType.COMMIT)
과 같이 플러시 모드를 변경할 수도 있다.
준영속 상태
- 준영속 상태란 영속 상태의 엔티티가 영속성 컨텍스트에서 분리된 상태를 의미한다. (detached)
- 영속성 컨텍스트가 제공하는 기능을 사용하지 못한다.
- em.detach(entity) : 특정 엔티티만 준영속 상태로 전환
- em.clear() : 영속성 컨텍스트를 완전히 초기화
- em.close() : 영속성 컨텍스트를 종료
본 포스팅은 인프런 김영한님 강의(자바 ORM 표준 JPA 프로그래밍 - 기본편)를 토대로 정리한 내용입니다.
반응형