이 글은 김영한님의 '자바 ORM 표준 프로그래밍' 교재를 바탕으로 정리한 글입니다. :)
이번 글에서는 JPA의 강점 중 하나인 영속성 관리에 대해 설명해보겠습니다~!🕺💃
엔티티 매니저: 엔티티 저장, 수정, 조회 등 엔티티와 관련된 모든 일을 처리하며 데이터베이스 연결이 꼭 필요한 시점까지 커넥션을 얻지 않는다(ex: 트랜잭션 시작 시). 여러 스레드가 동시에 접근하면 동시성 문제가 발생하기에 스레드 간에 공유해선 안된다.
엔티티 매니저 팩토리: 이름 그대로 엔티티 매니저를 만드는 공장으로, 이 공장을 만드는 비용이 상당히 크다. 때문에 애플리케이션 전체에서 엔티티 매니저 팩토리 하나를 공유해서 사용한다. 스프링 부트에서는 이를 자동으로 설정해준다.
💡 영속성 컨텍스트란?
JPA를 이해하는 데 있어 가장 중요한 개념이다. '엔티티를 영구 저장하는 환경'이라는 뜻으로 엔티티 매니저로 엔티티를 저장하거나 조회할 때 영속성 컨텍스트에 엔티티가 1차적으로 보관 및 관리된다.
1차 캐시
영속성 컨텍스트는 내부에 캐시를 가지고 있는데 이것을 1차 캐시라 한다. 캐시에 저장된 엔티티는 @Id로 매핑된 식별자로 구분된다.
엔티티 조회 시 가장 먼저 메모리에 있는 1차 캐시를 검색한다. 만약 찾는 엔티티가 캐시에 없으면 엔티티 매니저는 데이터베이스를 조회해 엔티티를 생성한 후 1차 캐시에 저장한 뒤 해당 엔티티를 반환한다.
아래 예시 코드로 관련 내용을 확인할 수 있다.
Member member = new Member();
member.setId("member1");
//1차 캐시에 저장
em.persit(member);
//1차 캐시에서 조회
Member findMember1 = em.find(Member.class, "member1");
//DB에서 조회
Member findMember2 = em.find(Member.class, "member2");
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); //true
필드가 많거나 저장되는 내용이 너무 큰 경우에는 하이버네이트 확장 기능(@DynamicUpdate)를 사용하면 되지만, 이를 사용하면 캐시된 쿼리를 사용하지 않고 새로운 쿼리를 생성해 오버헤드가 발생한다고 합니다... 약 30개 정도되는 컬럼을 수정하는 경우는 되어야 기본 전략보다 빠르다고 하네요...
플러시(flush)는 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다. 실행 시 변경 감지가 동작해 수정 엔티티에 대한 쿼리를 생성하고 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송한다.
플러시 호출 방법은 아래 세 가지가 있다.
em.setFlushMode(FlushModeType.COMMIT);
준영속이란 영속성 컨텍스트가 관리하는 영속 상태의 엔티티가 영속성 컨텍스트에서 분리된 상태로, 준영속 상태의 엔티티는 영속성 컨텍스트가 제공하는 기능을 사용할 수 없다.
준영속 상태로 전환되는 경우는 아래 세가지이다.
준영속이나 비영속 상태의 엔티티를 영속 상태로 변경하기 위해 병합 함수인 merge()를 사용한다. 병합 함수는 파라미터로 엔티티를 받아 식별자로 영속성 컨텍스트와 데이터베이스를 조회한 후 찾는 엔티티가 없으면 받아온 엔티티 정보로 새로운 영속 상태의 엔티티를 생성해 반환한다.
public static void main(String args[]){
Member member = createMember("memberA", "userA");
member = mergeMember(member);
}
public Member createMember(String id, String name){
EntityManager em;
//회원 생성
Memeber member = new Member();
member.setId(id);
member.setName(name);
em.persist(member);
//영속성 컨텍스트 종료
em.close();
}
public Member mergeMember(Member member){
EntityManager em;
//병합 엔티티 반환
return em.merge(member);
}
읽어주셔서 감사합니다. 🐥