이클립스 설치와 프로젝트 불러오기
- JPA를 사용해서 테이블 하나를 등록/수정/삭제/조회하는 간단한 JPA 애플리케이션 생성
- 이클립스 대신 인텔리제이 사용으로 생략
- 예제 프로젝트 내려받기 경로
- 인텔리제이에 예제 프로젝트 설정하기
H2 데이터베이스 설치
- H2 데이터베이스 설치 후 bin/h2.bat를 실행하여 H2 데이터베이스를 서버 모드로 실행
- 참고로 H2 데이터베이스는 JVM 메모리 안에서 실행되는 임베디드 모드와
실제 데이터베이스처럼 별도의 서버를 띄워서 동작하는 서버 모드가 존재 - H2 데이터베이스를 서버 모드로 실행한 후 웹 브라우저에서 http://127.0.0.1:8082로 H2 데이터베이스에 접속
- 예제 MEMBER 테이블 생성
-- member.sql
CREATE TABLE MEMBER (
ID VARCHAR(255) NOT NULL, --아이디(기본 키)
NAME VARCHAR(255), --이름
AGE INTEGER NOT NULL, --나이
PRIMARY KEY (ID)
)
라이브러리와 프로젝트 구조
- 필요한 모든 라이브러리를 직접 내려받아서 관리하기는 어려우므로 메이븐이라는 도구를 사용하여 라이브러리 관리
pom.xml에 사용할 라이브러리를 적어주면 라이브러리(jar 파일)를 자동으로 내려받아서 관리하며 빌드 기능을 제공 - JPA 구현체로 하이버네이트를 사용하기 위한 핵심 라이브러리로는 3개가 존재
1. hibernate-core : 하이버네이트 라이브버리
2. hibernate-entitymanager : 하이버네이트가 JPA 구현체로 동작하도록 JPA 표준을 구현한 라이브러리
3. hibernate-jpa-2.1-api : JPA 2.1 표준 API를 모아둔 라이브러리 - 예제에서 사용할 프로젝트 구조
JpaMain.java : 실행 클래스
Member.java : 회원 엔티티
persistence.xml : JPA 설정 정보
- 메이븐 설정 파일
<dependencies>에 사용할 라이브러리를 지정하며
groupId + artifactId + version만 적어주면 라이브러리를 메이븐 공식 저장소에서 내려받아 추가해 줌
<!-- 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>jpabook</groupId>
<artifactId>ch02-jpa-start1</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<!-- 기본 설정 -->
<java.version>1.6</java.version>
<!-- 프로젝트 코드 인코딩 설정 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- JPA, 하이버네이트 버전 -->
<hibernate.version>4.3.10.Final</hibernate.version>
<!-- 데이터베이스 버전 -->
<h2db.version>1.4.200</h2db.version>
</properties>
<dependencies>
<!-- JPA, 하이버네이트 -->
<!-- hubernate-entitymanager를 라이브러리로 지정하면 -->
<!-- hibernate-core.jar과 hibernate-jpa-2.1-api.jar 라이브러리를 함께 내려 받음 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- H2 데이터베이스 -->
<!-- H2 데이터베이스에 접속하기 위해 H2 라이브러리 지정 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2db.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
객체 매핑 시작
- 회원 테이블 생성
-- member.sql
CREATE TABLE MEMBER (
ID VARCHAR(255) NOT NULL, --아이디(기본 키)
NAME VARCHAR(255), --이름
AGE INTEGER NOT NULL, --나이
PRIMARY KEY (ID)
)
- 회원 클래스 생성
// Member.java
@Entity
@Table(name="MEMBER")
public class Member {
@Id
@Column(name = "ID")
private String id; // 아이디
@Column(name = "NAME")
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를 사용하기 위해 매핑 정보를 참고하여 회원 클래스와 회원 테이블을 매핑
매핑 정보를 표시하는 어노테이션을 추가해, JPA는 매핑 정보 덕분에 어떤 엔티티를 어떤 테이블에 저장해야 하는지 알 수 있음
매핑 정보 회원 객체 회원 테이블 클래스와 테이블 Member MEMBER 기본 키 id ID 필드와 컬럼 username NAME 필드와 컬럼 age AGE
// Member.java
import javax.persistence.*; // JPA 어노테이션의 패키지
/* @Entity
클래스를 테이블과 매핑한다고 JPA에게 알려주며,
@Entity가 사용된 클래스를 엔티티 클래스라고 함 */
@Entity
/* @Table
엔티티 클래스에 매핑할 테이블 정보를 알려주며,
여기서는 name 속성을 사용해 Member 엔티티를 MEMBER 테이블에 매핑
생략시 클래스 이름을 테이블 이름으로 매핑 */
@Table(name="MEMBER")
public class Member {
/* @Id
엔티티 클래스의 필드를 테이블의 기본 키에 매핑하며,
여기서는 엔티티의 id 필드를 테이블의 ID 기본 키 컬럼에 매핑
@Id가 사용된 필드는 식별자 필드라고 함 */
@Id
@Column(name = "ID")
private String id; // 아이디
/* @Column
필드를 컬럼에 매핑하며,
여기서는 name 속성을 사용해서
Member 엔티티의 username 필드를 MEMBER 테이블의 NAME 컬럼에 매핑 */
@Column(name = "NAME")
private String username; // 이름
/* 매핑 정보가 없는 필드
age 필드에는 매핑 어노테이션이 없으므로 (생략되어 있음)
필드명을 사용해서 컬럼명으로 매핑하며,
여기서는 필드명이 age이므로 AGE 컬럼으로 매핑 */
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;
}
}
persistence.xml 설정
- JPA는 persistence.xml을 사용해서 필요한 설정 정보를 관리
설정 파일이 META-INF/persistence.xml 클래스 패스 경로에 있으면 별도의 설정 없이 JPA가 인식할 수 있음
<!-- persistence.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- XML 네임스페이스와 사용할 버전을 지정 -->
<!-- JPA 2.1을 사용 -->
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">
<!-- JPA 설정은 영속성 유닛으로 시작하며 일반적으로 연결할 데이터베이스당 하나의 영속성 유닛을 등록 -->
<!-- 영속성 유닛에는 고유한 이름을 부여해야 하는데 여기서는 jpabook이라는 이름을 사용 -->
<persistence-unit name="jpabook">
<properties>
<!-- 필수 속성 -->
<!-- javax.persistence로 시작하는 속성은 JPA 표준 속성으로 특정 구현체에 종속되지 않음 -->
<!-- JPA 표준 속성 - JDBC 드라이버 -->
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<!-- JPA 표준 속성 - 데이터베이스 접속 아이디 -->
<property name="javax.persistence.jdbc.user" value="sa"/>
<!-- JPA 표준 속성 - 데이터베이스 접속 비밀번호 -->
<property name="javax.persistence.jdbc.password" value=""/>
<!-- JPA 표준 속성 - 데이터베이스 접속 URL -->
<property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
<!-- hibernate로 시작하는 속성은 하이버네이트 전용 속성이므로 하이버네이트에서만 사용 가능 -->
<!-- 하이버네이트 속성 - 데이터베이스 방언(Dialect) 설정 : H2 데이터베이스 사용 -->
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
<!-- 옵션 -->
<!-- 하이버네이트가 실행한 SQL을 출력 -->
<property name="hibernate.show_sql" value="true" />
<!-- 하이버네이트가 실행한 SQL을 출력할 때 보기 쉽게 정렬 -->
<property name="hibernate.format_sql" value="true" />
<!-- 쿼리를 출력할 때 주석도 함께 출력 -->
<property name="hibernate.use_sql_comments" value="true" />
<!-- JPA 표준에 맞춘 새로운 키 생성 전략을 사용 -->
<property name="hibernate.id.new_generator_mappings" value="true" />
<!--<property name="hibernate.hbm2ddl.auto" value="create" />-->
</properties>
</persistence-unit>
</persistence>
- JPA는 특정 데이터베이스에 종속적이지 않은 기술이므로 다른 데이터베이스로 손쉽게 교체할 수 있음
다만 각 데이터베이스가 제공하는 SQL 문법과 함수가 조금식 다르다는 문제점이 존재 - 데이터 타입 : 가변 문자 타입으로 MySQL은 VARCHAR, 오라클은 VARCHAR2를 사용
다른 함수명 : 문자열을 자르는 함수로 SQL 표준은 SUBSTRING()을 사용하지만 오라클은 SUBSTR()을 사용
페이징 처리 : MySQL은 LIMIT을 사용하지만 오라클은 ROWNUM을 사용 - 이처럼 SQL 표준을 지키지 않거나 특정 데이터베이스만의 고유한 기능을 JPA에서는 방언이라고 하며,
개발자가 특정 데이터베이스에 종속되는 기능을 적게 사용하여 교체하기 쉽도록 다양한 데이터베이스 방언 클래스를 제공하므로
persistence.xml에서 가장 중요한 속성은 데이터베이스 방언을 설정하는 hibernate.dialect - 이로 인해 개발자는 JPA가 제공하는 표준 문법에 맞추어 JPA를 사용하면 되고,
특정 데이터베이스에 의존적인 SQL은 데이터베이스 방언이 처리해 주므로
데이터베이스가 변경되어도 코드의 변경 없이 데이터베이스 방언만 교체하면 됨 - 하이버네이트는 다양한 데이터베이스 방언을 제공
H2 : org.hibernate.dialect.H2Diaclect
오라클 10g : org.hibernate.dialect.Oracle10gDialect
MySQL : org.hibernate.dialect.MySQL5InnoDBDiaclect
애플리케이션 개발
- 객체 매핑 완료 후 persistence.xml로 JPA 설정도 완료했으므로 애플리케이션을 시작하는 코드로 JPA 애플리케이션 개발
// JpaMain.java
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(); // 트랜잭션 기능 획득 (트랜잭션 API)
try {
tx.begin(); // 트랜잭션 시작
logic(em); // 비즈니스 로직 실행
tx.commit();// 트랜잭션 커밋
} catch (Exception e) {
e.printStackTrace();
tx.rollback(); // 예외 발생 시 트랜잭션 롤백
} finally {
em.close(); // 엔티티 매니저 종료
}
emf.close(); // 엔티티 매니저 팩토리 종료
}
// 비즈니스 로직
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);
}
}
- 코드는 크게 3부분으로 나뉨
1. 엔티티 매니저 설정
2. 트랜잭션 관리
3. 비즈니스 로직
1. 엔티티 매니저 설정
엔티티 매니저 팩토리 생성
/* JPA를 시작하려면 persistence.xml의 설정 정보를 사용해서 엔티티 매니저 팩토리를 생성
Persistence 클래스를 사용해 엔티티 매니저 팩토리를 생성해서 JPA를 사용할 수 있게 준비
persistence.xml의 설정 정보를 읽어서 JPA를 동작시키기 위한 기반 객체를 만들고
JPA 구현체에 따라 데이터베이스 커넥션 풀도 생성하므로 비용이 아주 커 전체에 한 번만 생성하고 공유해서 사용
META-INT/persistence.xml에서 이름이 jpabook인 영속성 유닛을 찾아서 엔티티 매니저 팩토리 생성 */
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook");
엔티티 매니저 생성
/* 엔티티 매니저 팩토리에서 엔티티 매니저를 생성하며 JPA의 기능 대부분은 엔티티 매니저가 제공
대표적으로 엔티티 매니저를 사용해서 엔티티를 데이터베이스에 등록/수정/삭제/조회 가능
엔티티 매니저는 내부에서 데이터소스(커넥션)를 유지하면서 데이터베이스와 통신
엔티티 매니저는 데이터베이스 커넥션과 밀접한 관계이므로 스레드 간에 공유하거나 재사용을 하면 안 됨 */
EntityManager em = emf.createEntityManager();
종료
/* 마지막으로 사용이 끝난 엔티티 매니저는 반드시 종료해야 함 */
em.close();
/* 애플리케이션을 종료할 때 엔티티 매니저 팩토리도 종료해야 함 */
emf.close();
2. 트랜잭션 관리
/* JPA를 사용하면 항상 트랜잭션 안에서 데이터를 변경해야 하며, 트랜잭션 없이 데이터를 변경하면 예외가 발생
트랜잭션을 시작하려면 엔티티 매니저에서 트랜잭션 API를 받아와야 함
트랜잭션 API를 사용해서 비즈니스 로직이 정상 동작하면 트랜잭션을 커밋하고 예외가 발생하면 트랜잭션을 롤백 */
EntityTransaction tx = em.getTransaction(); // 트랜잭션 기능 획득 (트랜잭션 API)
try {
tx.begin(); // 트랜잭션 시작
logic(em); // 비즈니스 로직 실행
tx.commit(); // 트랜잭션 커밋
} catch (Exception e) {
e.printStackTrace();
tx.rollback(); // 예외 발생 시 트랜잭션 롤백
}
3. 비즈니스 로직
회원 엔티티를 하나 생성한 다음 엔티티 매니저를 통해 데이터베이스에 등록, 수정, 삭제, 조회를 함
// 비즈니스 로직
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);
}
등록
/* 엔티티 매니저와 persist() 메소드에 저장할 엔티티를 넘겨줌
JPA는 회원 엔티티의 매핑 정보를 분석해 SQL을 만들어 데이터베이스에 전달
INSERT INTO MEMBER (ID, NAME, AGE) VALUES ('id1', '지한', 2) */
em.persist(member);
수정
/* JPA는 어떤 엔티티가 변경되었는지 추적 기능을 갖추고 있으므로
엔티티의 값만 변경하면 UPDATE SQL을 생성해서 데이터베이스에 값을 변경
UPDATE MEMBER
SET AGE=20, NAME='지한'
WHERE ID='id1' */
member.setAge(20);
삭제
/* 엔티티를 삭제하려면 엔티티 매니저의 remove() 메소드에 삭제하려는 엔티티를 넘겨줌
JPA는 DELETE SQL을 생성해서 실행
DELETE FROM MEMBER WHERE ID = 'id1' */
em.remove(member);
한 건 조회
/* find() 메소드는 조회할 엔티티 타입과 @Id로
데이터베이스 테이블의 기본 키와 매핑한 식별자 값으로 엔티티 하나를 조회
이 메소드를 호출하면 SELECT SQL을 생성해서 데이터베이스에 결과를 조회하고
결과 값으로 엔티티를 생성해서 반환
SELECT * FROM MEMBER WHERE ID='id1' */
Member findMember = em.find(Member.class, id);
System.out.println("findMember=" + findMember.getUsername() + ", age=" + findMember.getAge());
하나 이상의 회원 목록 조회
/* 데이터베이스 테이블을 대상으로 쿼리하는 SQL과 달리, 엔티티 객체를 대상으로 검색하는 JPA에서는
엔티티 객체(클래스와 필드)를 대상으로 검색하기 위해 검색 조건이 포함된 SQL인 JPQL 쿼리 언어 사용
JPA는 SQL을 추상화하는 JPQL이라는 객체지향 쿼리 언어 제공
MEMBER 테이블이 아닌, 회원 엔티티 객체로부터 검색
JPQL을 사용하려면 먼저 em.createQuery(JPQL, 반환 타입) 메소드를 실행해서 쿼리 객체를 생성한 후
쿼리 객체의 getResultList() 메소드를 호출
JPA는 JPQL을 분석해서 적절한 SQL을 만들어 데이터베이스에서 데이터를 조회
SELECT M.ID, M.NAME, M.AGE FROM MEMBER M */
List<Member> members = em.createQuery("select m from Member m", Member.class).getResultList();
System.out.println("members.size=" + members.size());
'Java-Spring > 자바 ORM 표준 JPA 프로그래밍' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍] 연관관계 매핑 기초 (0) | 2022.03.29 |
---|---|
[자바 ORM 표준 JPA 프로그래밍] 엔티티 매핑 - 실전 예제 (0) | 2022.03.26 |
[자바 ORM 표준 JPA 프로그래밍] 엔티티 매핑 (0) | 2022.03.23 |
[자바 ORM 표준 JPA 프로그래밍] 영속성 관리 (0) | 2022.03.20 |
[자바 ORM 표준 JPA 프로그래밍] JPA 소개 (0) | 2022.03.13 |