JPA 시작하기
- 김영한님의 자바 ORM 표준 JPA 프로그래밍 기본편을 통해 JPA의 기본 개념, 프로젝트 환경 설정, 엔티티 매핑, 그리고 JPQL을 활용한 데이터 조회 방법을 정리함
프로젝트 환경 설정
의존성 설정
-
pom.xml 핵심 의존성
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
<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> </dependencies>
-
의존성 설명
hibernate-entitymanager- JPA 구현체인 Hibernate를 포함함
h2- 경량 데이터베이스로 학습 및 테스트 환경에 적합함
H2 데이터베이스
H2 데이터베이스 소개
- 주요 특징
- 매우 가볍고 빠름 (용량 약 1.5MB)
- 웹용 쿼리 도구를 제공함
- MySQL, Oracle 데이터베이스 시뮬레이션 기능 지원
- 시퀀스,
AUTO_INCREMENT기능 지원
- 다운로드
- http://www.h2database.com/
H2 데이터베이스 실행
-
실행 방법
1 2 3 4 5 6
# Windows h2.bat # Mac/Linux chmod 755 h2.sh ./h2.sh
-
접속 정보
- JDBC URL
jdbc:h2:tcp://localhost/~/test
- 사용자명
sa
- 비밀번호
- (없음)
- JDBC URL
테이블 생성
-
Member 테이블 DDL
1 2 3 4 5
CREATE TABLE Member ( id BIGINT NOT NULL, name VARCHAR(255), PRIMARY KEY (id) );
JPA 설정
persistence.xml 파일 위치
-
파일 경로 (필수)
1 2 3 4 5
src/ └── main/ └── resources/ └── META-INF/ └── persistence.xml ← 반드시 이 경로에 위치 -
JPA는META-INF/persistence.xml경로에서 설정 파일을 자동으로 인식함
persistence.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 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="hello"> <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"/> </properties> </persistence-unit> </persistence>
필수 속성 설명
- JDBC 드라이버 설정
javax.persistence.jdbc.driver- 데이터베이스 드라이버 클래스 지정 (H2는
org.h2.Driver)
- 데이터베이스 드라이버 클래스 지정 (H2는
- 데이터베이스 접속 정보
javax.persistence.jdbc.user- DB 사용자명
javax.persistence.jdbc.password- DB 비밀번호
javax.persistence.jdbc.url- JDBC URL
- 데이터베이스 방언
hibernate.dialect- 특정 데이터베이스에 맞는 SQL을 생성하도록 지정함
옵션 속성 설명
- SQL 로깅 옵션
hibernate.show_sql- 실행되는 SQL을 콘솔에 출력함
hibernate.format_sql- SQL을 보기 좋게 포맷팅하여 출력함
hibernate.use_sql_comments- 쿼리에 주석을 추가하여 어떤 작업인지 표시함
데이터베이스 방언 (Dialect)
- 방언이란?
- JPA는 특정 데이터베이스에 종속되지 않는 표준 기술임
- 각 데이터베이스마다 SQL 문법과 함수가 조금씩 다름
- 방언(Dialect)은 JPA가 사용하는 SQL을 특정 데이터베이스에 맞게 변환해주는 역할을 함
-
데이터베이스별 차이점 예시
기능 MySQL Oracle H2 가변 문자 타입 VARCHARVARCHAR2VARCHAR문자열 자르기 SUBSTRING()SUBSTR()SUBSTRING()페이징 LIMITROWNUMLIMIT - 주요 방언 클래스
- H2
org.hibernate.dialect.H2Dialect
- Oracle 10g
org.hibernate.dialect.Oracle10gDialect
- MySQL 5 InnoDB
org.hibernate.dialect.MySQL5InnoDBDialect
- PostgreSQL
org.hibernate.dialect.PostgreSQL95Dialect
- H2
- Hibernate는 40가지 이상의 데이터베이스 방언을 지원함
애플리케이션 개발
JPA 구동 방식
- 설정 정보 조회
Persistence클래스가META-INF/persistence.xml파일을 읽음
- EntityManagerFactory 생성
- 애플리케이션 전체에서 하나만 생성하여 공유함
- EntityManager 생성
- 요청마다 생성하며 쓰레드 간 공유하지 않음
- 트랜잭션 시작 및 비즈니스 로직 실행
- 모든 데이터 변경은 트랜잭션 내에서 수행됨
- 트랜잭션 커밋 또는 롤백
- EntityManager 종료
- EntityManagerFactory 종료
엔티티 클래스 생성
-
Member 엔티티
1 2 3 4 5 6 7 8 9 10 11 12 13
@Entity // JPA가 관리하는 엔티티 public class Member { @Id // 기본 키 매핑 private Long id; private String name; // 기본 생성자 필수 (JPA 스펙) public Member() { } // Getter, Setter }
-
주요 애노테이션
@Entity- JPA가 관리하는 객체임을 나타내며 테이블과 매핑됨
@Id- 테이블의 Primary Key와 매핑됨
애플리케이션 기본 구조
-
JpaMain 클래스
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
public class JpaMain { public static void main(String[] args) { // EntityManagerFactory 생성 (애플리케이션 로딩 시점에 딱 한 번) EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); // EntityManager 생성 EntityManager em = emf.createEntityManager(); // 트랜잭션 시작 EntityTransaction tx = em.getTransaction(); tx.begin(); try { // 비즈니스 로직 (CRUD) tx.commit(); } catch (Exception e) { tx.rollback(); } finally { em.close(); } emf.close(); } }
회원 등록 (INSERT)
-
등록 코드
1 2 3 4 5 6
// 회원 등록 Member member = new Member(); member.setId(1L); member.setName("HelloA"); em.persist(member); // 영속화
- 반드시 트랜잭션 안에서 실행되어야 함
-
실행되는 SQL
1 2 3 4
Hibernate: /* insert hellojpa.Member */ insert into Member (name, id) values (?, ?)
회원 조회 (SELECT)
-
조회 코드
1 2 3 4
// 회원 조회 Member findMember = em.find(Member.class, 1L); System.out.println("findMember.id = " + findMember.getId()); System.out.println("findMember.name = " + findMember.getName());
-
실행되는 SQL
1 2 3 4 5 6 7 8
Hibernate: select member0_.id as id1_0_0_, member0_.name as name2_0_0_ from Member member0_ where member0_.id=?
회원 수정 (UPDATE)
-
수정 코드
1 2 3 4 5 6 7 8
// 회원 조회 Member findMember = em.find(Member.class, 1L); // 회원 수정 (변경 감지) findMember.setName("HelloJPA"); // em.persist(findMember) 불필요 // 트랜잭션 커밋 시점에 자동으로 UPDATE SQL 실행
- JPA는 트랜잭션 커밋 시점에 엔티티의 변경을 감지하여
UPDATE SQL을 자동 생성함 - 변경 감지가 동작하려면 반드시 트랜잭션 안에서 실행되어야 함
- JPA는 트랜잭션 커밋 시점에 엔티티의 변경을 감지하여
-
실행되는 SQL
1 2 3 4 5
Hibernate: /* update hellojpa.Member */ update Member set name=? where id=?
회원 삭제 (DELETE)
-
삭제 코드
1 2 3 4 5
// 회원 조회 Member findMember = em.find(Member.class, 1L); // 회원 삭제 em.remove(findMember);
- 반드시 트랜잭션 안에서 실행되어야 함
-
실행되는 SQL
1 2 3 4
Hibernate: /* delete hellojpa.Member */ delete from Member where id=?
JPA 주의사항
- EntityManagerFactory
- 애플리케이션 전체에서 하나만 생성하여 공유해야 함
- 생성 비용이 크므로 애플리케이션 로딩 시점에 딱 한 번만 생성함
- EntityManager
- 쓰레드 간 공유를 절대 금지함 (동시성 문제 발생)
- 요청마다 생성하고 사용 후 반드시 종료해야 함
- 트랜잭션
- JPA의 모든 데이터 변경은 트랜잭션 안에서만 실행해야 함
- 조회는 트랜잭션 없이도 가능함
JPQL
JPQL이 필요한 이유
- 기본 조회 방법의 한계
em.find()메서드는 PK 기반 단건 조회만 가능함- 복잡한 검색 조건 (예: “나이가 18살 이상인 회원을 모두 검색”)이 필요한 경우 해결 불가능함
JPQL 소개
- JPQL (Java Persistence Query Language)
- 엔티티 객체를 대상으로 검색하는 객체지향 쿼리 언어
- SQL과 문법이 유사하지만 테이블이 아닌 엔티티 객체를 대상으로 쿼리를 작성함
- 실행 시 SQL로 변환되어 데이터베이스에 전달됨
JPQL과 SQL 비교
| 특징 | JPQL | SQL |
|---|---|---|
| 대상 | 엔티티 객체 | 테이블 |
| 문법 | 객체 지향 | 관계형 |
| DB 독립성 | 독립적 | 종속적 |
| 추상화 수준 | 추상화된 쿼리 | 구체적 쿼리 |
JPQL 기본 예제
-
전체 회원 조회
1 2 3 4 5 6 7 8 9
// JPQL로 전체 회원 조회 List<Member> result = em.createQuery( "select m from Member m", // 엔티티 객체 대상 Member.class ).getResultList(); for (Member member : result) { System.out.println("member.name = " + member.getName()); }
-
실행되는 SQL
1 2 3 4 5 6 7
Hibernate: /* select m from Member m */ select member0_.id as id1_0_, member0_.name as name2_0_ from Member member0_
조건 검색
-
WHERE 절 사용
1 2 3 4 5
// ID가 2 이상인 회원만 검색 List<Member> result = em.createQuery( "select m from Member m where m.id > 2", Member.class ).getResultList();
파라미터 바인딩
-
이름 기준 파라미터 바인딩
1 2 3 4 5 6 7
// 이름이 일치하는 회원 검색 List<Member> result = em.createQuery( "select m from Member m where m.name = :name", Member.class ) .setParameter("name", "HelloA") .getResultList();
- 파라미터 바인딩을 사용하면 SQL 인젝션 공격을 방지하고 쿼리 재사용이 가능함
페이징
-
페이징 처리
1 2 3 4 5 6 7 8
// 페이징: 1번부터 10개 조회 List<Member> result = em.createQuery( "select m from Member m order by m.id desc", Member.class ) .setFirstResult(1) // 시작 위치 (0부터 시작) .setMaxResults(10) // 조회할 데이터 수 .getResultList();
-
실행되는 SQL (H2)
1 2 3 4 5 6 7 8
select member0_.id as id1_0_, member0_.name as name2_0_ from Member member0_ order by member0_.id desc limit ? offset ?
JPQL 문법
-
기본 문법
1 2 3 4 5 6 7 8
-- SELECT 문 SELECT m FROM Member m WHERE m.age > 18 -- UPDATE 문 UPDATE Member m SET m.age = 20 WHERE m.age < 18 -- DELETE 문 DELETE FROM Member m WHERE m.age < 18
-
주요 특징
- 엔티티와 속성은 대소문자를 구분함 (
Member,name) - JPQL 키워드는 대소문자를 구분하지 않음 (
SELECT,FROM,WHERE) - 엔티티 이름을 사용하며, 테이블 이름이 아님
- 별칭(alias) 사용이 필수임
- ex)
select m from Member m
- ex)
- 엔티티와 속성은 대소문자를 구분함 (
요약 정리
- JPA는 자바 ORM 표준으로, 객체와 관계형 데이터베이스 간의 매핑을 처리하고 SQL을 자동 생성하여 개발 생산성을 크게 향상시킴
- EntityManagerFactory는 애플리케이션 전체에서 하나만 생성하여 공유하며, EntityManager는 요청마다 생성하고 쓰레드 간 공유를 금지해야 함
- persistence.xml 파일을 통해 데이터베이스 연결 정보와 JPA 설정을 관리하며, 반드시
META-INF디렉토리에 위치해야 함 - 데이터베이스 방언(Dialect)을 사용하면 특정 데이터베이스에 종속되지 않고 다양한 데이터베이스를 지원할 수 있음
- JPA의 모든 데이터 변경 작업은 반드시 트랜잭션 안에서 실행되어야 하며, 변경 감지(Dirty Checking) 기능을 통해 별도의
update메서드 호출 없이 데이터 수정이 가능함 - JPQL은 엔티티 객체를 대상으로 하는 객체지향 쿼리 언어로, 복잡한 검색 조건이 필요한 경우 SQL을 직접 작성하지 않고도 데이터를 조회할 수 있음
em.persist()로 저장,em.find()로 조회, setter로 수정(변경 감지),em.remove()로 삭제하는 기본 CRUD 패턴을 숙지하는 것이 중요함