들어가며
이 포스팅의 내용은 «자바 ORM 표준 JPA 프로그래밍»을 참고하고 정리한 것입니다.
권장 환경은 다음과 같습니다.
- 자바 8 이상
- Maven 사용
- H2 데이터베이스 사용
- 인텔리제이 사용
Hello JPA 애플리케이션을 통해 간단히 CRUD 할 수 있는 서버 애플리케이션을 만들어보겠습니다.
라이브러리 추가 - 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>jpa-basic</groupId>
<artifactId>ex1-hello-jpa</artifactId>
<version>1.0.0</version>
<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>
</project>
JPA 설정하기 - persistence.xml
- JPA 설정 파일
- {프로젝트 디렉토리}/src/main/resources/META-INF/persistence.xml 위치함
- persistence-unit name으로 이름 지정
- javax.persistence로 시작: JPA 표준 속성
- hibernate로 시작: 하이버네이트 전용 속성
<?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">
<class>hellojap.Member</class>
<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=""/>
<!-- h2 접속할 때 사용한 jdbc url과 동일하게 만듬-->
<property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
<!-- h2 방언 사용 -->
<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.hbm2ddl.auto" value="create" />-->
</properties>
</persistence-unit>
</persistence>
persistence-unit
JPA 설정은 영속성 유닛(persistence-unit)으로 시작합니다. 영속성 유닛, 즉 persistence-unit은 연결된 하나의 데이터베이스당 하나의 영속성 유닛을 등록합니다.
JPA 표준속성
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”
JPA 표준 속성은 javax로 시작합니다. 위의 네 속성은 각각 드라이버, 유저명, 패스워드, db url입니다. JPA 표준 속성은 구현체와 관계없이 어디서든 사용할 수 있습니다.
하이버네이트 속성
property name=”hibernate.dialect” value=”org.hibernate.dialect.H2Dialect”
- hibernate로 시작하는 속성은 하이버네이트 전용 속성입니다. 이 속성은 하이버네이트에서만 사용할 수 있습니다.
- dialect는 JPA가 많은 데이터베이스를 지원해야 하는 문제 때문에 생긴 속성입니다.
- 현재는 H2 데이터베이스 방언에 맞게 hibernate가 SQL을 만들어 준다는 것입니다.
** 데이터베이스 방언
- JPA는 특정 데이터베이스에 종속되지 않습니다.
- 각각의 데이터베이스가 제공하는 SQL 문법과 함수는 조금씩 다름
- 가변 문자: MySQL은 VARCHAR, Oracle은 VARCHAR2
- 문자열을 자르는 함수: SQL 표준은 SUBSTRING(), Oracle은 SUBSTR()
- 페이징: MySQL은 LIMIT , Oracle은 ROWNUM
- 방언: SQL 표준을 지키지 않는 특정 데이터베이스만의 고유한 기능
- hibernate.dialect 속성에 지정
- H2 : org.hibernate.dialect.H2Dialect
- Oracle 10g : org.hibernate.dialect.Oracle10gDialect
- MySQL : org.hibernate.dialect.MySQL5InnoDBDialect
- 하이버네이트는 40가지 이상의 데이터베이스 방언 지원
** JPA 구동방식
- Persistence 클래스에서 persistence.xml을 참고해 설정정보를 조회합니다.
- 설정 정보를 기반으로 EntityManagerFactory를 생성하고
- EntityManagerFactory는 EntityManager를 생성합니다.
주의
1. 엔티티 매니저 팩토리는 하나만 생성해서 애플리케이션 전체에서 공유
2. 엔티티 매니저는 스레드 간에 공유하면 안 된다.(사용하고 버려야 한다)
3. JPA의 모든 데이터 변경은 트랜잭션 안에서 실행
회원 클래스 생성
@Entity
@Table(name = "MEMBER")
public class Member {
@Id
@Column(name = "ID")
private String id;
@Column(name = "NAME")
private String username;
private Integer age;
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;
}
}
어노테이션의 의미는 다음과 같습니다.
@Entity
이 클래스를 테이블과 매핑한다고 JPA에게 선언합니다. 엔티티 애노테이션이 사용된 클래스를 엔티티 클래스라고 합니다.
@Table
엔티티 클래스에 매핑할 테이블 정보를 알려줍니다. 여기서는 name 속성을 이용하여 이 클래스를 MEMBER 테이블과 매핑했습니다. 이 애노테이션을 생략하면 클래스 이름을 테이블 이름으로 매핑합니다.
@id
엔티티 클래스의 필드를 테이블의 Primary key로 매핑합니다.
@Column
필드를 칼럼에 매핑합니다. 만약 매핑 정보를 주지 않는다면(위의 예제에서는 age), 필드명을 이용하여 칼럼과 매핑합니다.
애플리케이션 개발
package jpabook.start;
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) {
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);
}
}
로직 구현 흐름은 다음과 같습니다.
엔티티 매니저 팩토리 생성 > 엔티티 매니저 생성 > 트렌잭션 획득 > 트렌잭션 시작 > 비즈니스 로직 시작 > 트렌잭션 커밋 또는 롤백 > 엔티티 매니저 종료 > 엔티티 매니저 팩토리 종료
엔티티 매니저 팩토리 생성
JPA를 시작하려면 우선 persistence.xml의 설정 정보를 보고 엔티티 매니저 팩토리를 생성해야 합니다. 이때 Persistence 클래스를 사용하는데 이 클래스는 엔티티 매니저 팩토리를 생성하여 JPA를 사용할 수 있게 해 줍니다.
즉 persistence.xml에서 설정한
<persistence-unit name="jpabook">
것을 보고,
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook")
엔티티 매니저 팩토리를 생성합니다.
엔티티 매니저 생성
EntityManger em = emf.createEntityManager();
- 엔티티 매니저 팩토리에서 엔티티 매니저를 생성한다. JPA의 기능 대부분은 이 엔티티 매니저가 제공한다. 대표적으로 엔티티 매니저를 사용해서 엔티티를 데이터베이스에 등록/수정/삭제/조회 할 수 있다.
- 엔티티 매니저는 내부에 데이터소스를(데이터베이스 컨넥션)를 유지하면서 데이터베이스와 통신한다.
- 따라서 애플리케이션 개발자는 엔티티 매니저를 가상의 데이터베이스로 생각할 수 있다.
- 엔티티 매니저는 데이터베이스 컨넥션과 밀접한 관계가 있음으로 스레드 간에 공유하거나 재사용하면 안된다.
종료
사용이 끝난 엔티티 매니저는 반드시 종료해야 한다.
em.close();
애플리케이션이 종료될 때 엔티티 매니저 팩토리도 종료해야 한다.
emf.close();
트렌잭션 관리
EntityTransaction tx = em.getTransaction();
try {
tx.begin(); // 트랜잭션 시작
logic(em); // 비즈니스 로직 실행
tx.commit(); // 트랜잭션 커밋
}catch (Exception e) {
tx.rollback(); // 예외발생시 트랜잭션 롤백
}
- JPA를 사용하면 항상 트랜잭션 안에서 데이터를 변경해야 한다.
- 트랜잭션 없이 데이터 베이스를 변경하면 예외가 발생한다. 트랜잭션을 시작하려면 엔티티 메니저 에서 트랜잭션 API를 받아와야 한다.
- 트랜잭션 API를 사용해서 비즈니스 로직이 정상 동작하면 트랜잭셔을 커밋하고 예외가 발생하면 롤백 한다.
비즈니스 로직
public static void logic(EntityManager em){
String id = "id";
Member emember = new Member();
member.setId(id);
member.setUsername("yun");
member.setAsge(28);
// 등록
em.persist(member);
// 수정
member.setAge(20);
// 한건 조회
Member findMember = em.find(Member.class, id);
// 목록 조회
List<Member> members = em.createQuery("select m from member m", Member.class);
// 삭제
em.remvoe(member);
}
비즈니스 로직을 보면, 트렌잭션 매니저를 가지고 CRUD를 하는 것을 알 수 있다.
엔티티 매니저에 객체를 등록하면, JPA는 객체의 매핑 정보를 분석하여 SQL을 생성한다.
위와 같은 코드에서는 다음과 같은 SQL을 만들 것이다.
INSERT INTO MEMBER (ID, NAME, AGE) VALUES (‘id1’, ‘지한’, 2);
- 테이블이 아닌 엔티티 객체를 대상으로 검색하려면 데이터베이스의 모든 데이터를 애플리케이션으로 불러와서 엔티티 객체로 변경한 다음 검색 해야하는데, 이는 사실상 불가능하다.
- 애플리케이션이 필요한 데이터만 데이터베이스에서 불러오려면 결국 검색 조건이 포함된 SQL을 사용해야한다. JPA는 JPQL 이라는 쿼리 언어로 이런 문제를 해결한다.
- JPQL은 엔티티 객체를 대상으로 쿼리한다. 쉽게 이야기해서 클래스와 필드를 대상으로 쿼리한다.
- SQL은 데이터베이스 테이블을 대상으로 쿼리한다.
- JPA는 JPQL을 분색해서 다음과 같은 적절한 SQL을 만들어 데이터베이스에서 데이터를 조회한다.
마무리
애플리케이션을 동작시키면 H2 데이터베이스에 데이터가 저장된 것을 확인할 수 있습니다.
'스프링 > Spring Data' 카테고리의 다른 글
JPA 소개 및 JPA의 기본 동작 과정 (0) | 2021.05.20 |
---|---|
SQL 중심적인 개발의 배경과 문제점 (0) | 2021.05.20 |