JPA는 SQL 중심 프로그래밍을 자동화하여 개발자가 쿼리를 짜는 노고를 줄이는 ORM 기술이다.
JPA를 구현하는 대표 구현체는 Hibernate이다. JPA 구현은 대부분 Hibernate로 구현된다. DB는 H2 DB를 사용할 예정이다.
( H2 사용은 위 포스팀 참고 )
그럼 JPA 프로젝트를 만들어보자.
1. pom.xml 설정하기
JPA 예제를 위한 라이브러리를 디펜던시로 추가해보자. 필수로 추가할 라이브러리는 두 가지이다.
1. Hibernate 라이브러리
2. H2 JDBC 라이브러리
JPA는 Hibernate 구현체로 구현되며 Hibernate는 H2 JDBC를 바탕으로 구현된다.
( JAVA 8보다 높은 버전인 경우, persistence.xml 을 읽기위한 xml bind API도 디펜던시로 추가해야 한다. )
추가로 롬복 라이브러리를 추가해주자.
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>hello-jpa</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- JPA 하이버네이트 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.3.10.Final</version>
</dependency>
<!-- H2 데이터베이스 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.199</version>
</dependency>
<!-- JAVA 8 이상 버전인 경우, persistence.xml 파일을 읽으려면 필요함 -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<!-- 롬복 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
2. persistence.xml 구성하기
이전 포스팅에서 JPA 동작원리를 설명하며 아래 그림에 대해서 설명하였다.
@Entity로 표시된 클래스를 '영속화'하려면 EntityManager가 필요한데, EntityManager 객체를 생성하는 클래스가 EntityManagerFactory이다. EntityManagerFactory는 설정정보가 필요하다. JPA는 DB와 통신해야하므로 JDBC정보와 추가옵션을 담은 설정정보가 필요하다. 설정정보는 META-INF 폴더 아래 persistence.xml에 작성하기로 약속되어 있다.
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="h2">
<properties>
<!-- JDBC 필수정보 -->
<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"/> <!-- H2 방언을 사용함을 명시 -->
<!-- 추가 옵션정보 -->
<property name="hibernate.show_sql" value="true"/> <!--SQL 쿼리문을 출력한다.-->
<property name="hibernate.format_sql" value="true"/> <!--쿼리문을 포맷팅하여 보여준다.-->
<property name="hibernate.use_sql_comments" value="true"/> <!--쿼리문 관련 정보를 주석으로 보여준다.-->
<property name="hibernate.hbm2ddl.auto" value="create" /> <!--하이버네이트에서 DDL을 조작한다 -->
</properties>
</persistence-unit>
</persistence>
persistence.xml은 persistence 태그로 구성된다. persistence 태그는 persistence-unit 태그를 여러 개 가질 수 있다.
persistence-unit 태그는 하나의 DB 연결정보를 담으므로 persistence-unit 태그를 여러개 만들어 여러 DB의 연결정보를 관리할 수 있다. 그 중 특정 DB 연결정보에 접근하려면 persistence-unit의 name 속성으로 접근하면 된다.
@Test
void test(){
//EntityManagerFactory 생성
EntityManagerFactory emf = Persistence.createEntityManagerFactory("h2"); //persistence-unit의 name 속성
//EntityManager 생성
EntityManager entityManager = emf.createEntityManager();
}
예를 들어, EntityManagerFactory는 persistence-unit의 name 속성의 값을 생성자 파라미터로 받아 생성된다. 그러므로 EntityManagerFactory - persistence-unit - DB로 연결된다.
-방언정보(hibernate.dialect)
JDBC 필수정보를 제외하고 다른 정보를 살펴보면, 방언정보(hibernate.dialect)가 있다. 하이버네이트는 H2, Oracle, MySQL 등등 여러 JDBC를 지원한다. JDBC는 공통의 표준이 있지만 각각의 특수성이 있다. 하이버네이트는 특수성을 지원하는 구현체가 있는데 이를, 방언이라고 부른다. 우리는 H2 DB를 사용하므로 H2 DB 방언 구현체를 속성으로 추가한다.
- SQL 출력 정보
하이버네이트는 자동으로 쿼리를 생성하고 실행하기에 실행된 쿼리를 로그로 출력하는 옵션을 추가할 수 있다.
- hibernate.hbm2ddl.auto
hibernate.hbm2ddl.auto 속성은 DB 스키마를 Hibernate 측에서 조작하는 설정이다. SpringBoot는 Oracle, MySQL 같이 운영에서 사용하는 DB는 해당 속성의 디폴트값을 none으로 한다. 복잡하고 독자적인 DB스키마를 가지고 있어 하이버네이트가 조작하기 힘들기 때문이다. 반면, h2같은 Embeded DB는 단순한 스키마를 가지므로, 객체정보를 토대로 하이버네이트가 DB스키마를 생성및조작할 수 있다. 그래서 SpringBoot는 해당 속성의 디폴트값을 create-drop으로 해놓는다.
우리는 SpringBoot 프로젝트가 아니므로 직접 설정을 넣어주어야 한다. hibernate.hbm2ddl.auto 속성을 create로 넣어주면 하이버네이트가 객체정보(엔티티정보)를 토대로 App이 실행시 DB스키마를 구성한다.
프로젝트를 실행하면 DB스키마를 생성하고 SQL쿼리문을 날려 기존에 있는 테이블은 삭제하고 새로운 테이블을 구성한다. H2 DB같은 테스트나 개발환경에서 사용하면 좋은 기능이다. 실운영 환경에서 사용하면 기존DB의 테이블 정보가 모두 삭제 될 수 있으니 주의해서 사용해야한다.
3. 테스트하기
pom.xml과 persistence.xml 의 설정을 완료하였으니 이제 테스트를 해보자.
Member 클래스 ( 엔티티 클래스 )
@Entity
@Data
public class Member {
@Id
public String name;
public int age;
public Member(String name, int age) {
this.name = name;
this.age = age;
}
}
Main 클래스
public class Main {
public static void main(String[] args) {
//EntityManagerFactory 생성
EntityManagerFactory emf = Persistence.createEntityManagerFactory("h2");
//EntityManager 생성
EntityManager entityManager = emf.createEntityManager();
//EntityTransaction 생성
EntityTransaction tx = entityManager.getTransaction();
//트랜잭션 시작
tx.begin();
try{
Member member = new Member("사쿠라",26); // 엔티티생성
entityManager.persist(member); // 엔티티 영속화
tx.commit(); // 커밋 => Insert 쿼리문 전송
}catch (Exception e){
tx.rollback();
}finally {
entityManager.close();
}
}
}
EntityManagerFactory는 persistence-unit 속성의 값으로 h2를 넣어준다. h2설정정보에 접근 가능한 EntityManager를 생성한다. EntityManager는 Entity를 영속화한다. Member 객체는 persist 메소드로 영속화된다. Member 엔티티는 테이블과 매핑되어 JPA가 생명주기를 관리한다.
영속화 과정은 하나의 트랜잭션 중에 일어나야 한다. APP-JPA-DB는 사실 APP-Session-DB와 같다. EntityManager가 App과 DB사이의 Session을 관리한다. DB작업은 원자성이 중요하므로 하나의 세션이 유지되는 동안에만 작업이 이루어져야 한다. 트랜잭션이 시작되고 커밋이 될 때, 쿼리문이 전송되고 실DB에 반영된다. 관련해서는 앞으로 자세히 다루어 볼 것이다.
프로젝트를 실행하면 아래와 같이 로그가 출력된다.
APP 시작시, Table이 삭제 및 생성되고 Member 엔티티가 영속화되고 트랜잭션이 커밋되면서 Insert문이 실행되는 모습이다.
H2 DB 대시보드에도 데이터가 INSERT되었음을 확인할 수 있다. 개발자가 SQL문을 작성하지 않아도 영속화만으로 쿼리문이 자동으로 생성되고 수행되었다.
간단한 JPA 프로젝트를 한번 만들어 보았다.
다음 포스팅부터는 JPA 개념을 더해가며 복잡한 프로젝트를 만들어보겠다.
참고자료
'JPA > JPA Basic' 카테고리의 다른 글
[JPA] 엔티티(Entity)가 기본생성자를 가져야 하는 이유 ( Reflection ) (0) | 2023.05.25 |
---|---|
[JPA] 트랜잭션(Transaction) 이해하기(2) - JPA (0) | 2023.05.24 |
[JPA] 트랜잭션(Transaction) 이해하기(1) - JDBC (0) | 2023.05.24 |
[JPA] JPA 동작원리 ( 영속성 컨텍스트 ) (0) | 2023.05.22 |
[JPA] 패러다임 불일치 (0) | 2023.05.18 |