개요
- JPA가 제공하는 기능은 크게 엔티티와 테이블을 매핑하는 설계부분과 매핑한 엔티티를 실제 사용하는 부분으로 나눌 수 있다.
- 3장 영속성 관리에서는 매핑한 엔티티를 엔티티 매니저를 통해 어떻게 사용되는지 보여준다.
- 개발자 입장에서 엔티티 매니저는 엔티티를 저장하는 가상의 데이터베이스로 생각하면 된다.
엔티티 매니저 팩토리와 엔티티 매니저
- 데이터 베이스를 하나만 사용하는 애플리케이션은 일반적으로 EntityMangerFactory를 하나만 생성한다.
- 엔티티 매니저 팩토리는 여러 스레드가 동시에 접근해도 안전하므로 서로 다른 스레드 간에 공유해도 되지만, 엔티티 매니저는 여러 스레드가 동시에 접근하면 동시성 문제가 발생하므로 스레드 간에 절대 공유하면 안 된다.
영속성 콘텍스트란?
- JPA를 이해하는데 가장 중요한 용어는 영속성 콘텍스트(persistence context)다.
- 엔티티 매니저로 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 콘텍스트에 엔티티를 보관하고 관리한다.
em.persist(member);
- 정확히 얘기하면 위 코드는 엔티티 매니저를 사용해서 멤버 엔티티를 영속성 콘텍스트에 저장한다.
- 영속성 콘텍스트는 엔티티 매니저를 생성할 때 하나 만들어진다.
- 그리고 엔티티 매니저를 통해서 영속성 콘텍스트에 접근할 수 있고, 영속성 콘텍스트를 관리할 수 있다.
엔티티의 생명주기
- 엔티티에는 4가지 상태가 존재한다.
- 비영속(new/transient): 영속성 콘텍스트와 전혀 관계가 없는 상태
- 영속(managed): 영속성 콘텍스트에 저장된 상태
- 준영속(detached): 영속성 콘텍스트에 저장되었다가 분리된 상태
- 삭제(removed): 삭제된 상태
비영속
엔티티 객체를 생성했다. 지금은 순수한 객체 상태이다. 따라서 영속성 콘텍스트나 데이터베이스와 전혀 관련이 없다.
Member member = new Member();
member.setId("member1");
member.setUserName("회원1");
영속
엔티티 매니저를 통해서 영속성 콘텍스트에 저장했다. 이렇게 영속성 콘텍스트가 관리하는 엔티티를 영속 상태라 한다.
// 객체를 저장한 상태(영속)
em.persist(member);
준영속
영속성 콘텍스트가 관리하던 영속 상태의 엔티티를 영속성 콘텍스트가 관리하지 않으면 준영속 상태가 된다.
em.detach를 호출하면 된다.
// 회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member);
삭제
엔티티를 영속성 콘텍스트와 데이터베이스에서 삭제한다.
// 객체를 삭제한 상태
em.remove(member);
영속성 콘텍스트의 특징
- 영속성 콘텍스트와 식별자 값영속성 콘텍스트는 엔티티를 식별자 값(@Id로 테이블의 기본키와 매핑한 값)으로 구분한다. 따라서 영속 상태는 식별자 값이 반드시 있어야 한다.
- 영속성 콘텍스트와 데이터베이스 저장JPA는 보통 트랜잭션을 커밋하는 순간 영속성 콘텍스트에 새로 저장된 엔티티를 데이터베이스에 반영하는데 이것을 플러시 라 한다.
- 영속성 콘텍스트의 장점
- 1차 캐시
- 동일성 보장
- 트랜잭션을 지원하는 쓰기 지연
- 변경 감지
- 지연 로딩
1차 캐시
- 영속성 콘텍스트는 내부에 캐시를 가지고 있는데 이것을 1차 캐시라 한다. 영속 상태의 엔티티는 모두 이곳에 저장된다.
이 캐시의 구조는 @Id를 key로 갖고 엔티티 인스턴스를 value로 갖는 map이라고 보면 된다.
// 엔티티를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
// 엔티티를 영속
em.persist(member);
영속성 콘텍스트에서 데이터를 저장하고 조회하는 기준은 모두 데이터베이스의 기본키 값이다.
Member member = em.find(Member.class, "member1");
- 1차 캐시에서 조회em.find()를 호출하면 먼저 1차 캐시에서 엔티티를 찾고 만약 찾는 엔티티가 1차 캐시에 없으면 데이터베이스에서 조회한다.
- 데이터베이스에서 조회만약 em.find()를 호출했는데 엔티티가 1차 캐시에 없으면 엔티티 매니저는 데이터베이스를 조회해서 엔티티를 생성한다.
- em.find(Member.class, "member2)를 실행한다.
- member2가 1차 캐시에 없으므로 데이터베이스에서 조회한다.
- 조회한 데이터로 member2 엔티티를 생성해서 1차 캐시에 저장한다.(영속 상태)
- 조회한 엔티티를 반환한다.이제 member1, member2 엔티티 인스턴스는 1차 캐시에 있다. 이 엔티티들 조회하면 메모리에 있는 1차 캐시에서 바로 불러온다. 따라서 성능상 이점을 누릴 수 있다.
동일성 보장
Member a = em.find(Member.class, "member1");
Member b = em.find(member.class, "member1");
System.out.println(a == b); // 동일성 비교
em.find(Member.class, "member1")를 반복해서 호출해도 영속성 콘텍스트는 1차 캐시에 있는 같은 엔티티 인스턴스를 반환한다.
따라서 둘은 같은 인스턴고 그 결과는 참이다.
트랜잭션을 지원하는 쓰기 지연
엔티티 매니저는 트랜잭션을 커밋하기 직전까지 데이터베이스에 엔티티를 저장하지 않고 내부 쿼리 저장소에 INSERT SQL을 차곡차곡 모아둔다.
그리고 트랜잭션을 커밋할 때 모아둔 쿼리를 데이터베이스 보내는데 이것을 트랜잭션을 지원하는 쓰기 지연이라고 한다.
em.persist(memberA);
em.persist(memberB);
// 여기까지 INSERT SQL을 데이터베이스 보내지 않는다.
// 커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit();
- 마지막에 트랜잭션을 커밋을 하게 된다.
- 커밋을 하면 엔티티 매니저는 우선 영속성 콘텍스트를 플러시 한다.
- 플러시는 영속성 콘텍스트의 변경 내용을 데이터베이스에 동기화하는 작업인데 이때 등록, 수정, 삭제한 엔티티를 데이터베이스에 반영한다.
- 다시 말하면 플러시는 쓰기 지연 SQL 저장소에 모인 쿼리를 데이터베이스에 보내는 것이다.
- 영속성 콘텍스트의 변경 내용을 데이터베이스에 동기화한 후에 실제 데이터베이스 트랜잭션을 커밋한다.
- 쓰기 지연을 잘 활용하면 등록 쿼리를 모아뒀다가 한 번에 데이터베이스에 전달해서 성능을 최적화할 수 있다.
변경 감지
- JPA에는 엔티티 수정에 필요한 update() 문이 존재하지 않는다. 엔티티의 데이터만 변경하면 데이터베이스에 자동으로 반영된다. 이것을 변경 감지라고 한다.
- JPA는 엔티티를 영속성 콘텍스트에 보관할 때, 최초 상태를 복사해서 저장해두는데 이것을 스냅숏이라 한다.
- 트랜잭션을 커밋하면 엔티티 매니저 내부에서 먼저 플러시가 호출된다.
- 엔티티와 스냅숏을 비교해서 변경된 엔티티를 찾는다.
- 변경된 엔티티가 있으면 수정 쿼리를 생성해서 쓰기 지연 SQL 저장소에 보낸다.
- 쓰기 지연 저장소의 SQL을 데이터베이스에 보낸다.
- 데이터베이스 트랜잭션을 커밋한다.변경 감지는 영속성 콘텍스트가 관리하는 영속 상태의 엔티티에만 적용된다.
변경 감지를 통해 만들어지는 JPA의 업데이트 구문은 엔티티의 모든 필드를 업데이트한다.
플러시
- 플러시는 앞에서 얘기했듯이 영속성 콘텍스트의 변경 내용을 데이터베이스 반영한다.플러시를 실행하면 다음과 같은 일이 일어난다.
- 변경 감지가 동작해서 영속성 콘텍스트에 있는 모든 엔티티를 스냅과 비교해서 수정된 엔티티를 찾는다.
- 수정된 엔티티는 수정 쿼리를 만들어 쓰기 지연 SQL 저장소에 등록한다.
- 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송한다.(등록, 수정, 삭제 쿼리)영속성 콘텍스트를 플러시 하는 3가지 방법
- em.flush()를 직접 호출한다.
- 트랜잭션 커밋 시 플러시가 자동 호출된다.
- JPQL 쿼리 실행 시 플러시가 자동 호출된다.플러시 모드 옵션
- FlushModeType.AUTO : 커밋이나 쿼리를 실행할 때 플러시(기본값)
- FlushModeType.COMMIT : 커밋할 때만 플러시
준영속
- 준영속 상태의 엔티티는 영속성 콘텍스트가 관리하지 않으므로 영속성 콘텍스트가 제공하는 어떠한 기능도 동작하지 않는다.
- 준영속 상태로 만드는 방법 3가지
- em.detach(entity): 특정 엔티티만 준영속 상태로 전환한다.
- em.clear(): 영속성 콘텍스트를 완전히 초기화한다.
- em.close(): 영속성 콘텍스트를 종료한다.
정리
- 엔티티 매니저는 엔티티 매니저 팩토리에서 생성된다.
- 엔티티 매너지가 만들어지면 그 내부에 영속성 콘텍스트도 함께 만들어진다.
- 영속성 콘텍스트를 가상의 데이터베이스로 생각해도 무방하며, 영속성 콘텍스트 덕분에, 1차 캐시, 동일성 보장, 트랜잭션을 지원하는 쓰기 지연, 변경 감지, 지연 로딩 기능을 사용할 수 있다.
- 영속성 콘텍스트에 저장된 엔티티는 플러시 시점에 데이터베이스에 반영되는데 일반적으로 커밋 시점으로 보면 된다.
반응형
'프로그래밍 > JPA' 카테고리의 다른 글
05장. 연관관계 매핑기초 (4) | 2022.01.22 |
---|---|
04장. Entity Mapping (6) | 2022.01.18 |
02. JPA 시작 (10) | 2022.01.11 |
01. JPA 소개 (4) | 2022.01.08 |
댓글