심화/Spring

[Spring] JPA와 영속성 컨텍스트

annovation 2025. 10. 6. 09:32

영속성 컨텍스트(Persistence Context)

 

💡영속성 컨텍스트(Persistence Context)란?

  • Persistence = "영속성, 지속성"
  • JPA에서 말하는 영속성 컨텍스트란, Entity 객체를 효율적으로 관리하기 위한 가상의 공간
Persistence를 객체의 관점으로 해석해 보자면 ‘객체가 생명(객체가 유지되는 시간)이나 공간(객체의 위치)을 자유롭게 유지하고 이동할수 있는 객체의 성질’을 의미

  • 개발자들은 JPA를 사용하면, 직접 SQL을 작성하지 않아도 DB에 데이터를 저장하거나 조회할 수 있으며 수정, 삭제 또한 가능하다.

  • 이러한 일련의 과정을 효율적으로 처리하기 위해 JPA는 영속성 컨텍스트에 Entity 객체들을 저장하여 관리하면서 DB와 소통한다.
  • 영속성 컨텍스트에서 PK로 Entity들을 식별한다.

🔎 Entity란?

  • JPA에서 관리되는 클래스 즉, 객체를 의미
  • Entity 클래스는 DB의 테이블과 매핑되어 JPA에 의해 관리된다.

EntityManager

EntityManager

 

💡EntityManager

  • EntityManager는 이름 그대로 Entity를 관리하는 관리자이다.
  • 영속성 컨텍스트에 접근하여 Entity 객체들을 조작하기 위해서는 EntityManager가 필요하다.
  • 개발자들은 EntityManager를 사용해서 Entity를 저장하고 조회하고 수정하고 삭제할 수 있다.
    • Entity 저장, 조회, 수정, 삭제
    • DB와 연결되는 SQL 실행을 대신 처리
  • EntityManager는 보통 EntityManagerFactory로부터 생성해서 사용한다.

EntityManagerFactory

EntityManagerFactory

💡EntityManagerFactory

  • EntityManager를 생성하는 공장(Factory)
  • 보통 DB 하나당 하나의 EntityManagerFactory만 생성된다.
  • 애플리케이션 실행 동안 살아있으며, 필요할 때마다 EntityManager를 만들어준다.
  • EntityManagerFactory를 만들기 위해서는 DB에 대한 정보를 전달해야한다.

🔎 persistence.xml

  • EntityManagerFactory를 만들기 위해 DB에 대한 정보를 전달해야할 때 사용하는 파일
  • 정보를 전달하기 위해서는 /resources/META-INF/ 위치에 persistence.xml 파일을 만들어 정보를 넣어두면 된다.
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
             xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
    <persistence-unit name="memo">
        <class>com.sparta.entity.Memo</class>
        <properties>
            <property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="jakarta.persistence.jdbc.user" value="root"/>
            <property name="jakarta.persistence.jdbc.password" value="{비밀번호}"/>
            <property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/memo"/>

            <property name="hibernate.hbm2ddl.auto" value="create" />

            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.use_sql_comments" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

 

➡️ Java에서의 사용 예시

EntityManagerFactory emf = Persistence.createEntityManagerFactory("memo");
EntityManager em = emf.createEntityManager();
  • EntityManagerFactory emf = Persistence.createEntityManagerFactory("memo");
    • 해당 코드를 호출하면 JPA는 persistence.xml 의 정보를 토대로 EntityManagerFactory를 생성한다.
  • EntityManager em = emf.createEntityManager();
    • 코드를 호출하면 EntityManagerFactory를 사용하여 EntityManager를 생성할 수 있다.

JPA의 트랜잭션

 

💡트랜잭션(Transaction)이란?

트랜잭션

  • DB 데이터의 무결성(Integrity)  정합성(Consistency) 을 보장하기 위한 논리적 단위
  • 쉽게 표현하자면 DB의 데이터들을 안전하게 관리하기 위해서 생겨난 개념

🔎 특징

  • 가장 큰 특징은 여러 개의 SQL이 하나의 트랜잭션에 포함될 수 있다는 점이다.
  • 이때, 모든 SQL이 성공적으로 수행이 되면 DB에 영구적으로 변경을 반영하지만 SQL 중 단 하나라도 실패한다면 모든 변경을 되돌린다. (rollback)

🔎 MySQL 예시

START TRANSACTION; # 트랜잭션을 시작합니다.

INSERT INTO memo (id, username, contents) VALUES (1, 'Robbie', 'Robbie Memo');
INSERT INTO memo (id, username, contents) VALUES (2, 'Robbert', 'Robbert Memo');
SELECT * FROM memo;

COMMIT; # 트랜잭션을 커밋합니다.

SELECT * FROM memo;
  • JPA는 DB의 이러한 트랜잭션 개념을 사용하여 효율적으로 Entity를 관리하고 있다.

💡JPA의 트랜잭션

  • 영속성 컨텍스트에 Entity 객체들을 저장했다고 해서 DB에 바로 반영 되지는 않는다.
  • DB에서 하나의 트랜잭션에 여러 개의 SQL을 포함하고 있다가 마지막에 영구적으로 변경을 반영하는 것 처럼 JPA에서도 영속성 컨텍스트로 관리하고 있는 변경이 발생한 객체들의 정보를 쓰기 지연 저장소에 전부 가지고 있다가 마지막에 SQL을 한번에 DB에 요청해 변경을 반영한다.

🔎 저장 성공 예시

@Test
@DisplayName("EntityTransaction 성공 테스트")
void test1() {
    EntityTransaction et = em.getTransaction(); // EntityManager에서 EntityTransaction을 가져옵니다.

    et.begin(); // 트랜잭션을 시작합니다.

    try { // DB 작업을 수행합니다.

        Memo memo = new Memo(); // 저장할 Entity 객체를 생성합니다.
        memo.setId(1L); // 식별자 값을 넣어줍니다.
        memo.setUsername("Robbie");
        memo.setContents("영속성 컨텍스트와 트랜잭션 이해하기");

        em.persist(memo); // EntityManager 사용하여 memo 객체를 영속성 컨텍스트에 저장합니다.

        et.commit(); // 오류가 발생하지 않고 정상적으로 수행되었다면 commit 을 호출합니다.
        // commit이 호출되면서 DB에 수행한 DB 작업들이 반영됩니다.
    } catch (Exception ex) {
        ex.printStackTrace();
        et.rollback(); // DB 작업 중 오류 발생 시 rollback을 호출합니다.
    } finally {
        em.close(); // 사용한 EntityManager를 종료합니다.
    }

    emf.close(); // 사용한 EntityManagerFactory 를 종료합니다.
}
  • ⚠️ 주의
    • Entity Class는 DB와 매핑이 되는 객체이기 때문에, Setter같은 값을 변경하는 메서드 사용을 할 때 주의해서 꼭 필요한 곳에만 쓸 수 있도록 한다.
    • 의도치 않게 Setter에 의해 데이터를 변경해 DB에 반영되는 실수가 생길 수 있다.
  • JPA에서 트랜잭션의 개념을 적용하기 위해 EntityManager에서 EntityTransaction을 가져와 트랜잭션을 적용할 수 있다.
  • EntityTransaction et = em.getTransaction();
    • 해당 코드를 호출하여 EntityTransaction을 가져와 트랜잭션을 관리할 수 있다.
  • et.begin();
    • 트랜잭션을 시작하는 명령어
  • et.commit();
    • 트랜잭션의 작업들을 영구적으로 DB에 반영하는 명령어
  • et.rollback();
    • 오류가 발생했을 때 트랜잭션의 작업을 모두 취소하고, 이전 상태로 되돌리는 명령어

🔎 저장 실패 예시

@Test
@DisplayName("EntityTransaction 실패 테스트")
void test2() {
    EntityTransaction et = em.getTransaction(); // EntityManager에서 EntityTransaction을 가져옵니다.

    et.begin(); // 트랜잭션을 시작합니다.

    try { // DB 작업을 수행합니다.

        Memo memo = new Memo(); // 저장할 Entity 객체를 생성합니다.
        memo.setUsername("Robbert");
        memo.setContents("실패 케이스");

        em.persist(memo); // EntityManager 사용하여 memo 객체를 영속성 컨텍스트에 저장합니다.

        et.commit(); // 오류가 발생하지 않고 정상적으로 수행되었다면 commit을 호출합니다.
        // commit이 호출되면서 DB에 수행한 DB 작업들이 반영됩니다.
    } catch (Exception ex) {
        System.out.println("식별자 값을 넣어주지 않아 오류가 발생했습니다.");
        ex.printStackTrace();
        et.rollback(); // DB 작업 중 오류 발생 시 rollback을 호출합니다.
    } finally {
        em.close(); // 사용한 EntityManager를 종료합니다.
    }

    emf.close(); // 사용한 EntityManagerFactory를 종료합니다.
}
  • 식별자 값을 넣어주지 않아 오류가 발생한 경우 but, auto increment 조건이 설정된 경우 식별자 값을 직접 넣지 않아도 된다.
  • 따라서 et.rollback(); 코드가 호출이되어 트랜잭션 작업 내용들이 취소된다.
  • DB를 확인해보면 해당 작업이 반영되어있지 않은 것을 확인할 수 있다.

🔎 영속성 컨텍스트 Debugging

 

1️⃣ 테스트 시작 위치 가장 왼쪽을 클릭하여 빨간색 원을 만든다. 그리고 해당 원에 우측클릭 후 Thread 부분을 클릭한 다음 Make Default 클릭 후 Done을 누른다.

 

2️⃣ 초록색 화살표 시작 버튼을 클릭 후 Debug를 클릭

 

3️⃣ 체크 부분을 클릭하여 순차적으로 코드를 진행

 

4️⃣ em.persist(memo); 코드 호출 후 persistenceContext 위치에 memo 객체가 저장되어있음을 확인할 수 있다.


출처

https://teamsparta.notion.site/2-8-2252dc3ef5148138bc6cd0ca5c783ef5

 

챕터2-8 : 영속성 컨텍스트란 무엇일까? | Notion

영속성 컨텍스트란?

teamsparta.notion.site