토이 프로젝트 배우게 된 것들 & 오류 해결🐰

JPA | @Transactional 과 변경감지

j_estory 2023. 1. 30. 22:05

DB 테이블에 저장되어 있는 데이터의 정보를 수정하기 위해 update 하는 로직을 개발하고자 한다면

다음과 같은 논리적 절차를 거치게 될 것이다.

  • 트랜잭션 시작
  • 변경하고자 하는 데이터의 id를 통한 조회
  • 조회한 데이터에 수정할 내용들로 교체
  • update 처리
  • 트랜잭션 커밋

JPA에서는 위의 데이터 변경 로직을 어떻게 처리해주는지 확인해보자

  • 트랜잭션의 시작
  • 영속성 Entity 조회 - 없으면 DB 조회 후 영속화
  • 조회한 영속성 Entity의 데이터 수정
  • 트랜잭션 커밋

위의 과정에서 볼 수 있듯이 JPA에서는 따로 update 쿼리를 요청하는 부분이 없습니다.

여기서 JPA의 장점 중 하나가 나오는데 바로 변경감지(Dirty Checking) 입니다.

 

✅ 변경감지

 

변경감지는 트랜잭션 커밋 시, 영속화된 Entity에서 가지고 있었던 최초 정보와 바뀐 Entity 정보를 비교해섯

바뀐 부분을 update 해주는 기능이다.

 

위의 내용을 하나하나 살펴 보면!!

 

JPA는 트랜잭션 커밋 시 EntityManager에서 flush를 자동 호출해준다.

flush는 영속성 컨텍스트의 변경 내용을 DB에 반영하는 것이라고 생각하면 됩니다.

트랜잭션을 커밋할 때 DB에 변경 내용을 update 하지 않고 커밋하게 되면 아무것도 변경되지 않기 때문에

JPA에서 트랜잭션 커밋 시 이를 자동적으로 수행해준다. 

 

flush를 호출하게 되면 스냅샷과 entity의 바뀐 정보를 서로 비교하게 된다.

스냅샷은 db에서 데이터를 가져와 영속성 컨텍스트에 저장해 entity를 영속화할 때의 최초 정보들이다.

가져온 entity 최초 정보를 가지고 로직 중간에 수정한 데이터들과 비교를 하는 것이다.

 

스냅샷 정보와 entity에서 바뀐 정보를 비교하여 최초 정보에서 바뀐 부분들을 기준으로 update 쿼리를 작성하게 되고

update 쿼리는 잠시 특정 저장소에 담겨져 있다가 마지막에 한꺼번에 DB로 요청을 보내게 된다. (JPA 쓰기지연 기능)

@Transactional
public void updateEntityData() {
	// 메소드가 끝나면 트랜잭션 커밋이 발생 
    // -> flush (변경감지 작동 후 쓰기지연을 통해 update 쿼리 요청)
}

 

JPA의 변경감지를 이용하기 위해서는 다음과 같은 조건이 있다.

  • 변경하려는 Entity가 영속 상태여야 한다. 즉, 영속성 컨텍스트 안에서 관리되는 상태
  • 트랜잭션 안에 묶여 있어야 한다. 
  • 트랜잭션이 제대로 커밋되어야 한다. 그래야 flush가 작동하기 때문이다.

 

✅ @Transactional 사용과 변경 감지

 

@Transactional이 없는 코드에서는 변경감지가 잘 작동 될까 ? 당연히 아니다!!! 

위에서 JPA 변경감지가 작동되기 위한 조건 중에는 Entity가 영속 상태여야 하고 트랜잭션 안에 묶여 있어야 한다는 점이 있었다.

 

@Transactional이 있는 경우에는 해당 메소드가 트랜잭션으로 묶여있기 때문에 

메소드가 끝나는 지점에 트랜잭션 커밋이 발생하게 되고 flush가 자동으로 작동하게 된다.

 

하지만 해당 어노테이션이 없으면 repository 메소드에 @Transactional 어노테이션이 잡혀 있더라도 

해당 쿼리메소드에만 트랜잭션이 적용이 되기 때문에 메소드가 끝나도 트랜잭션 커밋이 일어나지 않는다. 

 

그렇게 때문에 servcie 단에서 트랜잭션을 묶지 않고 repository 단위로 트랜잭션을 묶는 전략을 사용한다면

update 작업에서 마지막 save 처리를 해줘야 한다.