728x90
1. 영속성 컨텍스트
- JPA에서 가장 중요한 것 두가지는
- 객체와 관계형 DB 매핑하기(정적인 느낌, 디비를 어케 설계하고 객체를 어케 설계해서 어케 매핑할거야?) 와
- 영속성 컨텍스트(실제 JPA가 나누어서 어떻게 동작하는지, 메커니즘) 이다.
- 영속성 컨텍스트에 대해서 우선 알아보자.
1.1 JPA 주요 구성 요소
- 엔티티 매니저(EntityManager):
- JPA에서 데이터베이스 작업을 관리하는 주요 인터페이스
- CRUD 연산을 수행하는 API를 제공
- 엔티티의 생명 주기를 관리
- EntityManagerFactory에 의해 생성
- 각 EntityManager 인스턴스는 데이터베이스 세션과 직접 연결되어 독립적인 작업을 수행할 수 있다.
- 엔티티:
- 데이터베이스의 테이블에 해당하는 클래스
- 각 엔티티 인스턴스는 테이블의 개별 레코드(행)에 해당
- 이 클래스는 JPA 어노테이션을 사용하여 데이터베이스 테이블과 매핑
- 영속성 컨텍스트(Persistence Context):
- 엔티티를 영구 저장하는 환경
- 엔티티의 생명주기와 상태를 관리
- 데이터베이스와의 동기화를 담당
- 트랜잭션 범위 내에서 엔티티를 저장하고 관
- 1차 캐시: 영속성 컨텍스트는 엔티티의 1차 캐시 역할을 수행하여, 한 트랜잭션 내에서 반복된 데이터베이스 호출을 최소화한다. -> 수정, 삭제 예제 커밋 부분
- 엔티티의 생명주기 관리: 엔티티의 상태(비영속, 영속, 삭제, 분리)를 관리한다.
- 변경 감지: 트랜잭션이 커밋될 때, 영속성 컨텍스트에 있는 엔티티들을 검사하여 변경된 엔티티를 자동으로 데이터베이스에 반영한다. -> 스냅샷과 비교해서 반영
- 지연 로딩: 엔티티와 그 연관된 객체들을 필요한 시점까지 로딩을 지연시켜 성능을 최적화한다.
- 트랜잭션:
- 데이터베이스 작업을 묶어주는 방법
- 작업들이 모두 성공하거나 실패하게 보장한다.
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 엔티티 클래스의 조건
- @Entity 어노테이션: 클래스는 @Entity 어노테이션으로 표시하여, 해당 클래스가 JPA 엔티티임을 나타낸다.
- 식별자: 각 엔티티는 유일하게 식별될 수 있는 식별자를 @Id 어노테이션을 사용하여 지정한다.
- 기본 생성자: 엔티티 클래스는 JPA 구현체가 엔티티 인스턴스를 프로그래밍적으로 생성할 수 있도록 public 또는 protected의 기본 생성자를 가져야 한다.
- 클래스 수준에서의 제한: 최종 클래스(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 영속성 유닛의 주요 구성 요소
- 엔티티 클래스 목록: 영속성 유닛은 하나 이상의 엔티티 클래스를 포함할 수 있으며, 이들은 데이터베이스 테이블과 매핑된다.
- 데이터 소스 설정: 데이터베이스 연결을 위한 설정 정보를 포함하며, 이는 JPA 구현체가 데이터베이스와의 연결을 관리하는 데 사용된다.
- 트랜잭션 타입: JPA는 리소스 로컬(Resource-local)과 JTA(Java Transaction API) 트랜잭션 두 가지 타입을 지원하며, 사용 환경에 따라 적합한 트랜잭션 관리 방식을 선택할 수 있다.
5. 엔티티 매핑
앞서.. jpa에서 제일 중요하게 볼 것
-
- 메커니즘
-
- 매핑 ⇒ 객체와 테이블 매핑
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
- 키 생성을 위한 별도의 데이터베이스 테이블을 사용한다.
- GenerationType.AUTO
- @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 관계
- 엔티티 여러 개가 다른 엔티티 하나와 관계를 가질 수 있다.
- @OneToMany 관계
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
'Spring > Spring Boot' 카테고리의 다른 글
Spring 이메일 인증 기능 구현하기 2 : 비밀번호 찾기 (0) | 2024.08.20 |
---|---|
Spring 이메일 인증 기능 구현하기 1편 : 회원가입시 이메일 인증 (0) | 2024.08.20 |
DB - JOIN, SUBQUERY (0) | 2024.05.03 |
추상클래스, 인터페이스 (0) | 2024.04.11 |
객체 지향 프로그래밍이란? (0) | 2024.03.28 |