JPA는 자바의 ORM기술이다.
ORM이란 Object-Relational Mapping 의 약자로 객체와 관계형 데이터베이스의 테이블을 매핑해주는 기술이다.
JPA에 대해서 알아보기 전에 먼저 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란, 자바에서 객체와 관계형 데이터베이스 간의 매핑을 도와주는 ORM 표준 인터페이스이다.
자바를 사용해 프로그래밍할 때, 객체를 데이터베이스 테이블과 자동으로 매핑해주는 중간 다리 역할을 하며, 개발자는 직접 SQL을 작성하는 수고를 줄이고 객체 중심으로 DB 작업을 수행할 수 있게 해준다.
JPA는 인터페이스이기 때문에 JPA를 구현체를 함께 사용해야 한다. 대표적으로는 Hibernate, EclipseLink, OpenJPA 가 있다.

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보다 객체 지향적인 쿼리 작성 가능

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 구동방식은 다음과 같다.
EntityManagerFactory는 JPA 설정 정보, 메타데이터, DB 커넥션 설정 등을 갖고 있는 무거운 객체이므로 애플리케이션 로딩 시 단 한 번만 생성되어야 하며, 이후에는 이 팩토리를 통해 트랜잭션 단위로 가벼운 EntityManager를 생성해 사용한다.
추가로 JPA는 데이터 변경 시에 반드시 트랜잭션 안에서 실행되어야한다.
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 영속성 컨텍스트, 엔티티매핑, 연관관계 등 더 자세하게 공부하고 다뤄봐야겠다.