JPA - JPA와 Hibernate

KoK·2025년 6월 18일

JPA

목록 보기
2/8
post-thumbnail

JPA는 자바의 ORM기술이다.
ORM이란 Object-Relational Mapping 의 약자로 객체와 관계형 데이터베이스의 테이블을 매핑해주는 기술이다.

JPA에 대해서 알아보기 전에 먼저 ORM에 대해서 정리해보자

ORM이란?

보통 JAVA를 사용해서 프로그래밍을 할 때 객체를 사용해서 프로그래밍을 하는데, 이 객체를 관계형 데이터베이스 테이블에 1:1로 자동으로 매핑해준다.
즉, 객체를 RDB 테이블에 자동으로 영속화 해주는 기술이다.
영속화에 대해서는 이 글(수정하기)에서 자세히 다뤄보겠습니다.

장점

  • 생산성 향상
    SQL 대신 객체 중심으로 개발할 수 있어서 코드 작성이 간결해지고 빠르다. 반복적인 CRUD 코드를 자동 생성해 주어 개발 속도가 빨라진다.
  • DB 독립성
    특정 DB에 종속되지 않고 여러 종류의 데이터베이스로 쉽게 전환할 수 있다. DB 방언(Dialect) 기능으로 각 DB의 SQL 차이를 자동으로 처리한다.
  • 복잡한 관계 매핑 지원
    1:1, 1:N, N:M 등 복잡한 객체 간 연관관계를 직관적으로 표현하고 관리할 수 있다.
  • 캐시 및 성능 최적화 기능 제공
    1차 캐시와 2차 캐시를 활용해 데이터베이스 접근 횟수를 최소화함으로써 성능을 향상시킨다.

단점

  • 성능 오버헤드
    자동 매핑과 캐시, 지연 로딩 등의 기능 때문에 직접 SQL을 작성하는 것보다 성능이 떨어질 수 있다.
  • 복잡한 쿼리 작성 어려움
    복잡한 조인이나 통계 쿼리 같은 경우는 직접 SQL을 작성하는 것이 더 효율적일 수 있다. ORM 쿼리 언어는 한계가 있다.
  • 디버깅 어려움
    내부에서 자동으로 SQL이 생성되므로 문제가 생겼을 때 원인을 파악하기 어렵다.
  • N+1 문제 발생 가능성
    연관된 엔티티를 조회할 때 쿼리가 과도하게 많이 발생하는 N+1 문제가 자주 발생한다. 이 문제는 성능 저하를 심화시키고, ORM을 제대로 이해하고 최적화하지 않으면 심각한 병목이 될 수 있다.

JPA란?

JPA란, 자바에서 객체와 관계형 데이터베이스 간의 매핑을 도와주는 ORM 표준 인터페이스이다.
자바를 사용해 프로그래밍할 때, 객체를 데이터베이스 테이블과 자동으로 매핑해주는 중간 다리 역할을 하며, 개발자는 직접 SQL을 작성하는 수고를 줄이고 객체 중심으로 DB 작업을 수행할 수 있게 해준다.

JPA는 인터페이스이기 때문에 JPA를 구현체를 함께 사용해야 한다. 대표적으로는 Hibernate, EclipseLink, OpenJPA 가 있다.

Hibernate

Hibernate는 Java언어를 위한 대표적인 ORM 프레임워크이고, JPA 인터페이스를 구현한 구현체이다.

JPA의 특징, 장점을 그대로 가지고 개발자가 직접 사용할 수 있게 한다.

  • JPA 구현체
    JPA가 정의한 표준 명세를 실제로 구현한 라이브러리로, Spring Data JPA에서 기본 구현체로 사용된다.
  • 자동 매핑
    자바 객체와 테이블 간의 매핑을 어노테이션이나 XML 설정을 통해 자동으로 처리함
  • 변경 감지(Dirty Checking)
    트랜잭션 내에서 엔티티의 값이 변경되면 자동으로 변경 사항을 감지하고 DB에 반영함
  • 지연 로딩(Lazy Loading)
    연관된 엔티티를 실제로 사용할 때까지 로딩을 미룸으로써 성능 최적화
  • 1차 / 2차 캐시 지원
    동일 트랜잭션 내에서의 중복 DB 접근을 줄여주는 캐싱기능 제공
  • DB 방언 제공
    다양한 DBMS에 맞는 SQL문법을 자동으로 적용해주어 DB 독립성을 높여줌 -> JPA는 특정 DB에 종속되지 않는다.
  • JPQL과 HQL 제공
    객체 중심의 쿼리언어(JPQL, Hibernate Query Language)를 통해 SQL보다 객체 지향적인 쿼리 작성 가능

JPA 사용 예제

package hellojpa;

import jakarta.persistence.*;
import jakarta.transaction.Transactional;

import java.util.List;

@Transactional
public class JpaMain {

    public static void main(String[] args) {
    // 엔티티매니저팩토리 생성 -> unitname넘겨줌으로써 
    // 엔티티매니저팩토리가 설정파일 읽음
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        // 엔티티매니저 생성
        EntityManager em = emf.createEntityManager();
        // 엔티티매니저에서 트랜잭션 가져옴
        EntityTransaction tx = em.getTransaction();
        // 트랜잭션 시작
        tx.begin();

		// 로직 시작
        Member member = new Member();
        member.setId(1L);
        member.setName("HelloA");

		// member객체 영속성 컨텍스트 등록 -> 1차캐시저장, 쓰기지연저장소 저장
        em.persist(member);
        
        // 커밋시점 
        // (flush 실행 -> DB로 SQL 발송 -> commit -> 실제 DB에 반영)
        tx.commit();
        
        // 리소스 정리
        em.close();
        emf.close();
    }
}

JPA 구동방식은 다음과 같다.

  1. persistence.xml (설정 파일 또는 Spring에서는 application.yml / application.properties)
  2. EntityManagerFactory 생성
    → DB 연결, 설정 읽기, Entity 메타데이터 초기화
  3. EntityManager 생성
    → 실제로 DB 작업을 수행하는 객체 (트랜잭션 단위로 사용)
  4. EntityManager를 통해 엔티티 저장, 조회, 수정 등 수행

EntityManagerFactory는 JPA 설정 정보, 메타데이터, DB 커넥션 설정 등을 갖고 있는 무거운 객체이므로 애플리케이션 로딩 시 단 한 번만 생성되어야 하며, 이후에는 이 팩토리를 통해 트랜잭션 단위로 가벼운 EntityManager를 생성해 사용한다.
추가로 JPA는 데이터 변경 시에 반드시 트랜잭션 안에서 실행되어야한다.

JPA를 통한 수정작업

  • JPA는 변경 사항을 자동으로 감지(DIRTY CHECKING) 해서 flush() 시점에 SQL을 보내고 commit() 시점에 DB에 반영한다.
  • 해당 기능을 통해서 JPA를 이용해 수정작업을 하게 되면 별도의 em.persist() 처럼 영속성컨텍스트 등록을 통해 저장하는 과정을 거치지 않아도 트랜잭션 커밋 시점에 JPA가 자동으로 엔티티 변경을 감지해서 UPDATE쿼리를 날리게된다.
  • 이 자동 반영은 영속성 컨텍스트가 활성화되어 있어야 하고,
    그 영속성 컨텍스트는 보통 트랜잭션이 열려 있어야 유지된다.
  • 트랜잭션이 없으면 변경을 감지해도 "언제 반영할지", "롤백 가능한지"를 판단할 수 없기 때문에, 변경 사항은 무조건 트랜잭션 안에서 실행해야 안전하고 일관되게 작동한다.
package hellojpa;

import jakarta.persistence.*;

public class JpaMain {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
        	// 엔티티매니저로 Member class의 아이디가 1L인 컬럼 찾기
            Member findMember = em.find(Member.class, 1L);
            // 찾아온 member의 이름을 HelloJPA로 변경
            findMember.setName("HelloJPA");

			// 이후 em.persist()로 영속성컨텍스트에 등록하지않아도
            // 트랜잭션 커밋시점에 변경감지를 통해 UPDATE쿼리 발송
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
        emf.close();
    }
}


변경 전

변경 후

지금까지 정리한 내용은 JPA의 내부 동작방식과 기본 개념에 대해서 정리해보았다.
다음엔 JPA 영속성 컨텍스트, 엔티티매핑, 연관관계 등 더 자세하게 공부하고 다뤄봐야겠다.

profile
개발 이것저것

0개의 댓글