JPA - 영속성 컨텍스트

bp.chys·2020년 5월 1일
2

JPA

목록 보기
1/15
post-thumbnail

JPA란?

  • Java Persistence API
  • 자바 진영의 표준 ORM 기술
  • 객체를 컬렉션에 넣어 관리하듯이 데이터베이스를 다룰 수 있게 해준다.
  • 객체를 저장하는 현실적인 대안은 RDB지만, 이를 위해서는 SQL로 직접 객체를 테이블에 매핑시켜줘야 한다.
  • 영속성 컨텍스트라는 논리적 공간을 제공하여 객체 저장에 대한 용이함을 제공한다.
  • JPA 인터페이스를 구현한 것이 Hibernate, EclipseLink, DataNucleus이다.

JPA가 필요한 이유 - 패러다임의 불일치

  • 어떤 패러다임? 객체 vs 관계형 데이터 베이스
  • 상속
    • 테이블의 슈퍼타입, 서브타입이 상속과 비슷하지만 Album 데이터 하나를 저장할 때, 객체는 자식만 생성하면 되지만 테이블에서는 슈퍼타입, 서브타입 테이블에 모두 추가해줘야 한다.
  • 연관관계
    • 객체의 연관관계는 단방향으로써 Team은 Member를 조회할 수 없지만, 테이블은 양방향이라서 서로 참조할 수 있다.

  • 데이터 타입
    • 객체는 Integer, Long, String을 쓰지만 테이블은 BIGINT, INTEGER, VARCHAR(255) 등를 사용한다.
  • 데이터 식별 방법

영속성 컨텍스트

정의

  • "엔티티를 영구 저장하는 환경"
  • 영속성 컨텍스트는 물리적인 개념이 아닌 논리적인 개념이다.
  • 엔티티 매니저를 통해서 영속성 컨텍스트에 접근할 수 있다.
  • EntityManager.persist(entity);

영속성 컨텍스트의 생명주기

  • 비영속 : 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
  • 영속 : 영속성 컨텍스트에서 관리되는 상태
    • EntityManager.persist(entity);
  • 준영속 : 영속성 컨텍스트에 저장되었다가 분리된 상태, 영속성 컨텍스트가 제공하는 기능을 사용 못함.
    • EntityManager.detach(entity); : 특정 엔티티만 준영속 상태로 전환
    • EntityManager.clear(); : 영속성 컨텍스트 초기화
    • EntityManager.close(); : 영속성 컨텍스트 종료
  • 삭제 : 삭제된 상태
    • EntityManager.remove(entity);

영속성 컨텍스트의 장점

  • 1차 캐시
  • 트랜 잭션을 지원하는 쓰기 지연 저장소
  • 지연 로딩 (Lazy Loading)
  • 변경 감지 (Dirty Checking)
  • DB에 종속적이지 않은 인터페이스로 설계되었기 때문에 DB 각각의 방언을 지원한다.
    • Hibernate 구현체를 많이 씀

💡 플러시(flush)란?

  • 영속성 컨텍스트의 변경 내용을 데이터 베이스에 반영 (동기화)
  • 엔티티 쓰기 지연 SQL 저장소의 쿼리를 데이터 베이스에 전송해준다.
  • 플러시는 영속성 컨텍스트를 비우지 않는다.
  • 트랜잭션이라는 작업 단위가 중요해진다.
  • 플러시 호출 방법 : EntityManager.flush(), 트랜잭션 커밋, JPQL 쿼리 실행

데모

1. 프로젝트 생성 및 JPA, Hibernate 의존성 추가

// build.gradle
dependencies {
++ compile group: 'org.hibernate.javax.persistence', name: 'hibernate-jpa-2.1-api', version: '1.0.2.Final'
}

2. JPA 설정 파일 생성

  • /resource/META-INF/persistence.xml
  • persistence-unit name으로 이름 지정
  • JPA는 특정 DB에 종속적이지 않은 인터페이스로 설계되었기 때문에 DB 각각의 방언을 지원한다.
<!-- persistence.xml -->

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
             xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
    <persistence-unit name="hello">
        <properties>
            <!-- 필수 속성 -->
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
            <property name="javax.persistence.jdbc.user" value="sa"/>
            <property name="javax.persistence.jdbc.password" value=""/>
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
            <!-- 옵션 -->
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.use_sql_comments" value="true"/>
            <!--<property name="hibernate.hbm2ddl.auto" value="create" />-->
        </properties>
    </persistence-unit>
</persistence>

3. JPA 동작 확인

  • EntityManagerFactory 생성 (빈으로 주입해도 무방)
  • EntityManagerFactory → EntityManager 생성
  • EntityManager → EntityTransaction 생성
  • 엔티티 매니저 팩토리는 하나만 생성해서 애플리케이션 전체에서만 공유한다.
  • 엔티티 매니저는 쓰레드간에 공유하지 않고 사용하고 버려야 한다.
  • JPA의 모든 데이터 변경은 트랜잭션 안에서 실행해야 한다.
public class JpaMain {
    public static void main(String[] args) {
        final EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        final EntityManager em = emf.createEntityManager();
        final EntityTransaction tx = em.getTransaction();
        
        tx.begin();  // 트랜잭션 시작
        try {
            Member persistMember = new Member();
            persistMember.setName("hello");
            em.persist(persistMember);  // 저장(Insert)
            
            Member findMember = em.find(Member.class, 2L);
            findMember.setName("changed");  // 수정(Update)

            tx.commit();  // 트랜잭션 커밋
        } catch (Exception e) {
            tx.rollback();  // 에러 발생 시 트랜잭션 롤백
        } finally {
            em.close();
        }

        emf.close();
    }
}

결론

JPA는 객체를 RDB(관계형 데이터베이스)에 편리하게 저장하기 위한 자바 진영의 표준 ORM이다. SQL문을 직접 작성할 필요없이 기본 제공되는 API와 영속성 컨텍스트를 사용해서 객체를 효과적으로 저장하고 조회할 수 있다. 필요하다면 JPQL을 사용해서 custom 쿼리를 작성할 수도 있다.

스프링 프레임워크로 개발할 경우, JPA보다 한 단계 더 추상화된 Spring-Data-JPA를 사용하여 Auditing이나, named Query 등 더욱 편한 API를 사용할 수 있지만, 편리한 사용법만 익히기보다 JPA의 기본 원리를 학습한다면 개발할 때 폭넓은 제어가 가능할 것으로 생각된다.

참고자료

  • 자바 ORM 표준 JPA 프로그래밍, 김영한 저
profile
하루에 한걸음씩, 꾸준히

0개의 댓글