이 글은 김영한님께서 지으신 '자바 ORM 표준 JPA 프로그래밍'의 내용을 바탕으로 작성하였습니다.
이클립스는 LUNA 버전 이상으로 설치한다.
프로젝트 불러오기는 생략한다.
설치가 필요 없고 용량도 1.7M로 가벼운 H2 데이터베이스를 사용한다.
H2 데이터베이스는 JVM 메모리 안에서 실행되는 임베디드 모드와 실제 데이터베이스처럼 별도의 서버를 띄워서 동작하는 서버 모드가 있다.
test라는 이름의 데이터베이스에 서버 모드로 접근할 수 있다.
빌드 도구는 메이븐을 사용한다. 메이븐은 라이브러리 관리 기능과 빌드 기능을 제공한다.
pom.xml 파일에 dependencies 태그 안에 사용할 라이브러리를 지정한다.
...
<dependencies>
<!-- JPA, 하이버네이트 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.3.10.Final</version>
</dependency>
<!-- H2 데이터베이스 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.187</version>
</dependency>
</dependencies>
...
CREATE TABLE MEMBER (
ID VARCHAR(255) NOT NULL, --아이디 (기본 키)
NAME VARCHAR(255), --이름
AGE INTEGER, --나이
PRIMARY KEY (ID)
)
public class Member {
private String id;
private String username;
private Integer 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 Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
JPA를 사용하려면 가장 먼저 회원 클래스와 회원 테이블을 매핑해야 한다.
| 매핑 정보 | 회원 객체 | 회원 테이블 |
|---|---|---|
| 클래스와 테이블 | Member | MEMBER |
| 기본 키 | id | ID |
| 필드와 컬럼 | username | NAME |
| 필드와 컬럼 | age | AGE |
import javax.persistence.*;
@Entity
@Table(name = "MEMBER")
public class Member {
@Id
@Column(name = "ID")
private String id;
@Column(name = "NAME")
private String username;
//매핑 정보가 없는 필드
private Integer age;
...
}
<?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"/> <!-- JDBC 드라이버 -->
<property name="javax.persistence.jdbc.user" value="sa"/> <!-- 데이터베이스 접속 아이디 -->
<property name="javax.persistence.jdbc.pasword" value=""/> <!-- 데이터베이스 접속 비밀번호 -->
<property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/> <!-- 데이터베이스 접속 URL -->
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> <!-- 데이터베이스 방언 설정 -->
<!-- 옵션 -->
<property name="hibernate.show_sql" value="true" /> <!-- 하이버네이트가 실행한 SQL을 출력한다. -->
<property name="hibernate.format_sql" value="true" /> <!-- 하이버네이트가 실행한 SQL을 출력할 때 보기 쉽게 정렬한다. -->
<property name="hibernate.user_sql_comments" value="true" /> <!-- 쿼리를 출력할 때 주석도 함께 출력한다. -->
<property name="hibernate.id.new_generator_mappings" value="true" /> <!-- JPA 표준에 맞춘 새로우 키 생성 전략을 사용한다. -->
</properties>
</persistence-unit>
</persistence>
SQL 표준을 지키지 않거나 특정 데이터베이스만의 고유한 기능을 JPA에서는 방언(Dialect)이라 한다. 개발자는 JPA가 제공하는 표준 문법에 맞추어 JPA를 사용하면 되고, 특정 데이터베이스에 의존적인 SQL은 데이터베이스 방언이 처리해준다. 따라서 데이터베이스가 변경되어도 애플리케이션 코드를 변경할 필요 없이 데이터베이스 방언만 교체하면 된다.
하이버네이트는 다양한 데이터베이스 방언을 제공한다.
import javax.persistence.*;
import java.util.List;
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) {...}
}
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook");
EntityManager em = emf.createEntityManager();
em.close(); //엔티티 매니저 종료
emf.close(); //엔티티 매니저 팩토리 종료
EntityTransaction tx = em.getTransaction();
try {
tx.begin(); //[트랜잭션] - 시작
logic(em); //비즈니스 로직 실행
tx.commit(); //[트랜잭션] - 커밋
} catch (Exception e) {
tx.rollback(); //[트랜잭션] - 롤백
}
public static void logic(EntityManager em) {
String id = "id1";
Member member = new Member();
member.setId(id);
member.setUsername("지한");
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=지한, age=20
members.size=1
비즈니스 로직을 보면 등록, 수정, 삭제, 조회 작업이 엔티티 매니저를 통해서 수행되는 것을 알 수 있다. 엔티티 매니저는 객체를 저장하는 가상의 데이터베이스처럼 보인다.
String id = "id1";
Member member = new Member();
member.setId(id);
member.setUsername("지한");
member.setAge(2);
//등록
em.persist(member);
INSERT INTO MEMBER (ID, NAME, AGE) VALUES ('id1', '지한', 2)//수정
member.setAge(20);
UPDATE MEMBER SET AGE=20, NAME='지한', WHERE ID='id1'em.remove(member);
DELETE FROM MEMBER WHERE ID = 'id1'Member findMember = em.find(Member.class, id);
SELECT * FROM MEMBER WHERE ID='id1'//목록 조회
TypedQuery<Member> query = em.createQuery("select m from Member m", Member.class);
List<Member> members = query.getResultList();
SELECT M.ID, M.NAME, M.AGE FROM MEMBER M