JPA(Java Persistence API) 01 - 개요

ORM

패러다임의 불일치. 객체지향 프로그래밍과 관계형 데이터베이스의 패러다임은 서로 다르기 때문에 불일치 문제가 발생.

따라서 객체와 관계형 데이터베이스 사이의 불일치 문제를 해결해 주는 역할로 ORM(Object-Relational Mapping)이 필요하며, JPA(Java Persistence API)가 바로 이런 ORM 프레임워크.

국내에서 아직까지도 많이 사용되는 MyBatis는 ORM 프레임워크가 아닌 SQL 매퍼.

예제 코드를 위해 필요한 의존성

1
2
3
4
5
6
7
dependencies {
compile group: 'org.hibernate.javax.persistence', name: 'hibernate-jpa-2.1-api', version: '1.0.2.Final'
compile group: 'org.hibernate', name: 'hibernate-core', version: '5.4.2.Final'

// 테스트용으로 H2 데이터베이스를 사용하기 위해서 필요
compile group: 'com.h2database', name: 'h2', version: '1.4.199'
}

JPA 설정

persistence.xml : JPA 환경 설정 정보를 지정하는 XML 파일.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">
<persistence-unit name="jpa-playground" transaction-type="RESOURCE_LOCAL">

<class>personal.jpa.playground.Member</class>
<class>personal.jpa.playground.Product</class>

<properties>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="javax.persistence.jdbc.user" value="test"/>
<property name="javax.persistence.jdbc.password" value="test"/>
<property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test"/>

<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>

<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.id.new_generator_mappings" value="true"/>
</properties>
</persistence-unit>
</persistence>

persistence-unit 요소의 transaction-type 속성은 RESOURCE_LOCAL, JTA 2가지가 있다.

RESOURCE_LOCAL로 설정하면 엔티티 매니저, 트랜잭션 등을 직접 관리해야 한다.

JTAJava Transaction API의 줄임말로, 엔터프라이즈 환경에서 주로 사용하는데 직접 엔티티 매니저를 생성하지 않고 컨테이너가 대신 생성해서 넘겨주는 엔티티 매니저를 사용하며 트랜잭션 역시 컨테이너가 관리한다.

쉽게 생각하자면 엔티티 매니저를 직접 생성하고 트랜잭션을 직접 관리할 경우는 RESOURCE_LOCAL, 컨테이너가 관리할 것이라면 JTA로 지정하면 된다.

지금은 엔터프라이즈 환경에서 사용하는 코드를 만드는 것이 아니라 직접 엔티티 매니저를 생성하고 트랜잭션을 관리하는 코드를 작성할 것이기 때문에 RESOURCE_LOCAL로 지정한다.

JPA 표준 속성

  • javax.persistence.jdbc.driver : JDBC 드라이버.
  • javax.persistence.jdbc.user : 데이터베이스 계정 아이디.
  • javax.persistence.jdbc.password : 데이터베이스 계정 패스워드.
  • javax.persistence.jdbc.url : 데이터베이스 URL.

하이버네이트 관련 속성

  • hibernate.dialect : 데이터베이스 방언(dialect) 설정.
  • hibernate.show_sql : 하이버네이트가 실행한 SQL 출력.
  • hibernate.format_sql : SQL 출력 시 정렬하여 출력.
  • hibernate.use_sql_comments : SQL 출력 시 주석도 같이 출력.
  • hibernate.id.new_generator_mappings : JPA 표준 키 생성 전략 사용.
  • hibernate.hbm2ddl.auto : 애플리케이션 실행 시 데이터베이스 테이블 자동 생성.

엔티티 매니저 팩토리

엔티티 매니저를 생성하기 위한 팩토리 객체. persistence.xml에 지정된 설정 정보를 바탕으로 생성된다. 이 때 JPA 구현체에 따라 데이터베이스 커넥션 풀도 생성하므로 생성 비용이 큰 편이다. 스레드 안전(Thread-safe)하게 구현되어 있고 생성 비용이 크므로 애플리케이션 전체에서 한 번만 생성하여 사용하는 것이 일반적이다.

엔티티 매니저

엔티티 매니저 팩토리에서 생성한다. 엔티티를 저장하거나 수정, 삭제, 조회 등 엔티티 관련 작업을 처리하는 역할의 객체다. 엔티티 매니저 내부에 데이터베이스 커넥션을 유지하면서 데이터베이스와 통신한다. 유의할 점으로는 엔티티 매니저가 데이터베이스 커넥션과 관계 있으므로 스레드 간에 공유하거나 재사용하면 안된다는 것.

트랜잭션 시작 및 커밋, 롤백

JPA를 사용하면 항상 트랜잭션 내에서 데이터를 변경해야 하며 트랜잭션 없이 데이터 변경 시 예외가 발생한다.

엔티티 매니저로부터 EntityTransaction 객체를 가져와 사용한다.

1
2
3
4
5
6
7
8
9
10
11
try {
EntityTransaction transaction = entityManager.getTransaction();

transaction.begin();
doSomething(entityManager);
transaction.commit();

} catch(Exception e) {
e.printStackTrace();
transaction.rollback();
}
  • begin() : 트랜잭션 시작.
  • commit() : 트랜잭션 커밋.
  • rollback() : 트랜잭션 롤백.

엔티티 등록 : persist()

엔티티를 삽입하는 INSERT SQL이 생성된다.

엔티티 수정

엔티티 수정 후 수정 내용 반영에는 별도의 메서드 호출이 필요 없다. JPA는 어떤 엔티티가 변경되었는지 추적 가능하기 때문에 엔티티의 값만 변경하면 UPDATE SQL이 생성되어 데이터베이스에 반영된다.

엔티티 삭제 : remove()

엔티티를 삭제하는 DELETE SQL이 생성된다.

엔티티 단일 조회 : find()

조회할 엔티티 타입과 식별자 값으로 엔티티 하나를 조회하는 SELECT SQL이 생성되어 조회한 결과 값으로 엔티티가 생성되어 반환된다.

JPQL(Java Persistence Query Language)

SQL을 추상화한 객체지향 쿼리 언어. SQL과 거의 유사한 문법을 가진다. 둘의 가장 큰 차이점은 어느 대상으로 질의하느냐, 인데 SQL이 데이터베이스의 테이블을 대상으로 질의하는 반면 JPQL은 엔티티 객체를 대상으로 질의한다.

예를 들어 엔티티 객체를 대상으로 특정 조건 검색하는 경우, 데이터베이스의 데이터를 전부 가져와서 엔티티 객체로 변경한 후 검색해야 하는데 대량의 데이터를 가진 데이터베이스의 경우 이것은 불가능한 일이다. 따라서 특정 조건으로 데이터베이스에서 필요한 데이터만 불러오기 위한 쿼리를 생성해야 하는데, 이 때 JPQL을 사용할 수 있다.

엔티티 매니저의 createQuery() 메서드를 사용하여 쿼리 객체를 생성하고 쿼리 객체의 getResultList() 메서드를 통해 쿼리에 해당하는 엔티티 객체들을 반환 받을 수 있다.

위 내용은 김영한님의 자바 ORM 표준 JPA 프로그래밍를 읽으며 개인적으로 요약 및 정리하는 내용이다.
자세한 내용이 알고 싶으면 김영한님의 자바 ORM 표준 JPA 프로그래밍을 직접 읽어보길 추천한다.