본문 바로가기
Spring/JPA

[Spring/JPA] Persist() , flush() 가 뭐야 ?

by windy7271 2024. 5. 24.
728x90
반응형

JPA는 요청이 들어오면 EntityManagerFactory 에서 EntityManager 를 생성한다.

 

EntityManager: 엔티티를 관리하는 역할을 수행하는 클래스

EntityManager 는 내부적으로 DB 커넥션 풀을 사용해서 DB에 붙는다.

 

그러면 어떤 방식으로 EntityManager 는 Entity들을 관리를 할까 ?? 

EntityManager 내부에 영속성 컨텍스트(Persistence Context)라는 걸 두어서 Entity들을 관리한다.

 

영속성 컨텍스트 : Entity를 영구 저장하는 환경

애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할을 한다. EntityManager 를 통해 엔티티를 저장하거나 조회하면 EntityManager는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.

 

 

영속성 컨텍스트의 기능

  • 1차캐싱
  • 동일성 보장 (하나의 트랜잭션 안에 존재하는 id가 같은 엔티티는 동일성 보장)
  • 변경감지 :1차 캐시에 들어있는 엔티티가 변경되면, 커밋 시점에 변경 내용 db반영
    • 영속성 컨텍스트가 관리하는 영속 상태의 엔티티에만 적용
  • 지연로딩:연관 관계 매핑이 되어 있는 엔티티를 조회할 때, 조회 시점에는 연관된 엔티티를 실제 객체 대신 프록시 객체로 로딩
    • 프록시 객체: 실제 엔티티 객체 대신 사용되는 객체, 실제 객체의 참조
  • 쓰기지연 : 커밋 직전까지 내부 쿼리 저장소에 sql 모아두고 커밋할때 한 번에 DB로 보낸다.

 

1.persist()  : EntityManager 를 사용해서  영속성 컨텍스트를 통해서 엔티티를 영속화 한다 (1차 캐싱)

여기서 영속화라는 말은 persistence를 하고 싶다는 것이고 데이터를 영원히 저장한다는 것 -> DB에 저장

 

Transactional 에서, 영속화가 된다는 말은 자동적으로 DB에 데이터가 들어간다는 것이다

 

 

2.Flush() : 영속성 컨텍스트의 변경 내용을 DB반영

  • 변경 감지가 동작 -> 영속성 컨텍스트에 있는 모든 엔티티를 비교해 수정된 엔티티 찾음 -> 수정된 엔티티 는 수정 쿼리 만들어 쓰기 지연 SQL 지연 저장소에 등록
  •  쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송한다
  • 영속성 컨텍스트는 비우지 않는다. -> clear() 호출

 방법:

1. flush() 호출

2. 트랜잭션 커밋 시 자동 호출

3. JPQL 쿼리 실행 전 플러시 호출

 

 

그러면 이제 이것을 실제 Spring 에서 실험을 해보겠다.

@Transactional
public Memo createMemo(MemoRequestDto requestDto) {
    Memo memo = new Memo(requestDto);
    memoRepository.save(memo); 
    return memo;
}

// 메모 삭제하기
@Transactional
public Long deleteMemo(Long id) {
    memoRepository.deleteById(id); 
    return id;
}

// 메모 수정하기
@Transactional
public Long update(Long id, MemoRequestDto requestDto) {
    Memo memo = memoRepository.findById(id).orElseThrow(
            () -> new IllegalArgumentException("아이디가 존재하지 않습니다.")
    );
    memo.update(requestDto); 
    return memo.getId();
}

 

 

1. Save

그러면 이런 생각이 든다.

repository.save() 하면 알아서 DB에 넣어주던데요 ?

 

JpaRepository의 구현체인  SimpleJparepository 의 save이다

 

@Transactional
@Override
public <S extends T> S save(S entity) {

    Assert.notNull(entity, "Entity must not be null.");

    if (entityInformation.isNew(entity)) {
       em.persist(entity);
       return entity;
    } else {
       return em.merge(entity);
    }
}

 

만약에 새로운 entity이면 persist()를 해주므로써 새로운 Entity 를 영속성 컨텍스트에 추가를 해주므로써 영속성 컨텍스트에서 관리를 해줄 수 있게 된다. 그러면 트랜잭션이 커밋될때 DB에 삽입되는 것이다.

 

- 이미 영속 상태인 Entity에 대해 persist를 호풀하면 예외가 발생하지는 않는다. 하지만 아무런 효과 가 없다.

 

그러면 이미 존재하는 경우 merge()를 호출해 주면서 

파라미터로 em.merge(entity) 를 넘겨주게 된다.

 

그러면 파라미터로 전달된 entity 의 상태를 복사한 새로운 영속 entity를 반환하게 된다.

 

비영속 상태의 entity를 merge 하게 되면 영속성 컨텍스트에 의해 관리되는 새로운 entity가 된다, 

 

그럼 여기서 영속? 비영속이 뭐에요 ? 라는 의문이든다

 

비영속 : entity가 아직 영속성 컨텍스트에 관리되지 않는 상태

Entity entity = new Entity();

 

영속 : entity가 영속성 컨텍스트에 의해 관리되는 사태

em.persist(entity)

 

 

 

준영속: 한때 영속 이였지만, 지금은 더이상 관리되지 않은 상태

  • 트랜잭션이 끝난 후 영속성 컨텍스트가 닫힐때.
  • em.detach(entity);

준영속 -> 영속 merge

영속-> 준영속 detach

 

 

여기서 또 의문 그러면 트랜잭션이 끝나면 영속성 컨텍스트가 닫히나요 !?

 

트랜잭션이 시작되면 영속성 컨텍스트가 생성되고, 트랜잭션이 끝나면 영속성 컨텍스트도 종료가 된다.

즉 모든 entity가 준영속 상태가 되게 된다.

 

메소드에 @Transactional을 붙여주면 메소드가 호출될때 새로운 영속성 컨텍스트가 생성돼 관리되고 트랜잭션이 종료되면 영속성 컨텍스트는 종료되는 것이다.

 

2. Update

udpate 문을 보면 원래 생각 대로 라면 

update 하고 save로 repository에 넣지 않았는데 수정된 값이 잘 들어간다.

 

그 이유는 @Transactional 때문이다.

 

영속성 컨텍스트 내부에 있는 

1차 캐시에 들어있는 Entity가 변경되면, 그 변경 내용을 감지해서 트랜잭션 커밋 시점에 변경된 내용들을 DB에 반영한다.

 

 

 

 

참고 

https://umanking.github.io/2019/04/12/jpa-persist-merge/

https://velog.io/@kimdy0915/Transactional-%EC%82%AC%EC%9A%A9%EA%B3%BC-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8persistence-context

https://sweets1327.tistory.com/60

https://velog.io/@merci/JPA-%EC%98%81%EC%86%8D%ED%99%94

https://velog.io/@neptunes032/JPA-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%9E%80

https://ict-nroo.tistory.com/130

https://perfectacle.github.io/2018/01/14/jpa-entity-manager-factory/

반응형

댓글