728x90

1. 영속성 컨텍스트

  • JPA에서 가장 중요한 것 두가지는
    1. 객체와 관계형 DB 매핑하기(정적인 느낌, 디비를 어케 설계하고 객체를 어케 설계해서 어케 매핑할거야?) 와
    2. 영속성 컨텍스트(실제 JPA가 나누어서 어떻게 동작하는지, 메커니즘) 이다.
  • 영속성 컨텍스트에 대해서 우선 알아보자.

1.1 JPA 주요 구성 요소

  • 엔티티 매니저(EntityManager):
    • JPA에서 데이터베이스 작업을 관리하는 주요 인터페이스
    • CRUD 연산을 수행하는 API를 제공
    • 엔티티의 생명 주기를 관리
    • EntityManagerFactory에 의해 생성
    • 각 EntityManager 인스턴스는 데이터베이스 세션과 직접 연결되어 독립적인 작업을 수행할 수 있다.
  • 엔티티:
    • 데이터베이스의 테이블에 해당하는 클래스
    • 각 엔티티 인스턴스는 테이블의 개별 레코드(행)에 해당
    • 이 클래스는 JPA 어노테이션을 사용하여 데이터베이스 테이블과 매핑
  • 영속성 컨텍스트(Persistence Context):
    • 엔티티를 영구 저장하는 환경
    • 엔티티의 생명주기와 상태를 관리
    • 데이터베이스와의 동기화를 담당
    • 트랜잭션 범위 내에서 엔티티를 저장하고 관
    1. 1차 캐시: 영속성 컨텍스트는 엔티티의 1차 캐시 역할을 수행하여, 한 트랜잭션 내에서 반복된 데이터베이스 호출을 최소화한다. -> 수정, 삭제 예제 커밋 부분
    2. 엔티티의 생명주기 관리: 엔티티의 상태(비영속, 영속, 삭제, 분리)를 관리한다.
    3. 변경 감지: 트랜잭션이 커밋될 때, 영속성 컨텍스트에 있는 엔티티들을 검사하여 변경된 엔티티를 자동으로 데이터베이스에 반영한다. -> 스냅샷과 비교해서 반영
    4. 지연 로딩: 엔티티와 그 연관된 객체들을 필요한 시점까지 로딩을 지연시켜 성능을 최적화한다.
  • 트랜잭션:
    • 데이터베이스 작업을 묶어주는 방법
    • 작업들이 모두 성공하거나 실패하게 보장한다.

1.2 Entity Manager Factory와 Entity Manager(엔티티 매니저)

  • 웹 어플리케이션 실행 -> 엔티티 매니저 팩토리 생성 -> 요청들에 따라 각각 엔티티 매니저 팩토리에서 엔티티 매니저를 생성 -> 각각의 매니저가 db 커넥션을 이용해 처리.

1.3 영속성 컨텍스트

  • 영속성 컨텍스트는 JPA를 이해하는데 가장 핵심이다.
  • 엔티티를 영구 저장하는 환경"이라는 의미
**EntityManager.persist("Entiy")**
  • persist 메서드를 호출하는 것은 객체를 db에 저장한다고 생각할 수 있지만 사실은 "엔티티를 영속화" 하는 것으로, db에 직접 저장하는 것이 아니라, "영속성 컨텍스트"에 저장하는 것이다.
  • 객체를 db에 저장하는구나? -> “엔티티를 영속화한다” -> 사실은 db에 저장하는게 아니라, 영속성 컨텍스트에 저장하는 거임.
  • 영속성 컨텍스트는 논리적인 개념. 눈에 보이지 않음. 엔티티 매니저를 통해서 영속성 컨텍스트에 접근.
  • 엔티티 매니저를 생성하면, 영속성 컨텍스트라는 공간이 생긴다고 생각하면 된다. (대충 1차 캐시를 영속성 컨텍스트라고 생각해도 될 것 같다. 물론 어느정도 차이가 있다).

1.3.1 엔티티 매니저와 영속성 컨텍스트의 상호작용 코드

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;

public class Example {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("ExamplePU");
        EntityManager em = emf.createEntityManager();

        em.getTransaction().begin();

        // 새 엔티티 생성 및 영속화
        User newUser = new User("carami", "carami@example.com");
        em.persist(newUser);

        // 엔티티 조회 -> 
        // 위 newUser가 영속화되었기 때문에 영속성 컨텍스트에 있는 newUser를 리턴
        User user = em.find(User.class, newUser.getId());
        em.getTransaction().commit();
        em.close();
    }
}

1.4 영속성 컨텍스트의 이점

  • 1차 캐시
    • 약간 중간에 있는 느낌. 버퍼링도 가능하고, 캐싱도 가능함.
    • 사실상 1차 캐시가 영속성 컨텍스트. 스프링에서는 조금 달라지긴 하지만 일단은….
    • .Persist() 하면 1차 캐시에 pk-엔티티 이렇게 저장
    • 조회를 하면, db를 바로 뒤지는게 아니라 1차캐시에서 조회함
    • 만약 1차 캐시에 없으면, db에서 조회하고, 이 값을 1차캐시에 다시 저장하고, 이걸 반환.
    • 어플리케이션에서 공유하는 캐시는 2차 캐시라고 함.
    • 디비에서 한번 가져오고, 계속해서 영속성 컨텍스트에다가 저장.
    • 성능적인 이점 보다는 컨셉이 주는 이점. 객체지향적인 느낌.

1.5 영속 엔티티의 동일성 보장.

  • ”== “ 비교하면 어떻게 되니? 같은 객체로 취급됨. 마치 자바 컬렉션에서 꺼낸 것 처럼. 1차 캐시가 있기 때문에 가능.
  • 1차 캐시로 “반복 가능한 읽기(repeatable read) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 어플리케이션 차원에서 제공한다.

 2. 엔티티의 생명주기

2.1 엔티티의 생명주기

  • 비영속 (new,transient)
    • 위와 같이 최초로 객체를 생성한 상태, 영속성 컨텍스트와는 아무런 관련 없는 상태
  • **Member member = new Member();**
  • 영속(managed)
    • 영속성 컨텍스트에 의해 관리되는 상태
    • 물론 위와 같이 persist 메서드를 호출한다고 해서 쿼리가 바로 날아가지는 않고, tx.commit() 시점에 실질적인 쿼리가 날아간다.
    • 즉, 이 시점에 바로 DB에 반영되는 것은 아니다.
  • em.persist(member);
  • 분리된(Detached) 상태: 엔티티가 영속성 컨텍스트에서 분리된 상태
  • 삭제된(Removed) 상태: 엔티티가 삭제되어 데이터베이스에서도 삭제될 상태

2.1.1 비영속 (new,transient)

  • 멤버객체를 생성을 하고, 엔티티 매니저에 안 넣은 상태
Member member = new Member(); 
member.setId(“memberID”) 

-> JPA와 아예 관계가 없음

2.1.2 영속 (managed)

  • em.persist(member)를 통해서, 영속성 컨텍스트라는 곳에 들어가 있는 상태.
  • 멤버가 이제 관리가 된다라는 의미.
  • 사실 이 때 db에 저장되는게 아님.
  • persist()메서드를 쓸 때 쿼리가 날아가는게 아님.
  • *tx.commit()을 하는 때에 쿼리가 날아가게 됨.

2.1.3 영속 -> 준영속

  • 준영속 상태
    • 만약, FIND를 했는데 영속성 컨텍스트에 없으면 DB에서 가져와서 1차 캐시에 올려놓음.
    • 즉, 영속 상태가 됨.
    • 근데 만약에 이거 영속성 컨텍스트에서 관리하고 싶지가 않아, 하면 detach()를 씀.
    • Em.detach() 특정 컨텍스트만 준영속으로 만들 떄.
    • Em.clear() em이라는 영속성 컨텍스트 안에 있는 걸 전부 날려버림. 즉 쿼리가 안날라감.
    • Em.close() 하면 em을 닫아버림. Jpa가 관리가 안되겠지?

2.2 생명주기 전환 메서드

  • persist(entity): 엔티티를 영속 상태로 만든다.
  • merge(entity): 분리된 상태의 엔티티를 다시 영속 상태로 복구한다.
  • remove(entity): 엔티티를 삭제 상태로 전환한다.
  • detach(entity): 엔티티를 분리 상태로 만들며, 이를 통해 성능 및 메모리 사용 최적화, 트랜잭션 관리, 테스트와 디버깅 등을 수행할 수 있다.

2.3 엔티티 클래스의 조건

  1. @Entity 어노테이션: 클래스는 @Entity 어노테이션으로 표시하여, 해당 클래스가 JPA 엔티티임을 나타낸다.
  2. 식별자: 각 엔티티는 유일하게 식별될 수 있는 식별자를 @Id 어노테이션을 사용하여 지정한다.
  3. 기본 생성자: 엔티티 클래스는 JPA 구현체가 엔티티 인스턴스를 프로그래밍적으로 생성할 수 있도록 public 또는 protected의 기본 생성자를 가져야 한다.
  4. 클래스 수준에서의 제한: 최종 클래스(final class)가 아니어야 하며, 변경 가능해야 하고, 일반 Java 객체처럼 메서드나 속성을 추가할 수 있다.

2.3.1 엔티티 예제 코드

package com.example.jpa;

import jakarta.persistence.*;

@Entity // 클래스는 @Entity 어노테이션으로 표시 해당 클래스가 JPA 엔티티 명시
@Table(name = "user")
public class User {
    @Id // 각 엔티티는 유일하게 식별될 수 있는 식별자
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "username")
    private String username;
    @Column(name = "email")
    private String email;

    // 기본 생성자
    public User() { }

    // 추가적인 생성자와 setter, getter가 올 수 있다.
}

3. 지연 실행

3.1 지연 실행

  • 배칭: JPA는 여러 SQL 문을 함께 배치 처리할 수 있어 데이터베이스로의 왕복 횟수 를 줄일 수 있다.
  • 순서 보장: SQL 작업이 올바른 순서로 실행되도록 보장하여 참조 무결성을 유지한다.

3.1.1 변경 축적

  • 최적화: JPA는 실행해야 할 SQL 문의 수를 최적화할 수 있습니다. 예를 들어, 한 트랜 잭션 내에서 엔티티가 여러 번 수정된 경우, JPA는 최종 상태에 대해서만 하나의 UPDATE 문을 발행할 필요가 있습니다.
  • 불필요한 작업 감소: 데이터베이스에 불필요한 쓰기 작업을 방지합니다.

3.1.2 트랜잭션 무결성 보장

  • 원자성: 트랜잭션 경계 내에서 발생하는 모든 변경 사항은 완전히 커밋되거나 오류가 발생할 경우 전체적으로 롤백된다.
  • 격리성: 트랜잭션이 커밋될 때까지 변경사항이 다른 트랜잭션에 보이지 않는다.

3.1.3 자원 사용 최적화

  • 잠금 감소: 쓰기 작업을 지연함으로써 데이터베이스 행에 대한 잠금 시간을 줄일 수 있으며, 이는 잠금 경합을 줄이고 애플리케이션의 동시성을 개선해준다.
  • 연결 사용 효율성: 데이터베이스 연결이 실제 필요한 SQL 실행에만 사용되므로, 연결 사용이 더 효율적이다.
  • 예시 코드 - 트랜잭션 관리
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

// 엔티티가 저장되지만 데이터베이스에는 즉시 기록되지 않습니다.
User newUser = new User("carami", "carami@example.com"); em.persist(newUser);

// 추가 수정 사항이 있을 수 있음
newUser.setEmail("carami@example.com");

// 커밋: 모든 변경사항이 데이터베이스에 효율적으로 반영됩니다.
em.getTransaction().commit();

em.close();
  • 예시 코드 - 조회시 변경사항에 따라 자동 업데이트
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;
public class UpdateExample {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("ExamplePU");
        EntityManager em = emf.createEntityManager();
        try {
            em.getTransaction().begin();

            // ID로 특정 엔티티를 조회
            User user = em.find(User.class, 1L);

            // 엔티티의 필드 값을 변경
            if (user != null) {
                user.setEmail("updated.email@example.com");   // 이메일 필드 수정
            }

            // 트랜잭션을 커밋할 때 변경된 내용이 자동으로 데이터베이스에 반영
            em.getTransaction().commit();
        } finally {
            em.close();
            emf.close();
        }
    }  
}

4. 영속성 유닛(Persistence Unit)

4.1 영속성 유닛

  • 엔티티 클래스와 데이터베이스 연결 설정을 그룹화하는 JPA의 구성 단위
  • 애플리케이션에서 데이터를 관리하는 방법을 정의하고, 주로 persistence.xml 파일 내에 설정된다.

4.2 영속성 유닛의 주요 구성 요소

  1. 엔티티 클래스 목록: 영속성 유닛은 하나 이상의 엔티티 클래스를 포함할 수 있으며, 이들은 데이터베이스 테이블과 매핑된다.
  2. 데이터 소스 설정: 데이터베이스 연결을 위한 설정 정보를 포함하며, 이는 JPA 구현체가 데이터베이스와의 연결을 관리하는 데 사용된다.
  3. 트랜잭션 타입: JPA는 리소스 로컬(Resource-local)과 JTA(Java Transaction API) 트랜잭션 두 가지 타입을 지원하며, 사용 환경에 따라 적합한 트랜잭션 관리 방식을 선택할 수 있다.

5. 엔티티 매핑

앞서.. jpa에서 제일 중요하게 볼 것

    1. 메커니즘
    1. 매핑 ⇒ 객체와 테이블 매핑

5.1 엔티티 매핑 소개

  • 객체와 테이블 매핑 : @Entity, @Table
  • 피륻와 컬럼 매핑 : @Column
  • 기본 키 메핑 : @Id
  • 연관관계 매핑 : @ManyToOne, @JoinColumn

5.1.1 기본 엔티티 매핑

  • @Entity
    • @Entity 어노테이션은 클래스가 JPA 엔티티임을 나타내며,
    • 이 클래스의 인스턴스는 데이터베이스에 저장될 데이터를 나타낸다.
    • 주요 속성
      • name: 엔티티 이름 지정 —> 그냥 기본 값 쓰는게 정신건강에 좋음
      • catalog: 엔티티가 속할 데이터베이스 카탈로그 지정
      • schema: 엔티티가 속할 데이터베이스 스키마 지정
      • table: 엔티티에 매핑될 데이터베이스 테이블 이름 지정
      • readOnly: 엔티티의 읽기 전용 여부 설정
      • inheritance: 엔티티의 상속 전략 지정
  • @Id
    • @Id 어노테이션은 클래스의 필드를 테이블의 기본 키(primary key)로 지정하며,
    • 각 엔티티 인스턴스를 유일하게 식별하는 데 사용된다.
    • @GeneratedValue(…)
      • @GeneratedValue 어노테이션은 기본 키의 값을 자동으로 생성할 방법을 명시
      • 주요 기본 키 생성 전략
        • GenerationType.AUTO
          • JPA 구현체가 자동으로 가장 적합한 전략을 선택
        • GenerationType.IDENTITY
          • 데이터베이스의 ID 자동 생성 기능을 사용하여 기본 키를 생성한다.
          • 기본 키 생성을 데이터베이스에 위임.
          • (MYSQL의 경우 AUTO-INCREMENT가 이 경우) “나는 모르겠고 디비야 니가 알아서 해조..”
        • GenerationType.SEQUENCE
          • 데이터베이스의 시퀀스를 사용하여 기본 키를 생성한다.
          • 필드의 데이터 타입을 Long으로 하는 것을 추천.
        • GenerationType.TABLE
          • 키 생성을 위한 별도의 데이터베이스 테이블을 사용한다.
  • @Table
    • @Table 어노테이션은 엔티티 클래스가 데이터베이스의 어떤 테이블에 매핑될 것인지를 명시한다.
    • @Table(name=”MBR”) 로 하면 DB의 “MBR”테이블로 쿼리가 나감.
    • 주요 속성
      • name: 매핑될 테이블의 이름을 지정하며, 지정하지 않으면 클래스 이름을 사용한다.
      • catalog: 엔티티가 속할 데이터베이스 카탈로그를 지정한다.
      • schema: 엔티티가 속할 데이터베이스 스키마를 지정한다.
      • uniqueConstraints: 테이블 수준에서 유니크 제약 조건을 지정하며, 여러 열을 포함할 수 있는 제약 조건을 정의할 때 사용된다.
  • @Column
    • @Column 어노테이션은 엔티티의 필드가 데이터베이스 테이블의 어떤 열에 매핑될 것인지를 정의한다.
    • 주요 속성
      • name: 필드가 매핑될 테이블의 열 이름을 지정하며, 지정하지 않으면 필드 이름이 사용
      • nullable: 열이 널(Null) 값을 허용할지 여부를 지정하며, 기본값은 true
      • length: 문자열 필드의 경우 열의 최대 길이를 지정하며, 기본값은 255
      • unique: 이 열이 테이블 내에서 유니크한 값을 가져야 할지 지정 —> 잘 안씀
      • insertable 및 updatable: 이 필드가 데이터베이스에 삽입하거나 업데이트할 수 있는지 여부를 지정하며, 둘 다 기본값은 true이다.
      • Precision, scale : bigint나 소수점 쓸 때 쓰면 됨

5.1.2 관계 매핑 (@OneToMany, @ManyToOne)

  • @OneToMany와 @ManyToOne 관계는 엔티티 간의 일대다 관계를 매핑할 때 사용하는 어노테이션이다.
    • @OneToMany 관계
      • 한 엔티티가 다른 엔티티 여러 개와 관계를 가질 수 있다.
    • @ManyToOne 관계
      • 엔티티 여러 개가 다른 엔티티 하나와 관계를 가질 수 있다.

5.2 엔티티 등록

  • 영속성 컨텍스트 안에는 1차캐시 말고도, “쓰기 지연 sql 저장소”가 있음
  • .persist()하면, jpa가 엔티티를 분석을 해서 “쓰기 지연 sql 저장소”에다가 쿼리문을 보관해 놓음. 차곡 차곡 여기다가 쿼리문을 저장해 놓음
  • 이 쿼리가 언제 날라가냐? Transaction.commit()할 때 날라가는 거임. 플러쉬라고 함. 이 때 실제 db에 커밋됨
  • entity 객체는 기본 생성자가 있어야 함
  • 이렇게 하면 버퍼링이라는게 가능. 하이버네이트 같은 경우에는 옵션이 있음<”hibernate.jdbc.batch_size, value = " ... "> 이 속성
  • 쿼리를 모았다가 디비에 한방에 보내는 것 —> 실전에서는 딱히 크게 얻을 이점이 많지는 않지만, 이러한 최적화의 여지가 생김.

5.3 엔티티 수정 : 변경감지(더티 체킹)

  • jpa의 목적은 마치 자바 컬렉션에 넣은 것처럼 다루는 거임
  • 만약에 컬렉션 값을 수정 할 때 그 값을 꺼내서 수정하고 다시 그걸 저장함? 안함. 딱 찾아온 다음에 변경만 함
  • em.find() 해서 가져온 값을 member.set() 해서 수정 하고, 다시 em.persist 하면 안됨!!!!xxxx —> 마치 자바 컬렉션에서 값 바꾸듯이 바꼈는데 sql이 날라감
  • commit()을 하면, flush()가 호출됨. 무슨 일이 벌어지냐, 엔티티와 스냅샷을 비교함
  • db에서 가져오든 해서 최초로 영속성 컨텍스트에 들어온 상태를 “스냅샷”을 띄워 둠
  • 만약 memberA를 변경하고 flush를 하면, 그 순간 엔티티와 스냅샷을 쫙 비교를 함
  • 비교를 해보고 어 memberA가 바꼈네? 하면 쓰기 지연 SQL 저장소에다가 UPDATE 쿼리를 저장해 두고, 이걸 DB에 날려줌
  • 즉, em.find() → em.set() → em.getTransaction.commit() 으로 진행

5.4 엔티티 삭제

  • Em.remove() 하면 delete() 쿼리 생성. 커밋 시점에 날라감
  • 결론 : jpa는 값을 바꾸면 트랜잭션 커밋 시점에 알아서 업데이트 쿼리가 날아가는 구나. 라고 생각하고, 따로 persist()를 안하는게 정답임

5. 5 생명주기 변환 메서드 사용 관련 예

  • 테이블 생성
CREATE TABLE schools (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL
);

CREATE TABLE students (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    school_id BIGINT,
    FOREIGN KEY (school_id) REFERENCES schools(id)
);

--  테이블 INSERT문
-- Schools 추가
INSERT INTO schools (name) VALUES ('Greenwood High School');
INSERT INTO schools (name) VALUES ('Riverside Academy');

-- Students 추가
INSERT INTO students (name, school_id) VALUES ('Alice', 1);
INSERT INTO students (name, school_id) VALUES ('Bob', 1);
INSERT INTO students (name, school_id) VALUES ('Charlie', 1);
INSERT INTO students (name, school_id) VALUES ('David', 2);
INSERT INTO students (name, school_id) VALUES ('Eva', 2);
  • Entity 클래스 - School
package com.example.jpa;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name="schools")
@Getter@Setter
@NoArgsConstructor
public class School {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    // cascade: 학교가 수정되거나 삭제되면 연관되어있던 학생도 같이 수정되거나 삭제
    // orphanRemoval: 부모(학교) 엔티티와 연관관계가 끊어진 자식(학생) 엔티티를 삭제
    @OneToMany(mappedBy = "school", cascade = CascadeType.ALL, orphanRemoval = true)    // 학교 1 : 학생 n
    private List<Student> students = new ArrayList<>();                                 // 일대다 관계

    public School(String name) {
        this.name = name;
    }
}
  • Entity 클래스 - Student
package com.example.jpa;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Entity
@Table(name="students")
@Getter@Setter
@NoArgsConstructor
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @ManyToOne                          // 본인 기준 관계(학생 n : 학교 1)
    @JoinColumn(name = "school_id")     // 조인 컬럼명
    private School school;  // school_id를 사용하기 위한 방법

    public Student(String name, School school) {
        this.name = name;
        this.school = school;
    }
}
  • 조회, 삽입, 수정, 삭제 예제
package com.example.jpa;

import jakarta.persistence.*;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class SchoolExamMain {

    private static void find(){
        EntityManager em = JPAUtil.getEntityManagerFactory().createEntityManager();
        em.getTransaction().begin();
        try{
            // 1번 학교 인스턴스 생성 후 이름 출력
            School school = em.find(School.class, 1L);
            log.info("schhol name : {}", school.getName());

            // 1번 학교의 학생들 이름 출력
            for (Student student : school.getStudents()) {
                log.info("student name : {}", student.getName());
            }

            // 5번 학생의 이름과 학교이름 출력
            Student student = em.find(Student.class, 5L);
            log.info("student name: {}", student.getName());
            log.info("school name: {}", student.getSchool().getName());
            em.getTransaction().commit();

        } finally {
            em.close();
        }
    }

    private static void create(){
        EntityManager em = JPAUtil.getEntityManagerFactory().createEntityManager();
        em.getTransaction().begin();
        try{
            School school = new School("LionSchool");

            Student student1 = new Student("홍길동", school);
            Student student2 = new Student("아무개", school);
            Student student3 = new Student("삽살개", school);

            school.getStudents().add(student1);
            school.getStudents().add(student2);
            school.getStudents().add(student3);

            // 영속화
            em.persist(school);

            // 실제 db와 비교해 반영
            em.getTransaction().commit();
        }finally {
            em.close();
        }
    }

    private static void update(){
        EntityManager em = JPAUtil.getEntityManagerFactory().createEntityManager();
        em.getTransaction().begin();
        try{
	          // find: 이미 영속성 컨텍스트에 존재
            School school = em.find(School.class, 2L);      

            school.setName("update School Name");

            em.getTransaction().commit();
        }finally {
            em.close();
        }
    }

    private static void delete(){
        EntityManager em = JPAUtil.getEntityManagerFactory().createEntityManager();
        em.getTransaction().begin();
        try{
            School school = em.find(School.class, 5L);

            // cascade 속성->학교가 삭제될 때 해당 학교에 소속되어 있는 학생들도 삭제
            em.remove(school);

            em.getTransaction().commit();
        }finally {
            em.close();
        }
    }

    public static void main(String[] args) {
//        find();
//        create();
//        update();
        delete();
    }
}

6. 플러시

6.1 플러시

  • 영속성 컨텍스트의 변경내용을 데이터베이스에 반영.
  • 보통 db 트랜잭션이 커밋 될 때 플러시가 일어남.
  • 쌓아놓은 sql문이 날라가는 것. 영속성 컨텍스트의 쿼리들을 db에 쭉 날려주는 것.

6.1.1 플러시 발생

  • .commit() 되면 자동으로 발생.
  • 변경 감지(더티 체킹), 수정된 엔티티를 쓰기 지연 sql 저장소에 등록함.
  • 쓰기 지연 sql 저장소의 쿼리를 db에 전송 (등록,수정,삭제 쿼리)를 쫙 보내는거임.
  • 플러시가 발생한다고 해서 커밋 이 발생하는건 아니고…

6.2 영속성 컨텍스트를 플러시 하는 방법

  • em.flush()
    • 직접 호출 —> 진짜로 이렇게 쓰는 경우는 없음
  • 트랜잭션 커밋
    • 플러시 자동 호출
  • Jpql 쿼리 실행
    • 플러시 자동 호출

Q. 플러시를 하면 1차 캐시가 지워지나요?

  • 아니요. 오직 쓰기 지연 sql 저장소에 있는 쿼리들만 db로 날려버리는 거임. 뭔가 바뀐거 이런것만 데이터베이스에 반영이 되는 것.

Q. jpql에서 플러시가 자동으로 호출되는 경우?

  • 만약 persist만 한 상태에서 중간에 jpql로 조회하는 쿼리를 날린다면, 아무것도 안날라올 수도 있음. 따라서, jpql은 무조건 플러시를 한번 날리고 시작함. 아 그래서 날라가는 구나 라고 생각하면 됨.

Q. 플러시 모드 옵션?

  • 딱히 쓸 일은 없음.
  • FlushModeType.COMMIT – 커밋 할 때만 플러시, 쿼리에는 플러시를 안한다. 위 같은 경우에 만약 JPQL을 하는데 뭐 아예 다른 테이블을 가져오고 그런 경우에는 굳이 플러시를 할 필요가 없겠지? 근데 굳이 플러시를 하고 싶지 않을 수도 있음. 정 원하면 플러시 모드를 커밋으로 바꾸라 -> 큰 도움이 되지는 않습니다. 걍 AUTO로 쓰세요. 손대지 말고.
  • 플러시는 영속성 컨텍스트를 비우는게 아니라, 영속성 컨텍스트를 DB에 반영 하는 것.
    • 트랜잭션이라는 작업 단위가 중요함.
    • 커밋 직전에만 동기화 하면 됨.
    • 어쨌든 트랜잭션 커밋 직전에만 날려주면 되니까!!
    • JPA는 결국 이런 동시성 관련 이슈는 다 DB 트랜잭션에 위임하기 때문에.
728x90

+ Recent posts