본문 바로가기
스프링(부트)/스프링 내용 정리

JPA (Java Persistence API)

by doflamingo 2021. 1. 5.

JPA

JPA (Java Persistence API)란 현재 자바의 ORM 표준 인터페이스를 말한다.

JPA는 인터페이스라 기술 스펙일뿐 실제로 구현을 해야한다.

그리고 JPA를 구현한 기술이 hibernate이다.

Hibernate이외에도 DataNucleus, EclipseLink같은 다른 구현체들이 존재한다.

그렇다면 JPA에서 가장 핵심인 영속성 컨텍스트(Persistence Context)에 대해서 알아보자

영속성 컨텍스트(Persistence Context)

영속성 컨텐스트란 엔티티를 영구 저장하는 환경이라는 뜻이다. 애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할을 한다.

엔티티 매니저를 통해 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.

하나의 트랜잭션을 이루기 위해서 EntityManagerFactory에서 EntityManager를 생성해서 사용한다.

EntityManagerFactory는 애플리케이션 실행시 하나만 생성하고, 하나의 EntityManager는 하나의 트랜잭션을 이루고 삭제한다.

EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("persistence-unit-name");
//--------------------------------------------------------------
EntityManager entityManager = entityManagerFactory.createEntityManager();
Transaction transaction = entityMananger.getTransaction();
transaction.begin();
// transaction crud
transaction.close();
entityManager.close();
//--------------------------------------------------------------

영속성 컨텍스트의 구조

영속성 컨텍스트 내부는 1차 캐시와 SQL저장소로 이루어진다.

영속성 컨텍스트 구조

1차 캐시

1차 캐시는 entityManager가 persist하거나 DB에서 find할 때, 저장되고 처음에 들어올 때의 형태가 스냅샷으로 저장된다. 만약 데이터가 변경시 스냅샷은 유지되고 Entity값만 변하게 되는 것이다.

SQL 저장소

SQL저장소는 entityManager가 DB로 쓸 때 JPA가 알아서 SQL을 생성하는데 그 SQL이 저장되는 저장소이다. 이 저장소는 SQL을 모아놨다가 flush가 되면 SQL문을 실행한다.

엔티티의 Life Cycle

영속성 컨텍스트에서 관리되는 엔티티는 4가지 상태를 가진다.

  1. 비영속 : 영속성 컨텍스트와 전혀 관련 없는 상태 (ex: 새로 생성된 객체)
  2. 영속 : 영속성 컨텍스트에서 관리되는 상태 (find를 통해 DB에서 가져오거나, persist된 후 상태)
  3. 준영속 : 영속성 컨텍스트에서 관리되다 분리된 상태(detach를 통해 분리된 상태 )
  4. 삭제: 삭제된 상태(remove를 통해 삭제된 상태)

영속성 컨텍스트의 이점

영속성 컨텍스트는 아래의 이점들이 있다.

  • 1차 캐시
  • 동일성(identity) 보장
  • 트랜잭션을 지원하는 쓰기 지연 (transactional write-behind)
  • 변경 감지(Dirty Checking)
  • 지연 로딩(Lazy Loading)

1차 캐시

영속성 컨텍스트의 구조에서도 말했던 것처럼 영속성 컨텍스트에는 1차 캐시가 존재하여 Entity를 영속하면 일단 1차 캐시에 저장된다.

//엔티티를 생성한 상태(비영속)
Member member = new Member();
member.setId(1L);
member.setUsername("회원1");
//엔티티를 영속
em.persist(member);

1차 캐시에 저장된 member Entity는 transaction이 commit되면 실제 DB에 insert 된다.

조회의 경우, 1차 캐시에 해당하는 값이 존재할 경우 1차캐시에서 가져오고 그렇지 않으면 DB에서 select후 1차캐시에 저장하고 가져온다.

동일성 보장

1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공한다.

1차 캐시로 가져온 두 객체는 동일한 객체로 취급된다.

Member a = em.find(Member.class,1L);
Member b = em.find(Member.class,1L);
System.out.println(a == b);// true    

트랜잭션을 지원하는 쓰기 지연

persist가 일어나면 위에서 설명한 SQL저장소에 SQL을 자동 생성해서 저장한다.

그 후 transaction commit 혹은 flush가 일어나면 SQL문이 한꺼번에 수행된다.

EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit();

변경 감지 (Dirty Checking)

영속성 컨텍스트는 update없이도 엔티티를 수정할 수 있다.

그 이유는 위에서도 말했다시피 SQL문은 transaction commit될 때만 수행되고

그 전까지는 DB의 변환이 없는데 대신 값을 수정하면 1차 캐시에 값이 변한다.

commit이나 flush가 일어나면 1차캐시에 있는 값과 스냅샷을 비교해서 변경이 감지되면

update문을 수행하게 된다.

EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin(); //[트랜잭션] 시작
//영속 엔티티 조회 
Member memberA = entityManager.find(Memeber.class, 1L);
//영속 엔티티 데이터 수정
memberA.setUserName("changeName");
transaction.commit();

Dirty Checking

Flush란

영속성 컨텍스트의 변경을 데이터베이스에 반영하는 것을 의미한다.

flush 하는 방법

  • entityManager.flush()
  • transaction.commit()
  • JPQL 쿼리 실행

 

이 글은 김영한님의 자바 ORM 표준 JPA 프로그래밍 - 기본편을 보고 작성한 글 입니다. 

 

댓글