티스토리 뷰

Java로 객체지향 프로그래밍을 하기위해서 도메인을 정의하고 객체간의 협력을 구성한다.

그러나 일반적인 프로젝트에는 데이터를 영속적으로 관리하기 위해 외부 스토리지에 대한 의존성을 가지게 된다.

객체 모델과 데이터베이스 모델은 서로 지향하는 패러다임이 다르기 때문에 이 간극에서 많은 비용과 불일치가 발생한다.

JPA는 이러한 간극을 좁혀주고 객체 중심의 설계를 유지할 수 있도록 도와준다.

 

JPA란?

JPA(Java Persistence API)는 Java의 ORM 기술 표준이다.

ORM 프레임워크는 객체와 테이블을 매핑하여 패러다임의 불일치를 해소해준다.

 

객체 매핑 시작

H2 Database

우선 H2 Console에 접속해서 위와 같이 Member 테이블을 생성한다.

 

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;

@Entity
@Table(name = "MEMBER")
@Getter
@Setter
public class Member {

    @Id
    @Column(name = "ID")
    private String id;

    @Column(name = "NAME")
    private String username;

    // mapping 정보가 없는 field
    private Integer age;
}

그리고 H2에 생성되어 있는 테이블과 매핑시킬 Member 클래스를 작성한다.

(2019년부터 javax.persistence → jakarta.persistence로 패키지명이 바뀌었다)

  • @Entity
    • 이 어노테이션이 붙은 클래스를 테이블과 매핑할 것임을 알린다
  • @Table
    • 엔티티 클래스에 매핑할 테이블 정보를 알려준다.
    • 이 어노테이션을 생략하면 클래스명과 같은 테이블과 매핑한다.
  • @Id
    • 이 어노테이션이 붙은 필드를 테이블의 기본 키에 매핑한다.
    • 이 어노테이션이 붙은 필드를 식별자 필드라고 한다.
  • @Column
    • 필드를 특정 이름을 가진 컬럼에 매핑한다.
  • 매핑 정보가 없는 필드
    • 매핑 어노테이션을 생략하면 자동으로 필드명과 동일한 컬럼명을 가진 컬럼에 매핑한다.

 

JPA 관련 설정

spring:
  datasource:
    driverClassName: org.h2.Driver
    url: jdbc:h2:~/test
    username: sa
    password:
  h2:
    console:
      enabled: true
      path: /h2-console
  jpa:
    properties:
      hibernate:
        dialect: org.hibernate.dialect.H2Dialect
        show_sql: true
        format_sql: true
        use_sql_comments: true
        id:
          new_generator_mappings: true

Spring에서 JPA를 사용할 때 application.yml과 같은 파일에서 설정을 관리한다.

  • datasource 설정값 : database 연결 정보
  • h2 설정값 : h2 console과 관련된 정보
  • jpa 설정값
    • dialect : 데이터베이스 방언
    • show_sql : 하이버네이트가 실행한 SQL 출력
    • format_sql : SQL 출력 시 보기 좋게 formatting
    • use_sql_comments : SQL 출력 시 주석도 포함
    • id.new_generator_mapping : 새로운 키 생성 전략 사용

 

데이터베이스 방언

JPA는 특정 데이터베이스에 종속되지 않기 위해 방언 클래스를 구현한다.

각 데이터베이스의 고유 문법이나 기능이 방언 클래스에 구현되고, 데이터베이스를 교체하는 경우 방언 클래스를 교체하여 쉽게 작업한다.

데이터베이스 방언

  • H2 : org.hibernate.dialect.H2Dialect
  • Oracle 10g : org.hibernate.dialect.Oracle10gDialect
  • MySQL : org.hibernate.dialect.MySQL5InnoDBDialect

 

예제

@PersistenceUnit
EntityManagerFactory emf;
  
@Test
void example() {
    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();
}

위 코드는 특정 비즈니스 로직을 처리하기 위해 EntityManager 클래스를 활용해 JPA 애플리케이션을 개발한 예제이다.

 

EntityManager 설정

EntityManager는 전역적으로 선언된 EntityManagerFactory를 통해 생성할 수 있다.

기본적으로 EntityManagerFactory는 @PersistenceUnit 어노테이션으로 주입받는다.

그리고 EntityManagerFactory의 createEntityManager() 메서드로 EntityManager를 생성한다.

 

EntityManager는 JPA의 대부분의 기능을 지원한다.

그러나 EntityManager는 DB 커넥션을 핸들링하고 있으므로 여러 스레드에서 공유되면 동시성 이슈가 발생할 수 있다.

따라서 EntityManager는 Singleton으로 사용되면 안되기 때문에 @Autowired 대신 @PersistenceContext으로 주입받아야한다.

EntityManager 사용구조 다이어그램

EntityManagerFactory가 생성되는 시점에 커넥션풀이 같이 생성된다.

EntityManager는 커넥션 풀에서 커넥션을 얻어서 데이터베이스에 접근한다.

이 때 EntityManager는 커넥션이 필요한 시점이 되기 전까지 커넥션을 얻지 않는다.

마지막으로 모두 사용된 EntityManagerFactory와 EntityManager는 꼭 close 해주어야 한다.

 

트랜잭션 관리

JPA를 사용하려면 항상 트랜잭션 안에서 데이터를 변경해야한다. 그렇지 않으면 예외가 발생한다.

 

비즈니스 로직

private 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);
}

EntityManager를 활용하여 실제로 데이터를 핸들링하는 비즈니스 로직이다.

 

위의 출력결과는 아래와 같다.

em.persist(member); → insert 문

member.setAge(20); → update 문

em.find(Member.class, id); → 단건 조회 (SQL 발생하지 않음)

em.createQuery(...); → select 문 (목록 조회)

em.remove(member); → delete 문

 

위의 예시에서 주의깊게 봐야할 부분은 아래와 같다.

  • setter를 이용한 update
    • JPA에서는 어떤 엔티티가 변경되었는지 추적할 수 있는 기능이 있다.
    • 단순 setter method만 호출하더라도 member 인스턴스가 수정된 것을 감지하고 update 문을 생성해서 값을 변경한다.
  • 단 건 조회
    • 우리가 insert한 Member는 영속성 컨텍스트 내부에 저장되어 있다.
    • 영속성 컨텍스트에 존재하는 객체를 조회하는 경우, 데이터베이스에서 조회하지 않고 비용이 적은 영속성 컨텍스트를 조회하여 객체를 반환한다. (쿼리가 나가지 않는다)
  • 목록 조회
    • 목록 조회하려는 조건이 EntityManager 내부에 있는 데이터가 전부라는 보장이 없다.
    • 따라서 데이터베이스를 조회해온다.
    • JPQL을 사용하여 필요한 데이터만 조회할 수 있도록 한다.
      • 엔티티를 조회하기 위한 쿼리 (클래스와 필드 대상)
      • 만들어진 JPQL → SQL로 만들어 데이터베이스 조회

 

다음 글: https://gojs.tistory.com/44

 

JPA 영속성 관리

영속성 컨텍스트란?영속성 컨텍스트란 엔티티를 영구 저장하는 환경이다.EntityManager로 엔티티를 저장하거나 조회하면 영속성 컨텍스트에 저장되고 조회된다. em.persist(member);이러한 코드를 실행

gojs.tistory.com

'공부 > JPA' 카테고리의 다른 글

JPA 프록시와 로딩 전략  (0) 2024.07.27
JPA Entity 간 연관관계 매핑 (2)  (0) 2024.07.27
JPA Entity 간 연관관계 매핑 (1)  (0) 2024.07.26
JPA Entity 구성하기  (0) 2024.07.24
JPA 영속성 관리 기법  (0) 2024.07.23
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/08   »
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 27 28 29 30
31
글 보관함