JPA / ORM 개요

TopOfTheHead·2025년 10월 19일

MyBatis / JPA

목록 보기
6/10

JPA ( Java Persistence API )
JAVA에서 ORM 기술표준으로 활용되는 API 라이브러리

ORM에 의해 어플리케이션DB EntityDB Table과 자동으로 Mapping하여 영속화DB Entity가 변화 시 Dirty Checking을 통해 변경사항을 감지 후 Update QueryDB에 전달하여 매핑DB Table의 데이터도 변경

JPA를 구현한 대표적인 오픈소스 구현체Hibernate가 존재.

CRUD를 수행 시 기존 JDBC는 개발자가 직접 SQL을 작성하는 번거로움이 존재
JPA구현체Hibernate를 통해 영속성 컨텍스트를 포함하는 EntityManagerAPI를 사용하는 경우 Hibernate가 자동으로 SQL을 생성하여 CRUD를 수행하므로 간편

Spring에서는 Spring Data JPA로 구현되어있으며 EntityManager가 아닌 JpaRepository 인터페이스 구현체DB EntityCRUD를 수행

@Entity가 선언된 Entity Class 내부에 클래스 객체 필드를 선언하는 경우 Entity 객체로 판단하므로, 연관관계 Mapping을 위한 어노테이션 ( @ManyToOne 등 )을 선언해야함.
▶ 일반 POJO 객체를 내부에 선언할 필요가 존재 시 @Embedded로 선언해야한다.

JPAHibernate를 사용하기 전에는 무조건 사용할 데이터베이스를 사전에 정의해야한다.

  • ORM (Object-Relational Mapping) :
    어플리케이션DB Entity 객체RDBMSDB Table에 자동으로 영속화 하는 것
    DB Entity 객체RDBMS DB Table 간 지속적으로 자동으로 Mapping하여 DB Entity가 변화 시 Update QueryDB에 전달하여 매핑DB Table의 데이터도 변경

    SQL을 직접 사용하지 않고, DB Entity 객체를 중심으로 DB와 상호작용을 수행
    RDBMS ( H2-DB, mySQL , postgreSQL ) 의 변경에도 적합한 Dialect를 자동으로 설정하여 유연한 적용이 가능.

ORM 특징

  • Dirty Checking
    영속성 컨텍스트 내에서 관리되고있는 영속 상태DB EntityTransaction 내에서 변화 시 자동으로 감지하여 변경된 내용을 자동으로 감지하여 flush() 시점에서 UPDATE Query를 생성 및 DB로 자동반영하는 JPA 기능
    DB Entity를 변경하는 TransactionCOMMIT되는 경우 변경된 정보에 대해서만 UPDATE를 자동으로 수행

  • 쓰기 지연 ( Transactional Write-Behind )
    Entity를 각각 생성 / 수정 시점에서 즉시 DB쿼리를 전달하지 않고, 영속성 컨텍스트 내에서 임시로 저장 후 COMMIT 시점에서 일괄적으로 DB쿼리를 전달하여 반영하는 기능
    디스크 I/O를 감소하기 위한 용도

    트랜잭션COMMIT되는 시점에서만 Lock이 적용되므로, Lock 발생을 최소화 할 수 있음.

  • Entity 객체 수정 / 삭제조회를 먼저 수행해야하므로 총 2번의 Query가 발생
    수정 / 삭제를 수행하는 경우 우선 영속화Entity객체영속성 컨텍스트에서 찾아와야하므로 각각 조회 쿼리가 추가적으로 발생
    ORM의 단점
Order foundedOrder = em.find(Order.class, order.getId());
em.remove(foundedOrder);

JPA 사용 시 주의사항

  • 지연로딩 ( Lazy Loading )
    컴파일 시점에서 프록시 객체로 초기화 및 런타임 시점에서 접근 시 DBQuery를 전달하여 실제 Entity 객체로 초기화

    。주로 (fetch = FetchType.LAZY)연관관계매핑Entity객체추가 조회하여 Query를 반복적으로 수행하여 N+1 문제가 발생할 수 있다

  • N+1 문제
    。특정 Query를 실행 시 여러번 Query가 실행되는 문제
    ex ) 댓글 조회 시 대댓글까지 조회

  • 복잡한 Query의 한계
    Query가 복잡한 경우 JPA를 통한 실행이 어렵고, Native Query 또는 MyBatis를 사용

  • DB Entity의 설계가 중요
    Entity Class를 잘못 설계하는 경우 성능저하를 유발할 수 있음.

JPA 핵심 구성 요소
EntityManagerFactory / EntityManager / 영속성 컨텍스트 / Entity가 존재

EntityManagerFactory
EntityManager을 생성하는 팩토리 객체

EntityManagerFactory는 내부적으로 Connection Pool을 가지고 있으므로, 어플리케이션 생애주기 내 오직 하나의 객체만 생성 및 관리되어야함.
▶ 해당 Connection PoolDB Connection을 기반으로 하는 EntityManager가 생성됨.

  • EntityManagerFactory 생성하는 방법
    META-INF/persistence.xml<persistence-unit> 태그를 기반으로 생성
EntityManagerFactory emf = Persistence.createEntityManagerFactory("<persistence-unit>식별자명");

Hibernate의 기능을 이용해 메서드 체이닝을 통해 간단히 DB 접속정보를 포함하여 생성
xml 설정파일 없이 어플리케이션 코드에서 간단히 생성가능.

HibernatePersistenceConfiguration hfg = new HibernatePersistenceConfiguration(
			"hibernate-exp-4");
		hfg
			.jdbcDriver("org.h2.Driver")
			.jdbcUrl("jdbc:h2:mem:test-db")
			.jdbcUsername("sa")
			.jdbcPassword("")
            .managedClass(Comment.class)
            .managedClass(Post.class)
            .property("hibernate.hbm2ddl.auto", "create-drop");
		EntityManagerFactory emf4 = hfg.createEntityManagerFactory()

Entity 클래스를 직접 등록해야한다.

EntityManager
DB Entity 객체의 전반적인 생명주기를 관리하는 역할을 수행하는 클래스
EntityManager 내부에 영속성 컨텍스트를 포함하므로, 영속성 컨텍스트에 등록된 Entity들을 관리

MyBatis - SqlSession과 같은 DAO 역할을 수행하며 EntityManagerFactory에 의해 DB Connection이 포함된 상태로 생성
JPA에서 실제 SQL을 수행하는 객체로서 EntityDB에 저장, 조회, 수정, 삭제하는 역할을 수행

EntityManager은 내부에 단일 DB Connection을 통한 Session을 포함하므로, 사용한 후에는 반드시 EntityManager객체.close()를 선언
EntityManager객체.close() 시 등록된 Entitydetached 상태로 전환

。하나의 EntityManger영속성 컨텍스트 (First Level Cache + Action Queue + Snapshot)를 포함

EntityManager em = EntityManagerFactory객체.createEntityManager();

  • ActionQueue
    영속성 컨텍스트 내 존재하는 자료구조
    Action Queue 내에는 DML Query들을 저장하고, flush()가 발생하는 시점에서 deque를 수행하여 일괄적으로 순차적으로 DB에 전달

    DB 격리수준과 별개로, 영속성 컨텍스트에서 Repeatable Read 격리수준을 제공


    EntityManager메서드

  • EntityManager객체.getTransaction()
    JPA에서 트랜잭션을 직접 제어 시 사용하는 EntityTransaction 객체를 생성
    ▶ 해당 EntityManager객체DAO 기능을 사용 시 tx객체.begin(), tx객체.commit(), tx객체.rollback() 등의 TCL을 지정

    Spring JPA에서는 @Transactional 어노테이션이 해당 과정을 추상화하여 자동으로 수행

  • EntityManager객체.close()
    EntityManager영속성 컨텍스트를 종료하는 메서드
    ▶ 내부에서 관리하는 Entity 객체비영속화

  • EntityManager객체.persist(DB Entity 객체)
    。해당 Entity 객체EntityManager영속성 컨텍스트영속화 시 사용하는 메서드
    ▶ 해당 Entity 클래스xml파일 내 등록되어야함.

    EntityManager객체.persist(Entity객체)를 수행하여 영속화한 경우, 해당 Entity 객체Auto Increment를 통해 자동으로 PK값을 할당됨.
    Test 수행 시 해당 PK값NULL인지 검증

  • EntityManager객체.find(Entity클래스타입, PK값)
    매핑DB Table에서 해당 PK값데이터Entity 객체로서 조회하는 경우 사용하는 메서드
Order foundedOrder = em.find(Order.class, order.getId());

▶ 조회할 클래스 객체 Type클래스 리터럴로 지정해야한다.

  • EntityManger객체.remove(Entity객체)
    영속화Entity객체를 삭제하는 메서드
Order foundedOrder = em.find(Order.class, order.getId());
em.remove(foundedOrder);

EntityManager.find()와 함께 사용

  • EntityManager객체.merge(Entity 객체)
    detached 상태Entity객체영속화 상태로 전환한 영속화 Entity 객체를 반환
    ▶ 반환된 Entity 객체로 작업 수행

  • EntityManager객체.createQuery(JPQL문, Entity클래스타입).getResultList()
    JPQLSELECT문을 통해 조회를 수행할 Query 객체를 생성 시 활용하는 메서드

    클래스 리터럴을 통해 Entity클래스.class를 선언하여 반환될 클래스메타데이터를 지정
EntityManager em = emf.createEntityManager();
String jpql = """
                SELECT m 
                    FROM Member m
                """;
TypedQuery<Member> query = em.createQuery(jpql, Member.class);Member.class).getResultList();

Query 객체를 통해 조회된 데이터 반환

  • Query객체.setParameter("변수명","값") :
    JPQL매개변수 바인딩 수행하는 메서드
    JPQL문매개변수변수바인딩:변수명으로 지정
String jpql = """
                SELECT m
                FROM Member m
                WHERE m.email = :email
                """;
TypedQuery<Member> query = em.createQuery(jpql, Member.class);
query.setParameter("email","wjdtn50@naver.com");
  • Query객체.setFirstResult(Offset).setMaxResults(Limit)
    JPQL문offsetlimit을 지정하는 메서드
    페이지네이션 구현 시 사용

    query.setFirstResult(pageNum * pageSize).setMaxResults(pageSize);
    setFirstResult : offset 지정 : limit * 페이지번호
    setMaxResults : limit 지정
  • Query객체.getResultList()
    JPQL문을 통해 다건조회Entity클래스 객체List<클래스> 객체로 반환
List<Member> resultList = em.createQuery(jpql, Member.class).getResultList();
  • Query객체.getSingleResult()
    JPQL문을 통해 단건조회Entity클래스 객체를 반환
Member resultList = em.createQuery(jpql, Member.class).getSingleResult();

EntityManager 사용 시 주의사항

  • EntityManager를 통한 작업 수행 시 해당 EntityManagerTransaction 객체를 생성하여 TCL을 적용
    。한 작업 단위try ~ catch ~ finally를 적용하여 BEGIN / COMMIT / ROLLBACK을 작성
    ▶ 작업이 모두 끝난 경우 EntityManager 객체를 종료
        // EntityManager의 Transaction 객체 생성saction 객체 생성
        EntityTransaction tx1 = entityManager.getTransaction();
		try{
			// Transaction BEGIN 실행
			tx1.begin();
			Post post = new Post(1, "title", "contents");
			entityManager.persist(post);
			// Transaction Commit
			tx1.commit();
		}catch (Exception e){
			// 트랜잭션 실패 시 rollback.
			tx1.rollback();
		} finally{
			entityManager.close();
			// EntityManager는 기능 하나당 EntityManager 객체를 사용하므로,
			// 기능 수행이 모두 끝난 경우 객체를 종료
		}
  • JPA에서는 하나의 작업 당 하나의 EntityManager 객체를 생성 및 할당하여 활용
    。해당 작업이 모두 종료된 경우 EntityManager객체를 종료 및 다음 작업에 대한 새로운 EntityManager객체를 생성하여 할당

  • Thread Safe 하지 않으므로, 하나의 스레드는 하나의 EntityManager를 사용하도록 설정
    HibernateSession을 감싸는 객체로서 SqlSession처럼 Connection 하나만 점유해서 사용하므로 Thread Safe하지 않음.

    。단. Tomcat의 경우 요청스레드 하나씩 생성해서 요청을 처리하므로 문제가 되지 않으나, Webflux를 사용하는 경우 해당 문제를 고려해야한다.

영속성 컨텍스트 ( Persistence Context )
EntityManager 객체 내부에 존재하는 DB Entity 객체영속 상태로서 저장 및 관리하는 용도의 저장소
메모리 상에 존재하여 HDDDB 반영캐싱 역할을 수행하고, 동시에 DB Entity영구적으로 저장 및 관리하여 영속성을 보장

EntityManagerDB Entity를 등록 시 쓰기 지연을 통해 영속성 컨텍스트 내에서 대기 후 flush()가 수행되는 경우 매핑DB Table에 반영됨

영속성 컨텍스트에서 관리되는 DB Entity 객체의 변경 시 Dirty Checking에 의해 변경을 감지하여 MappingDB Table에도 변경 쿼리를 전달

  • 컨테이너 부트스트래핑 :
    EntityManager객체 생성 시 EntityManagerFactoryConnection Pool에서 DB Connection 객체를 꺼내온 후 반영하면서 영속성 컨텍스트를 초기화

    flush() :
    commit 시 호출되어 기존 Pending 상태인 모든 Entity매핑DB Table에 일괄적으로 반영하도록 DBDML을 전송하는 역할을 수행
    Pending : DB Entity 변경내용이 DB에 반영되지않고 영속성 컨텍스트에만 반영된 상태

    flush()이 호출된 시점에서 EntityManagerAcition Queue 상에 존재하는 SQL DMLDeque하여 모두 DB에 전달 및 반영

DB Entity
ORM에 의해 DB의 특정 Table1:1 Mapping@Entity Class클래스

영속성 컨텍스트에 등록되어 영속화된 Entity 객체Entity Manager에 의해 DB의 특정 Table데이터로서 취급

Entity Class@Entity 선언하여 정의
▶ 추가적으로 필요 시 @ManyToOne 등을 선언하여 FK를 설정

Entity Class필드final이 선언되면 안된다.


Entity Class 정의 시 주의사항

  • @Column(nullable = false)를 설정하더라도, Entity 객체 생성 시 Null값에 대한 검사를 하지 않는다.
    개발자는 입력되는 데이터에 대해 Spring Validation을 통해 무결성을 검사하는 로직을 작성해야한다.
    nullable = falseHibernate에게 ddl-auto 생성 시 해당 필드제약조건 : not null로 설정하는 속성으로 Entity에 입력되는 데이터 자체를 검사해주지는 않는다.

  • DB Entity클라이언트에게 반환하면 안되며, DTO를 정의하여 필요한 데이터만 추출하여 클라이언트에게 반환
    DB Entity연관관계를 통해 다른 DB Entity 객체도 포함하고 있으므로

  • Entity 클래스필드final을 선언하지 말아야한다

  • PK 역할@Id 필드를 반드시 정의해야하고, Setter를 적용하면 안된다.
    。 해당 @Id 필드를 기준으로 영속성 컨텍스트에서 식별

    。자동으로 @GeneratedValue를 통해 값할당이 수행되므로 Setter를 적용하면 안된다.

  • 필드자료형Wrapper Class로 지정
    Null이 들어갈 수 있으므로.

  • 기본생성자protected를 선언하여 외부 패키지에서 기본생성자가 호출되지 않도록 설정
    DBNull값Record가 생성되지 않도록 보호

    EntityDB Table 차이

    • Entity :
      DB에서 관리해야할 독립적으로 존재하는 대상

      데이터 모델링설계 대상이 되는 DB Table를 부르는 용어
      DB Table과 다른 개념으로, 모델링 단계에서 DB Table을 설계하는 틀 역할을 수행

    • DB Table
      DB에 저장되어 구현된 실제 데이터 구조

DB Entity의 상태
비영속 / 영속 / 준영속 / 삭제

			// Transient
			Post post = new Post(1, "title", "contents");
			// Managed
			entityManager.persist(post);
			// Removed
			entityManager.remove(post);
  • 비영속 ( Transient )
    @Entity가 선언되었으나 영속성 컨텍스트에 한번도 등록되지않아 관리되고 있지않은 순수 POJO
    EntityManager.save()에 등록되기전까지는 new를 통해 생성된 직후의 Entity 객체 상태비영속으로 DB에 반영되지않음

    Class@OneToMany 등의 연관관계 매핑이 정의되있더라도 영속화 되기전까지는 초기화되지않아 연관관계가 없는 Null 상태

  • 영속 ( Managed )
    DB Entity영속성 컨텍스트에 등록되어 관리되고 있는 상태
    JpaRepository객체.save(비영속POJO)를 수행하는 경우 Persistence Context에 저장되어 관리

    。해당 시점에서 @OneToMany 등으로 정의된 연관관계를 정의하기 위해 DBQuery를 전달하여 연관관계 Entity객체를 초기화
    비영속 상태인 경우 @OneToMany fieldNull인 상태.

    Dirty Checking에 의해 DB Entity의 변화를 자동으로 감지하고 flush() 시점에서 실제 DB Table에 반영

    영속화 : DB Entity영속성 컨텍스트에 의해 관리되는 상태로 만드는것을 의미.

  • 준영속( Detached )
    영속성 컨텍스트 내 존재하는 영속 상태였다가 연결이 끊긴 상태의 DB Entity
    ▶ 더이상 영속성 컨텍스트에 의해 관리되지 않음

    EntityManager.close()일 경우 해당 EntityManager영속성 컨텍스트 내 등록된 Entitydetach로 전환

  • 삭제 ( Removed )
    Persistence Context에서 완전히 삭제된 상태
    EntityManager.remove(entity) 호출 시 해당 상태로 전환

JPA 원리
Spring Data JPA 기준


1. Repository에서 JpaRepository를 확장 및 JpaRepository에 구현된 API( EntityManager.save(DB Entity) ) 수행
Repository : Entity에 접근하기위한 인터페이스
JpaRepository : Entity Manager에 의한 CRUD가 이미 구현된 인터페이스

public interface MemberRepository extends JpaRepository<Member,Long> {
}

Repository에서 JpaRepository<>를 확장해서 해당 Entity에 대한 CRUD를 수행


2. EntityManager에 의해 Persistence ContextDB Entity를 등록
。등록 시 해당 Entity상태 ( 비영속 / 영속 / 준영속 / 삭제 )를 추적


3. 추후 Transaction에서 등록된 DB Entity가 변경된 경우 Dirty Checking에 의해 변경내용을 감지
트랜잭션을 통해 변경된 내용은 pending 상태영속성 컨텍스트 내에서만 반영


4. 트랜잭션Commit할 경우 flush()를 호출하여 Pending 상태변경내용만 갱신하는 UPDATE SQL매핑DB Table에 전송하여 반영

JPA와 Hibernate의 차이점
JPAAPI / HibernateAPI에 대한 구현체로서 JPA만으로 사용이 불가능하고 실제 Hibernate와 같은 JPA 구현체가 존재해야 JPA를 통한 CRUD를 수행가능

  • JPA
    API를 정의 ( 객체를 DB의 table로 Mapping하는 방식 정의 )
    @Entity : Interface로서 Entity가 무엇인지 정의
    @Id , @Column : 변수를 DB Table의 Field로 mapping
    EntityManager에 존재하는 Method를 구현함으로써 JPA API 사용.

  • Hibernate
    。 대표적인 오픈소스 JPA 구현체

    Hibernate JAR을 class path에 추가해서 HibernateJPA 구현체로 사용.
    ▶ (ex. @Entity의 경우. jakarta.persistence 대신 org.hibernate.annotations사용. )
    。코드에서 직접 Hibernate annotation을 사용하지 않는 이유는 다른 JPA 구현체(Toplink 등)이 존재하므로, Hibernate로만 한정해서 사용하지 않기 위해!

    인터페이스처럼 코드는 JPA를 사용하여 작성하여 추상화하고 Hibernate는 코드가 아닌, 구현체로만 사용하는것이 좋다.
    • Dialect :
      JPA 또는 Hibernate에서 사용하는 특정 DB에 해당하는 SQL문법기능을 사용하도록 설정
      ▶ 각 DB는 통일된 ANSI SQL를 사용하나 각각 작은 차이가 존재하므로 보완하기위해 Dialect를 설정
      Hibernate는 해당 문법/기능적 차이를 Dialect를 통해 자동으로 조정
profile
공부기록 블로그

0개의 댓글