JPA

JPA

Z00_HWAN_99 2024. 10. 24. 14:38
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