728x90
반응형
JPA를 사용하는 이유
- 초창기에는 JDBC API를 직접 데이터베이스에 연동하였음.
- 이후 iBatis(현재 MyBaits), JdbcTemplate 등인 SQL Mapper 기술을 활용하여 사용코드를 확연히 줄일 수 있었음.
- 그러나, 여전히 CRUD를 위한 SQL을 반복해서 작성해야 하는 과정은 여전히 존재하며, 비생산적 구조로 남아있음.
- 이를 개선하기 위해 고민하고 테이블 이름을 입력하면 CRUD SQL을 자동으로 생성해주는 도구에 객체 모델링 기능을 합친 것이 바로 JPA.
- 객체와 관계형 데이터베이스 간의 차이를 중간에서 해결해주는 ORM(Object Relational Mapping) ==> JPA(자바의 ORM 기술 표준)
- 반복적인 CRUD SQL 알아서 처리한다.
- 객체 모델링과 관계형 데이터베이스 사이의 차이점을 해결 -> 영속성 컨텍스트에서의 객체 운영 관리 방법
JPA와 Hibernate
- JPA : 자바 진영의 ORM 기술 표준, 데이터베이스 작업을 위한 규약을 정의한 인터페이스
- Hibernate : ORM 프레임워크, JPA의 구현체
SQL을 직접 다룰 때 발생하는 문제점
- 반복적인 코드 작성
- SQL에 의존적인 개발 : DAO를 사용해서 SQL을 숨겨도 DAO 직접 실행하여 SQl을 직접 수정해야 함.
- 이러한 것은 진정한 의미의 계층 분할이 아니라고 생각함 : 물리적으로 SQL과 JDBC API를 데이터 접근 계층에 숨기는 것을 가능할지라도 논리적으로는 엔티티와 아주 강한 의존관계를 가지고 있음.
JPA 특징
- 어노테이션을 이용한 매핑 설정
- XML 파일을 이용한 매핑 설정도 가능
- String, int, LocalDate 등 기본적인 타입에 대한 매핑 지원
- 커스텀 타입 변환기 지원
- 자신이 만든 타입을 DB 컬럼에 매핑 가능
- 밸류 타입 매핑 지원
- 한 개 이상 칼럼을 한 개 타입으로 매핑 가능
- 클래스 간 연관 지원
- 상속에 대한 매핑 지원
persistence.xml 설정
- JPA는 persistence.xml을 사용해서 필요한 설정 정보를 관리한다.
- 이 설정 파일이 META-INF/persistence.xml 클래스 패스 경로에 있으면 별도의 설정 없이 JPA가 인식
- 설정 파일은 persistence로 시작한다. 이곳에 XML 네임스페이스와 사용할 버전 지정
- JPA 설정은 영속성 유닛(persistence-unit)이라는 것부터 시작하는데 일반적으로 연결할 데이터베이스 당 하나의 영속성 유닛을 등록한다.
- 이름이 jakarta.persistence로 시작하는 속성은 JPA 표준 속성으로 특정 구현체에 종속되지 않는다.
- 반면 hibernate로 시작하는 속성은 하이버네티으 전용 속성이므로 하이버네이트에서만 사용 가능하다.
애플리케이션 개발
- 객체와 데이터베이스 매핑 User <=> jpabegin.user
- 영속성 컨텍스트 설정 : persistence.xml에 안에 User 클래스 등록
package jpabasic.reserve.domain;
import jakarta.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "user")
public class User {
@Id
private String email;
private String name;
@Column(name = "create_date")
private LocalDateTime createDate;
protected User() {}
public User(String email, String name, LocalDateTime createDate) {
this.email = email;
this.name = name;
this.createDate = createDate;
}
public String getEmail() {
return email;
}
public String getName() {
return name;
}
public LocalDateTime getCreateDate() {
return createDate;
}
public void changeName(String newName){
this.name = newName;
}
}
package main;
import jakarta.persistence.*;
import jpabasic.reserve.domain.User;
import java.time.LocalDateTime;
public class UserSaveMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabegin");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
try{
tx.begin();
User user = new User("minji@newjins.com", "minji", LocalDateTime.now());
em.persist(user);
tx.commit();
} catch (Exception e){
e.printStackTrace();
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
package main;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.Persistence;
import jpabasic.reserve.domain.User;
public class UserGetMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabegin");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
try{
tx.begin();
User user = em.find(User.class, "minji@newjins.com");
if(user == null){
System.out.println("minji 없음");
} else {
System.out.printf("minji가 팬사인회에 참석중입니다. email=%s, name=%s, createDate=%s\n", user.getEmail(), user.getName(), user.getCreateDate());
}
tx.commit();
} catch (Exception e){
e.printStackTrace();
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
package main;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.Persistence;
import jpabasic.reserve.domain.User;
public class UserUpdateMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabegin");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
User user = em.find(User.class, "minji@newjins.com");
if (user == null) {
System.out.println("minji 없음");
} else {
String newName = "minji" + "변경함";
user.changeName(newName);
System.out.printf("minji가 팬사인회에 참석중입니다. email=%s, name=%s, createDate=%s\n", user.getEmail(), user.getName(), user.getCreateDate());
}
tx.commit();
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
요약
- 기본 구조
- EntityManagerFactory 초기화
- DB 작업이 필요할 때마다
- EntityManager 생성
- EntityManager로 DB 조작
- EntityTransaction으로 트랜잭션 관리
- 하지만 스프링과 연동할 때는
- 대부분 스프링이 대신 처리하므로 매핑 설정 중심으로 작업
- 영속 컨텍스트
- 엔티티를 메모리에 보관
- 변경을 추적해서 트랜잭션 커밋 시점에 DB 반영
728x90
반응형
'JPA' 카테고리의 다른 글
Persistence Context - LifeCycle (0) | 2024.10.24 |
---|