1. JPA를 이용한 CRUD


/META-INF/persistence.xml 파일에 JPA 설정값을 정의합니다. (어떤 DB를 사용할지, 계정 정보, Hibernate 관련 추가 속성)

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.2">
    <persistence-unit name="hello">
        <properties>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://192.168.0.2:3306/shop"/>
            <property name="javax.persistence.jdbc.user" value="test"/>
            <property name="javax.persistence.jdbc.password" value="text"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/>
            
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.fromat_sql" value="true"/>
            <property name="hibernate.use_sql.comments" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

다음으로 main 메서드에서 EntityManager를 이용한 코드입니다.

public class Main {
    public static void main(String[] args) {
        // 파라미터는 /META-INF/persistence.xml 이라는 JPA 설정 파일에 정의해놓은 
        // <persistence-unit name="hello"> 를 의미합니다.
        EntityManagerFactory emFactory = 
            Persistence.createEntityManagerFactory("hello");
        
        // EntityManager가 DB 커넥션을 물고 있기 때문에 사용 후 꼭 .close() 로 해제해야 합니다.
        EntityManager entityManager = emFactory.createEntityManager();
        
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();
        try {
            // 삽입
            Member member = new Member(1L, "홍길동");
            entityManager.persist(member);
            
            // 조회
            Member findMember = entityManager.find(Member.class, 1L);
            
            // 수정 
            findMember.setName("김길동");
            
            // 삭제 
            entityManager.remove(findMember);
        } catch (Exception e) {
            tx.rollback();
        } finally {
            entityManager.close();
        }
        
        emFactory.close();
    }
}
  • EntityManagerFactory는 하나만 생성해서 애플리케이션 전체에서 공유합니다.
  • EntityManager는 스레드 간에 공유하면 안됩니다. (사용하고 버려야 합니다.)
  • JPA의 모든 데이터 변경은 트랜잭션 안에서 실행해야 합니다.

기본적으로 RDB는 쿼리를 트랜잭션 안에서 실행하도록 설계되어 있습니다.
만약 트랜잭션 코드없이 CRUD가 정상 실행되었다면, auto commit이 활성화되어 있기 때문입니다.
(즉, 내부적으로는 무조건 트랜잭션 처리가 일어납니다.)

2. JPQL


JPA를 사용하면 entity 객체를 중심으로 개발하는데 이때, 문제는 검색 쿼리입니다.
검색을 할때도 DB의 테이블이 아닌 entity 객체를 대상으로 검색합니다.

  • JPA는 SQL을 추상화한 JPQL이라는 객체지향 쿼리언어를 제공합니다.
    FROM절에 DB의 테이블명이 아닌 entity 클래스명을 명시합니다. 해당 entity 클래스가 어떤 테이블을 가리키는지는 몰라도 됩니다.
    이로 인해, 어떤 DB로 변경하든 테이블명이 수정되든 쿼리를 수정해야할 일은 전혀 없습니다.
// .setFirstResult()와 .setMaxResults()는 추출할 시작 위치와 오프셋입니다.
// MySQL을 이용할때 해당 코드는 LIMIT 쿼리입니다.
List<Member> result = 
    entityManager.createQuery("SELECT m FROM Member as m", Member.class)
        .setFirstResult(0)
        .setMaxResults(4)
        .getResultList();
        
for (Member member : result) {
    System.out.println("member.getName() : " + member.getName());
}

3. 영속성 컨텍스트


JPA에서 가장 중요한 두 가지는 다음과 같습니다.

  1. 객체와 관계형 데이터베이스 매핑하기
  2. 영속성 컨텍스트

영속성 컨텍스트를 이해하면 JPA가 내부적으로 어떻게 동작하는지 이해할 수 있습니다.
따라서 JPA를 이해하는데 가장 중요한 용어이며, 한글로 번역하면 "entity를 영구 저장하는 환경" 이라고 볼 수 있겠습니다.

[그림 1] JPA 사용흐름

JPA는 J2SE 환경과 J2EE, Spring 프레임워크와 같은 컨테이너 환경에서 차이가 있습니다.

J2SE 환경

EntityManager와 영속성 컨텍스트와 1:1 대응됩니다. 즉, EntityManager를 생성하면 내부에 영속성 컨텍스트도 가지고 있습니다.

[그림 2] SE 환경에선 EntityManager와 영속성 컨텍스트가 1:1 관계

J2EE 또는 Spring 프레임워크와 같은 컨테이너 환경

EntityManager와 영속성 컨텍스트가 N:1 대응됩니다.

[그림 3] EE 환경에선 EntityManager와 영속성 컨텍스트가 N:1 관계

Entity 객체의 생명주기

Entity 객체는 크게 4가지 상태로 나눠볼 수 있습니다.

  • 비영속(new/transient): 영속성 컨텍스트와 전혀 관계가 없는 상태
  • 영속(managed): 영속성 컨텍스트 내에서 관리되는 상태
  • 준영속(detached): 영속성 컨텍스트에서 관리되다가 detach한 상태
  • 삭제(removed): 삭제된 상태

 

비영속 상태는 다음과 같이 단순히 객체를 생성만 해놓은 상태를 말합니다.

Member member = new Member(2L, "유재석");

영속 상태는 객체를 EntityManager 클래스의 .persist() 메서드를 이용해 영속성 컨텍스트에 저장해 관리하는 상태를 말합니다.

EntityManager entityManager = emf.createEntityManager();
entityManager.getTransaction().begin();
entityManager.persist(member); // 객체를 해당 EntityManager의 영속성 컨텍스트에 저장합니다.