자바 서버 개발을 위한 JPA에 대해서 알아보고자 했다. 학습 내용은 자바 ORM 표준 JPA 프로그래밍 교재를 기반으로 작성했다.
MySQL, Oracle 등 주로 사용되는 데이터베이스를 설치해도 되지만 테스트로 사용하기엔 부담이 있다. 그러므로 설치가 필요 없고 실제로 테스트로 사용되기도 하는 H2 데이터베이스를 사용하겠다.
https://www.h2database.com에 들어가서 All platforms
으로 내려받으면 설치 없이 사용할 수 있다. 압축을 풀고 bin/h2.bat
을 실행하면 서버 모드로 실행할 수 있다. 그리고 웹 브라우저에서 localhost:8082
로 접속하면 아래와 같은 화면을 볼 수 있다.
위 화면에서 왼쪽 위 언어를 한국어로 설정하고 JDBC URL을 jdbc:h2:tcp://localhost/~/test
으로 수정한 후 연결을 선택하면 test 데이터베이스에 서버 모드로 접근한다.
예제로 사용하기 위한 테이블을 생성하자.
CREATE TABLE MEMBER(
ID VARCHAR(255) NOT NULL,
NAME VARCHAR(255),
AGE INTEGER NOT NULL,
PRIMARY KEY (ID)
)
메이븐은 라이브러리 관리 도구이다.
pom.xml
에 사용할 라이브러리를 작성하면 라이브러리를 자동으로 내려받아 관리한다.
먼저 Maven 프로젝트를 생성하고 pom.xml
을 아래와 같이 작성하여 의존성을 추가해준다.
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.crackco</groupId>
<artifactId>02-jap-start</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<!-- encoding settings -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- hibernate version -->
<hibernate.version>4.3.10.Final</hibernate.version>
<!-- h2 database version -->
<h2db.version>1.4.187</h2db.version>
</properties>
<dependencies>
<!-- hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- h2 database -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2db.version}</version>
</dependency>
</dependencies>
</project>
<dependency>
안에 <groupId>
, <artifactId>
, <version>
를 작성하면 Maven 저장소에서 내려받아 라이브러리에 자동으로 추가된다.
위에 작성한 Member
테이블의 내용으로 Member
클래스를 작성한다.
package jpabook.start;
/**
* @author Woojo
*/
public class Member {
private String id;
private String username;
private int age;
// Getter, Setter
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
그 다음 JPA에서 제공하는 매핑 어노테이션을 추가한다. 매핑 어노테이션을 추가하기 위해서 javax.persistence
를 import
하면 된다.
package jpabook.start;
import javax.persistence.*;
/**
* @author Woojo
*/
@Entity
@Table(name = "MEMBER")
public class Member {
@Id
@Column(name = "ID")
private String id;
@Column(name = "NAME")
private String username;
// 매핑 정보가 없는 필드
private int age;
// Getter, Setter
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
예제에서 사용한 매핑 어노테이션
- @Entity
해당 클래스를 테이블과 매핑을 명시, 해당 클래스를 엔티티 클래스라고 한다.- @Table
엔티티 클래스에 매핑할 테이블 정보를 명시,name
속성을 사용하여MEMBER
테이블에 매핑한다고 명시하였고 생략 시 클래스 이름과 같은 테이블에 매핑한다.- @Column
필드를 컬럼에 매핑한다.name
속성을 사용하여 매핑할 컬럼명을 명시할 수 있다.
생략 시 필드명과 같은 이름의 컬럼에 매핑한다.
META-INF/persistence.xml
을 작성하여 JPA가 필요한 설정 정보를 관리할 수 있게 한다.
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
version="2.1">
<persistence-unit name="jpabook">
<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.id.new_generator_mappings" value="true" />
<!--<property name="hibernate.hbm2ddl.auto" value="create" /> -->
</properties>
</persistence-unit>
</persistence>
설정은 <persistence>
로 xml의 네임스페이스와 사용할 버전을 지정한다.
JPA 설정은 <persistence-unit name="">
으로 설정할 수 있으며 일반적으로 데이터베이스 하나당 등록한다. 또한 name
속성을 사용하여 고유한 이름을 부여해야 한다.
데이터베이스와 연결을 위해서 <properties>
안에 각 <property>
를 설정해주어야 한다.
- JPA 표준 속성
javax.persistence.jdbc.driver
: JDBC 드라이버javax.persistence.jdbc.user
: 데이터베이스 접속 사용자명javax.persistence.jdbc.password
: 데이터베이스 접속 비밀번호javax.persistence.jdbc.url
: 데이터베이스 접속 url- hibernate 속성
hibernate.dialect
: 데이터베이스 방언 설정
앞서 객체 매핑, persistence.xml
을 작성하였으면 애플리케이션 개발을 위해서 main
메서드를 작성해주어야 한다.
package jpabook.start;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class JPAMain {
public static void main(String[] args) {
// 엔티티 매니저 팩토리 생성
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook");
// 엔티티 매니저 생성
EntityManager em = emf.createEntityManager();
// 트랜잭션 획득
EntityTransaction tx = em.getTransaction();
try {
tx.begin(); // 트랜잭션 시작
logic(em); // 비즈니스 로직 실행
tx.commit(); // 트랜잭션 커밋
} catch (Exception e) {
tx.rollback(); // 에러 발생 시 트랜잭션 롤백
} finally {
em.close(); // 엔티티 매니저 종료
}
emf.close(); // 엔티티 매니저 팩토리 종료
}
private static void logic(EntityManager em) {
}
}
persistence.xml
의 설정 정보를 기반으로 EntityManagerFactory
를 생성한다. EntityManagerFactory
는 단 한 번 생성하여 사용하고 마지막에 종료한다.
EntityManagerFactory
를 사용하여 EntityManager
를 생성할 수 있다. 대부분의 JPA 기능은 EntityManager
가 제공하므로 데이터베이스의 조작을 수행하는 역할을 수행한다.
EntityManager
는 데이터베이스 연결과 밀접한 관계가 있으므로 스레드간 공유하거나 재사용하면 안 된다.
JPA는 항상 트랜잭션 안에서 데이터를 조작해야 한다. 트랜잭션은 EntityManager
의 API를 사용하여 EntityTransaction
을 받아올 수 있다.
private static void logic(EntityManager em) {
String id = "id1";
Member member = new Member();
member.setId(id);
member.setUsername("CrackCo");
member.setAge(2);
// 등록
em.persist(member);
// 수정
member.setAge(20);
// 멤버 조회
Member findMember = em.find(Member.class, id);
System.out.println("findMember=" + findMember.getUsername() + ", age=" + findMember.getAge());
// 목록 조회
List<Member> members = em.createQuery("select m from Member m", Member.class).getResultList();
System.out.println("members.size=" + members.size());
// 삭제
em.remove(member);
}
// 출력
findMember=Crackco, 20
members.size=1
EntityManager
객체 em
을 사용하여 데이터베이스 CRUD를 수행하는 코드이다.
대부분 EntityManager
의 API를 사용하여 데이터를 조작하지만 Update는 객체를 수정할 뿐이다. 이는 JPA가 엔티티의 변경을 추측하는 기능을 갖추고 있기 때문에 자동으로 Update SQL을 수행한 결과이다.
아래는 목록을 조회하는 코드이다.
List<Member> members = em.createQuery("select m from Member m", Member.class).getResultList();
System.out.println("members.size=" + members.size());
JPA를 사용하는 개발자는 엔티티 객체를 중심으로 개발하고 데이터베이스 연동은 JPA에 맡기게 된다. 하지만 검색을 수행할 땐 모든 데이터를 불러와서 엔티티 객체로 변환하고 검색을 수행해야 한다. 이는 시스템에 매우 큰 부담을 주며 사실상 불가능에 가깝기 때문에 필요한 데이터를 검색하기 위해 검색 조건이 있는 SQL을 사용해야 한다. JPA에선 JPQL이라는 쿼리 언어를 사용하여 문제를 해결한다.
JPQL
SQL을 추상화한 객체 지향 쿼리 언어로 SQL과 문법이 비슷하다.
SQL과 차이점으로는 엔티티 객체를 대상으로 쿼리를 수행한다는 것이다.