JPA 시작
객체 매핑
회원 테이블 생성
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, Constructor 등등..
}
JPA를 사용하려면 회원 클래스와 테이블을 매핑해야 한다.
매핑 정보
매핑 정보 | 회원 객체 | 회원 테이블 |
클래스와 테이블 | Member | MEMBER |
기본 키 | id | ID |
필드와 컬럼 | username | NAME |
필드와 컬럼 | age | AGE |
JPA가 제공하는 매핑 어노테이션 추가하기
@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, Constructor 등등..
}
회원 클래스에 매핑 정보를 표시하는 어노테이션 몇 개를 추가했다.
@Entity, @Table, @Column이 매핑 정보다.
어노테이션 | 설명 |
@Entity | 이 클래스를 테이블과 매핑한다고 JPA에게 알려준다. @Entity가 사용된 클래스를 엔티티 클래스라고 한다. |
@Table | 엔티티 클래스에 매핑할 테이블 정보를 알려준다. name 속성을 사용해 Member 엔터티를 MEMBER 테이블에 매핑했다. 어노테이션을 생략하면 클래스 이름을 테이블 이름으로 매핑한다. |
@ID | 엔티티 클래스의 필드를 테이블의 기본 키(PK)에 매핑한다. |
@Column | 필드를 컬럼에 매핑한다. name 속성을 사용해서 username 필드를 NAME 컬럼에 매핑했다. |
매핑 정보 없는 필드 | 매핑 어노테이션을 생략하면 필드명을 사용해서 컬럼명으로 매핑했다. |
persistence.xml 설정
JPA는 persistence.xml을 사용해서 필요한 설정 정보를 관리한다. 이 설정 팡리이 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>
JPA 표준 속성
- javax.persistence.jdbc.driver : JDBC 드라이버
- javax.persistence.jdbc.user : 데이터베이스 접속 아이디
- javax.persistence.jdbc.password : 데이터베이스 접속 비밀번호
- javax.persistence.jdbc.url : 데이터베이스 접속 URL
하이버네이트 속성
- hibernate.dialect : 데이터베이스 방언 설정
- 데이터베이스 방언
JPA는 데이터베이스에 종속적이지 않은 기술이다.
따라서 다른 데이터베이스로 손쉽게 교체 가능하다.
각 데이터베이스가 제공하는 SQL문법과 함수가 조금씩 다르다는 문제점이 있다.
- 데이터 타입 : MySQL은 VARCHAR, 오라클은 VARCHAR2
- 다른 함수명 : SQL 표준은 SUBSTRING(), 오라클은 SUBSTR()
- 페이징 처리 : MySQL은 LIMIT, 오라클은 ROWNUM
SQL 표준을 지키지 않거나 특정 데이터베이스만의 고유한 기능을 JPA에서는 방언이라 한다.
특정 데이터베이스에 종속되는 기능을 많이 사용하면 나중에 데이터베이스 교체가 어렵다.
하이버네이트를 포함한 대부분 JPA 구현체들은 다양한 방언 클래스를 제공한다.
- 하이버네이트 전용 속성
- hibernate.show_sql : 하이버네이트가 실행한 SQL을 출력한다.
- hibernate.format_sql: 하이버네이트가 실행한 SQL을 출력할 때 보기 쉽게 정렬한다.
- hibernate.use_sql_commnets: 쿼리를 출력할 때 주석도 함께 출력한다.
- hibernate.id.new_generator_mappings: JPA 표준에 맞춘 새로운 키 생성 전략을 사용한다.
- ddl-auto
jpa.hibernate.ddl-auto: create #create-drop, update, validate, none
create | Session factory가 실행될 때 스키마를 지우고 다시 생성. 클래스패스에 import.sql 이 존재하면 찾아서, 해당 SQL도 함께 실행 |
create-drop | create와 같지만 session factory가 내려갈 때 스키마 삭제. |
update | 시작시, 도메인과 스키마 비교하여 필요한 컬럼 추가 등의 작업 실행. 데이터는 삭제하지 않음. |
validate | Session factory 실행시 스키마가 적합한지 검사함. 문제가 있으면 예외 발생. |
로컬, 개발할 때는 create
운영시에는 auto 설정을 빼거나 validate 정도..
update로 둘 경우 스키마들이 꼬여서 결국 drop해야 한다.
애플리케이션 개발
impoert javax.persistence.*;
import java.util.List;
public class JpaMain {
public static void main(String[] args) {
//엔티티 매니저 팩토리 생성
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa");
//엔티티 매니저 생성
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(); //엔티티 매니저 팩토리 종료
}
}
코드는 크게 3부분으로 나뉘어 있다
- 엔티티 매니저 설정
- 트랜잭션 관리
- 비즈니스 로직
- 엔티티 매니저 생성 과정
- 엔티티 매니저 팩토리 생성
JPA를 시작하려면 persistence.xml의 설정 정보를 사용해서 엔티티 매니저 팩토리를 생성 해야 한다. 이 떄 Persistence 클래스를 사용하는데 이 클래스는 엔티티 매니저 팩토리를 생성해서 JPA를 사용할 수 있게 준비한다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa");
META-INF/persistence.xml에서 이름이 jpa인 영속성 유닛 persistence-unit을 찾아서 엔티티 매니저 팩토리를 생성한다.
이때 persistence.xml의 설정 정보를 일겅서 JPA를 동작시키기 위한 기반 객체를 만들고 JPA 구현체에 따라서는 DB 커넥션 풀도 생성하므로 엔티티 매니저 팩토리를 생성하는 비용은 아주 크다.
엔티티 매니저 팩토리는 애플리케이션 전체에서 딱 한번만 생성하고 공유해서 사용해야 한다.
- 엔티티 매니저 생성
EntityManager em = emf.createEntityManager();
엔티티 매니저 팩토리에서 엔티티 매니저를 생성한다. JPA의 기능 대부분은 엔티티 매니저가 제공한다.
대표적으로 엔티티 매니저를 사용해서 DB에 CRUD를 할 수 있다.
내부에 데이터베이스 커넥션을 유지하면서 DB와 통신한다.
따라서 개발자는 엔티티 매니저를 가상의 데이터베이스로 생각 할 수 있다.
엔티티 매니저는 데이터베이스 커넥션과 밀접한 관계가 있으므로 스레드간에 공유하거나 재사용하면 안된다.
- 종료
em.close(); // 엔티티 매니저 종료
emf.close(); // 엔티티 매니저 팩토리 종료
마지막으로 사용이 끝난 엔티티 매니저와 애플리케이션을 종료할 때 엔티티 매니저 팩토리도 함께 종료해야 한다.
트랜잭션 관리
JPA를 사용하면 항상 트랜잭션 안에서 데이터를 변경해야 한다.
트랜잭션 없이 데이터를 변경하면 예외가 발생한다.
트랜잭션을 시작하려면 엔티티 매니저에서 트랜잭션 API를 받아와야 한다.
EntityTransaction tx = em.getTransction(); //트랜잭션 API
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
}
비즈니스 로직을 보면 CRUD 작업이 엔티티 매니저를 통해 수행된다.
엔티티 매니저는 객체를 저장하는 가상의 DB처럼 보인다.
- 등록
엔티티 매니저의 persiste() 메소드에 저장할 엔티티를 넘겨주면 된다.
JPA는 회원 엔티티의 매핑정보를 분석해 다음과 같은 SQL을 만들어 DB에 전달한다.
INSERT INTO MEMBER (ID, NAME, AGE) VALUES ('id1', '이름', 2);
- 수정
em.update() 같은 메소드는 없다.
JPA는 어떤 엔티티가 변경되었는지 추적하는 기능을 갖추고 있다.
UPDATE MEMBER
SET AGE=20
WHERE ID='id1';
- 삭제
엔티티를 삭제하려면 엔티티 매니저의 remove() 메소드에 삭제하려는 엔티티를 넘겨준다.
JPA는 DELETE 쿼리를 생성한다.
DELETE FROM MEMBER WHERE ID = 'id1';
- 조회
find() 메소드는 조회할 엔티티 타입과 @ID로 데이터베이스 테이블의 기본 키와 매핑한 식별자 값으로 엔티티 하나를 조회하는 가장 단순한 조회 메소드이다.
SELECT * FROM MEMBER WHERE ID = 'id1';
JPQL
하나 이상의 회원 목록을 조회하는 코드를 살펴보자
//목록 조회
TypeQuery<Member> query = em.createQueary("select m from Member m", Member.class);
List<Member> members = query.getResultList();
JPA를 사용하면 엔티티 객체를 중심으로 개발하고 데이터베이스에 대한 처리는 JPA에 맡겨야 한다.
문제는 검색 쿼리다. JPA는 엔티티 객체를 중심으로 개발하므로 검색 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색해야 한다.
근데 테이블이 아닌 엔티티 객체를 대상으로 검색하려면 DB의 모든 데이터를 애플리케이션으로 불러와서 엔티티 객체로 변경한 다음 검색해야 하는데, 필요한 데이터만 불러오려면 결국 조건이 포함된 SQL을 사용해야 한다.
JPA는 JPQL 쿼리 언어로 해결한다.
JPA는 SQL을 추상화한 JPQL이라는 객체지향 쿼리언어를 제공한다.
SQL과 문법이 거의 유사해서 SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN등을 사용할 수 있다.
둘의 가장 큰 차이점은
- JPQL은 엔티티 객체를 대상으로 쿼리한다.
- SQL은 데이터베이스 테이블을 대상으로 쿼리한다.
조회에서 select m from Member m이 JPQL인데 이것은 회원 엔티티 객체를 말한다. MEMBER 테이블이 아니다.
JPQL은 데이터베이스 테이블을 전혀 알지 못한다.
JPQL을 사용하려면 em.createQuery( JPQL, 반환 타입) 메소드를 실행해서 쿼리 객체를 생성한 후
쿼리 객체의 getResultList() 메소드를 호출하면 된다.
'JPA' 카테고리의 다른 글
JPA 연관관계 매핑 기초 (0) | 2024.06.25 |
---|---|
JPA 엔티티 매핑 (0) | 2024.06.23 |
JPA 영속성 관리 (0) | 2024.06.20 |
JPA(Java Persistent API) 소개 (0) | 2024.05.11 |