Post

JPA 영속성 컨텍스트

JPA를 쓰는 이유

OOP 와 RDB 사이의 간극을 극복하기위해 사용.

영속성 컨텍스트 (Entity Manager)

x509

  • 영속성 컨텍스트는 EntityManager 또는 1차 캐시를 포함하고 있다.
  • Entity Manager는 사용자 요청마다 생성된다. 즉 ThreadLocal에 생성되어 쓰레드간 공유를 차단한다.

데이터 조회

JPA에서 조회를 하면 바로 DB에 조회를 하는것이 아닌 1차캐시에 데이터가 있는지 조회를 한다. 그리고 1차캐시에 데이터가 존재하지 않으면 DB에 조회 쿼리를 요청한다.

x609

  1. 1차캐시 조회
  2. 1차캐시에 데이터 존재할경우 바로 반환
  3. 데이터 존재하지 않을경우 DB에 조회 요청
  4. DB로 부터 가져온 데이터를 1차캐시에 업로드
  5. 1차캐시에서 데이터 조회

데이터 INSERT

  • DB에 네트워크를 사용하여 쿼리를 전송하는것은 상당한 비용이 발생한다. 따라서 DB에 쿼리를전송하기전 가장 최적의 데이터 상태를 유지하고 트랜잭션이 종료될때 쿼리를 DB에 전송한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class JpaBookMain {
  public static void main(String[] args) {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
    EntityManager em = emf.createEntityManager();
    EntityTransaction tx = em.getTransaction();
    tx.begin();

    try {
      Member member = new Member();
      member.setUsername("Tom");
      em.persist(member);
      // 여기까지 1차 캐시에 보관만 하고 INSERT 쿼리를 DB에 전송하지 않는다. 
      
      tx.commit(); // 커밋 하는 순간 INSERT 쿼리를 DB에 전송한다.
    } catch (Exception e) {
      e.printStackTrace();
      tx.rollback();
    } finally {
      em.close();
      emf.close();
    }
  }
}

데이터 변경

변경감지 (Dirty Checking)

  • 아래의 코드에서 findMember를 다시 persist 하거나, update하는 코드는 찾아볼 수 없다. 그 이유는 바로 트랜잭션 종료시점에 EntityManager의 스냅샷(DB 에서 읽어온 최초의 상태)과 비교하여 변경이 일어났다면 하이버네이트가 자동으로 update 쿼리를 생성하여 DB에 전송한다. x609
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class Main {
  public static void main(String[] args) {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("config");
    EntityManager em = emf.createEntityManager();
    EntityTransaction tx = em.getTransaction();

    tx.begin(); // 트랜잭션 시작 #########

    try {

      Member member = new Member();
      member.setUsername("Tom");
      em.persist(member);
          
      // EntityManager 초기화 (강제로 DB로 쿼리 전송)    
      em.flush();
      em.close();

      // DB로 부터 데이터 조회
      Member findMember = em.find(Member.class, member.getId()); 
			// 데이터 수정
      findMember.setUsername("Kyle");
      
      // 트랜잭션 종료 #########
      // EntityManager의 스냅샷과 비교하여 변경사항이 있으면 Update 쿼리 자동 생성
      tx.commit();
      
    } catch (Exception e) {
      e.printStackTrace();
      tx.rollback();
    } finally {
      em.close();
      emf.close();
    }
  }
}

병합 (merge)

x609

  • jpa 에서 데이터를 수정하는 방법으로 merge가 있다. 이 방식은 객체를 영속화하기는 하지만 변경하고자하는 값으로 덮어씌우는 방식이라 사용할떄 주의가 필요하다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Entity
class Item {
  @Id @GeneratedValue
  private Long id;
  private String name;
  private int price;
  private int stockQuantity;
}

@Repository
@RequiredArgsConstructor
class ItemRepository {
  private final EntityManager em;
  
  public void saveWithMerge(Item item) {
    if (item.getId() == null) {
      em.persist(item);
    } else {
      em.merge(item);       // itemId 값이 null 이 아닐때(이미 insert된적 있는 객채) 
                            // DB에서 가져온 영속화된 객체에 item 객체의 필드를 덮어씌워서 영속화 진행
    }
  }
  
  public void saveWithDirtyChecking(Item item) {
    Item findItem = em.find(Item.class, id);    // 1차캐시 또는 DB로 부터 영속화된 객체 생성
    findItem.update(item);                      // 영속화된 객체에서 필요한 부분만 item을 통해 수정
                                                // transaction commit 시점에 DB로 update 쿼리 생성
  }
}
  • 예를들어 위의 코드에서 price가 null이라면 영속화된 객체 item 의 필드값 price에 null로 덮어쓰게 된다. 따라서 이 방식 보다는 변경감지(Dirty checking) 방식을 사용하자





자바 ORM 표준 JPA 프로그래밍 - 기본편
hibernate.org

This post is licensed under CC BY 4.0 by the author.