개인 공부 (23.07~

[JPA] dirty checking 더티체킹 간단 이해

Song쏭 2023. 9. 9. 21:46

JPA의 더티 체킹은 엔터티의 상태 변화를 감지하고,

이 변화를 데이터베이스에 자동으로 반영하는 기능이다.

 

따라서, JPA를 사용하면 엔터티의 데이터를 변경한 후 

별도로 update 메서드를 호출할 필요가 없다.

 

JPA의 영속성 컨텍스트가 관리하는 엔터티에 대해 변경이 감지되면, 해당 트랜잭션이 커밋될 때 변경 사항을 데이터베이스에 반영하는 것이다.


예시로

EntityManager를 사용한 순수JPA

JpaRepository 인터페이스를 사용한 Spring Data JPA

이렇게 2가지를 기준으로 비교해보았다.

 

EntityManager를 사용한 순수JPA

아래 코드를 보면

 findById로 단건 조회를 한다. 단건조회 한 값은 Hotel 엔터티에 담는다. 

requestDto의 값을 단건조회 시에 담았던 Hotel 엔터티에 셋팅한다.

그리고 Hotel 엔터티의 값을 사용하여 Repository의 update메서드를 호출하여 DB에 담는다. 

DB에 잘 담은 값을 Hotel 엔터티로 받아서 

HotelResponseDto형식으로 컨트롤러에 전달하는 코드이다. 

이렇게 Repository에 내가 만든 update()메서드를 통해서 DB에 수정 값이 담기게 되는 것을 알 수 있다.

@Service
public class HotelService {

    private final HotelRepository hotelRepository;

    public HotelResponseDto modifyHotel(int hotelId, HotelRequestDto requestDto) {
    
        Hotel hotel = hotelRepository.findById(hotelId)
                .orElseThrow(() -> new EntityNotFoundException("Hotel not found with id " + hotelId));

        modifyStringIfNotNull(requestDto.getName(), hotel::setName);
        modifyStringIfNotNull(requestDto.getType(), hotel::setType);
        modifyStringIfNotNull(requestDto.getAddress(), hotel::setAddress);
        modifyStringIfNotNull(requestDto.getContact(), hotel::setContact);
        modifyStringIfNotNull(requestDto.getEmail(), hotel::setEmail);
        modifyStringIfNotNull(requestDto.getDescription(), hotel::setDescription);

        modifyIntIfNotZero(requestDto.getStar(), hotel::setStar);
        modifyIntIfNotZero(requestDto.getMinPrice(), hotel::setMinPrice);
        modifyIntIfNotZero(requestDto.getMaxPrice(), hotel::setMaxPrice);
        modifyIntIfNotZero(requestDto.getRooms(), hotel::setRooms);

        Hotel update = hotelJpaRepository.update(hotelId, hotel);
        return new HotelResponseDto(update);
    }

}

 

이번에는

JpaRepository 인터페이스를 사용한 Spring Data JPA

예시이다.

아래 코드를 보면 

먼저 해당 서비스 클래스에서 @Transactional 어노테이션이 필요하다.

엔터티의 상태가 변경되었기 때문에

트랜잭션이 종료될 때(modifyHotel() 실행이 끝나고 @Transactional 어노테이션이 종료될 때),

JPA는 더티 체킹을 통해 이 변경을 감지하고, 

해당 엔터티를 DB에 자동으로 update한다.

만약 @Transactional 어노테이션이 적용되어 있지 않다면, 더티 체킹이 동작하지 않을 수 있다.

따라서 해당 서비스 메서드나 서비스 클래스에 @Transactional 어노테이션이 적용되어 있는지 확인하는 것은 중요하다.

 

DB에 자동으로 update가 되니,

자연스럽게 Hotel update = hotelJpaRepository.update(hotelId, hotel); 이 부분은 필요없게 된다.

그리고 Hotel 엔터티에 요청값이 셋팅 되었으니, return 부분에서도 그대로 매개변수값으로 사용이 가능하다.

@Service
@Transactional //더티체킹을 위해 필요.
public class HotelService {

    private final HotelJpaRepository hotelJpaRepository;
    
    public HotelResponseDto modifyHotel(int hotelId, HotelRequestDto requestDto) {

        Hotel hotel = hotelJpaRepository.findById(hotelId)
                .orElseThrow(() -> new EntityNotFoundException("Hotel not found with id " + hotelId));

        modifyStringIfNotNull(requestDto.getName(), hotel::setName);
        modifyStringIfNotNull(requestDto.getType(), hotel::setType);
        modifyStringIfNotNull(requestDto.getAddress(), hotel::setAddress);
        modifyStringIfNotNull(requestDto.getContact(), hotel::setContact);
        modifyStringIfNotNull(requestDto.getEmail(), hotel::setEmail);
        modifyStringIfNotNull(requestDto.getDescription(), hotel::setDescription);

        modifyIntIfNotZero(requestDto.getStar(), hotel::setStar);
        modifyIntIfNotZero(requestDto.getMinPrice(), hotel::setMinPrice);
        modifyIntIfNotZero(requestDto.getMaxPrice(), hotel::setMaxPrice);
        modifyIntIfNotZero(requestDto.getRooms(), hotel::setRooms);
        
//JpaRepository를 상속받은 Repository로 할 때는 더티체킹이 되어서 사용할 필요가 없다.
//        Hotel update = hotelJpaRepository.update(hotelId, hotel); 
        return new HotelResponseDto(hotel);
    }
}