자바/JPA 프로그래밍

2. JPA 시작, 내부 구조

hooooolly 2024. 11. 3. 18:30

🔽데이터베이스 방언

  • 방언: SQL 표준을 지키지 않는 특정 데이터베이스만의 고유한 기능
  • JPA는 자동으로 방언을 해석해서 특정 데이터베이스에 종속하지 않는다
  • 데이터베이스 변경 시 보수가 용이하다
  • 하이버네이트는 40가지 이상의 데이터베이스 방언 지원
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>

 

🔽JPQL

  • JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어 제공
  • JPQL은 테이블이 아닌 객체를 대상으로 검색하는 객체 지향 쿼리 (SQL은 데이터베이스 테이블을 대상으로 쿼리)
  • SQL을 추상화해서 특정 데이터베이스 SQL에 의존 X

🔽JPA 내부 동작

  • 영속성 컨텍스트
    • 엔티티를 영구 저장하는 환경
    • 엔티티 매니저를 통해 접근한다 (엔티티 매니저 내부에 영속성 컨텍스트 공간이 생긴다)

  • 생명 주기

1. 비영속

  • JPA와 전혀 관계없는 객체만 생성한 상태

2. 영속

  • em.persist를 통해 엔티티 매니저 안의 영속 컨텍스트에 객체를 저장한 상태로 객체는 관리될 준비가 됨

3. 준영속

  • 영속 상태의 엔티티가 영속성 컨텍스트에서 분리
  • 영속성 컨텍스트가 제공하는 기능을 사용 못함
    • em.detach : 특정 엔티티만 준영속 상태로 전환
    • em.clear : 영속성 컨텍스트를 완전히 초기화
    • em.close : 영속성 컨텍스트를 종

4. 삭제

  • em.remove 객체를 삭제한 상태
package hellojpa;

import jakarta.persistence.*;

public class JpaMain {

    public static void main(String[] args) {
        //엔티티 매니저 팩토리 생성
        //persistence.xml 설정파일의 <persistence-unit name="hello">로 설정 정보를 조회
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        //엔티티 매니저 생성
        //엔티티 매니저는 쓰레드 간에 공유하면 안된다  (사용하고 버린다)
        EntityManager em = emf.createEntityManager();

        //데이터를 변경하는 모든 작업은 꼭 transcation안에서 작업을 해아한다
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        Member member = null;
        try {
            //insert
            member = new Member();
            member.setId(2L);
            member.setName("Hello2");
            em.persist(member); //엔티티 저장

            //조회, 수정
            Member find =  em.find(Member.class, 2L);
            find.setName("JPA"); //JPA를 통해서 엔티티를 가져오면 엔티티 매니저가 관리함
            //트랜잭션 커밋 시점에 변경이 됐는지 안됐는지 체크함
            //변경사항이 있으면 update 쿼리를 먼저 만들어서 날리고 트랜잭션 커밋함
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
        emf.close();
    }
}

 

 

🔽 영속성 컨텍스트의 이점

➡️ 1차 캐시

  • DB까지 조회하지 않고 1차 캐시에서 조회하고 1차 캐시에 없으면 DB에서 조회해 온 엔티티를 1차 캐시에 저장한다
  • 이때, 고객의 요청마다 하나씩 연결되는 영속성 컨텍스트(엔티티 매니저)는 비즈니스가 끝나면 지우면서 1차 캐시도 사라짐
  • 데이터베이스 하나의 트랜잭션 안에서만 조회 시 효과가 있음

➡️ 영속 엔티티의 동일성 보장

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

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

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

try {
    Member memberA = new Member(10L, "A");
    Member memberB = new Member(11L, "B");

    em.persist(memberA);
    em.persist(memberB);
    //여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
    //커밋하는 순간 데이터베이스에 한꺼번에 모아서 INSERT SQL을 보낸다.
    //버퍼링 같은 기능
    tx.commit(); // [트랜잭션] 커밋

 

➡️변경 감지/Dirty Checking

  • 커밋하는 순간 JPA 내부적으로 flush를 호출한다.
  • 이때 엔티티와 스냅샷(최초의 상태)을 모두 비교한다.
  • 변경 내용이 있으면 쓰기 지연 SQL 저장소에 update 쿼리문을 생성한다.
  • 데이터베이스에 update 쿼리문을 실행한다.
  • 커밋한다.

 

🔽플러시

  • 영속성 컨텍스트의 변경내용을 데이터베이스에 반영(동기화)
  • 플러시는 직접 호출하거나 (em.flush) 트랜잭션 커밋이나 JPQL 쿼리를 실행할 때 자동 호출된다
  • 플러시 옵션을 바꿔서 커밋할 때만 플러시를 할 수 있다
em.setFlushMode(FlushModeType.COMMIT);
em.setFlushMode(FlushModeType.AUTO);