1. 더티 체킹 개념
- 더티 체킹(Dirty Checking): JPA의 기능으로, 영속성 컨텍스트에 담긴 엔티티의 상태 변화를 자동으로 감지하여 트랜잭션 커밋 시점에 변경 사항을 데이터베이스에 반영하는 메커니즘.
2. 영속성 컨텍스트(Persistence Context)
- 정의: 엔티티를 영속성 컨텍스트에 담아 관리하는 일종의 캐시.
- 역할: 엔티티의 상태를 추적하고, 변경된 엔티티를 데이터베이스에 자동으로 반영.
3. 엔티티의 생명주기
- 비영속 상태(New/Transient): 엔티티가 영속성 컨텍스트에 담기지 않은 상태.
- 영속 상태(Managed): 엔티티가 영속성 컨텍스트에 담긴 상태. 더티 체킹의 대상.
- 준영속 상태(Detached): 영속성 컨텍스트에서 분리된 상태.
- 삭제 상태(Removed): 엔티티가 삭제된 상태.
4. 더티 체킹 동작 원리
- 변경 감지: 영속성 컨텍스트는 트랜잭션이 시작될 때 엔티티의 스냅샷을 저장. 트랜잭션 종료 시 스냅샷과 비교하여 변경 사항을 감지.
- 트랜잭션 커밋 시점: 감지된 변경 사항을 데이터베이스에 반영.
5. 트랜잭션 내에서의 더티 체킹 예제
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void updateUserName(Long userId, String newName) {
// 1. 엔티티 조회: 영속성 컨텍스트에 담김
User user = userRepository.findById(userId).orElseThrow(() -> new EntityNotFoundException("User not found"));
// 2. 엔티티 속성 변경: 더티 체킹 대상
user.setName(newName);
// 3. 트랜잭션 커밋: 변경 사항이 자동으로 데이터베이스에 반영
}
}
6. 주요 고려사항
- 트랜잭션 사용: 더티 체킹은 트랜잭션 내에서 작동.
- 영속성 컨텍스트: 엔티티가 영속성 컨텍스트에 있어야 더티 체킹이 가능.
- 명시적 저장: 경우에 따라 명시적으로 save 메서드를 호출하여 변경 사항을 저장할 수도 있음.
7. 신규 엔티티와 더티 체킹
- 신규 엔티티는 save 호출 시 영속성 컨텍스트에 담기며, 그 이후 변경 사항은 더티 체킹의 대상이 됨.
@Transactional
public void createUser(String name) {
User user = new User();
user.setName(name);
userRepository.save(user); // 엔티티가 영속성 컨텍스트에 담기고 데이터베이스에 저장됨
}
8. 동시성 이슈
* 동시성 문제는 여러 사용자가 동시에 동일한 데이터를 수정할 때 발생할 수 있음. 예로 두 사용자가 동일한 엔티티를 동시에 수정하고 저장하는 경우 데이터 일관성 문제가 발생할 수 있다.
9. 해결(제어) 방법
- 여러가지 제어 방법이 있고 락을 사용하는 방법 두가지만 간략하게 정리했습니다. 이런 방식이 있다 정도만 알고 활용하면 될 것 같습니다.
1. 낙관적 락(optimistic lock) : 가장 일반적인 해결 방법으로, Optimistic Locking을 활용하여 충돌을 방지한다.
엔티티에 버전 필드(ex. @Version)를 추가하여 버전이 일치하지 않는 경우에는 충돌로 간주하고 예외를 발생시키거나 복구한다.
- 사용 방법: 낙관적 락은 여러 사용자가 동시에 데이터를 수정할 가능성이 낮을 때 사용된다. 엔티티를 읽을 때 버전 번호나 타임스탬프를 체크하여 충돌을 감지하고, 업데이트시에만 실제로 데이터베이스에서 해당 엔티티의 상태를 확인하고 업데이트한다.
- 장점: 동시성이 높은 환경에서 성능이 좋다. 일반적인 상황에서 데이터베이스 락을 걸지 않고도 충돌을 피할 수 있다.
- 적합한 상황: 대부분의 경우에서 사용할 수 있으며, 충돌이 발생할 가능성이 낮은 경우 적합하다.
- optimistic lock 예제)
@Entity
public class Entity {
@Id
private Long id;
@Version
private Long version; // Optimistic Locking을 위한 버전 필드
// getters, setters, other fields...
}
2. 비관적 락(pessimistic lock) : 특정 엔티티를 읽을 때 잠금을 거는 방식. 이 방법은 특히 긴 트랜잭션 내에서 엔티티의 상태가 변경되지 않도록 보장한다. "LockModeType.PESSIMISTIC_WRITE"를 사용하여 엔티티를 잠근다.
- 사용 방법: 비관적 락은 데이터가 수정될 가능성이 크거나 긴 시간 동안 데이터를 잠금(lock) 해야 할 때 사용된다. 데이터를 읽을 때 바로 데이터베이스에서 잠금을 걸어 다른 사용자가 해당 데이터를 수정할 수 없도록 한다.
- 장점: 데이터 일관성을 보장할 수 있으며 긴 트랜잭션에서 데이터가 변경되지 않도록 보호할 수 있다.
- 적합한 상황: 특정 데이터의 동시 수정이 빈번하거나 데이터 일관성을 엄격하게 유지해야 하는 경우에 적합하다.
- pessimistic lock 예제)
public interface EntityRepository extends JpaRepository<Entity, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT e FROM Entity e WHERE e.id = :id")
MyEntity findByIdWithPessimisticWriteLock(@Param("id") Long id);
}
9. 더티 체킹(Dirty Checking) 정리
* JPA 더티 체킹은 영속성 컨텍스트에서 엔티티의 상태 변화를 자동으로 감지하여 데이터베이스에 반영하는 핵심 기능.
1. 자동 변경 감지: 트랜잭션 내에서 영속성 컨텍스트에 있는 엔티티의 속성 변화를 추적한다
2. 트랜잭션 커밋 시점: 트랜잭션이 커밋될 때, 더티 체킹에 의해 변경된 엔티티의 상태가 데이터베이스에 자동으로 반영된다
3. 영속성 컨텍스트 필요: 엔티티가 영속성 컨텍스트에 관리되어야만 더티 체킹이 작동한다
4. 신규 엔티티의 저장: 신규 생성된 엔티티는 save() 메서드를 통해 데이터베이스에 저장되고, 이후에 발생하는 변경 사항도 더티 체킹을 통해 관리된다
5. 명시적 저장: 경우에 따라 명시적으로 save() 메서드를 호출하여 변경 사항을 데이터베이스에 반영할 수 있다
10. 동시성 해결 정리
1. 낙관적 락 : 충돌이 적을 때 사용하며, 성능을 위해 데이터베이스 락을 피하고 업데이트 충돌을 감지한다.
2. 비관적 락 : 충돌이 발생할 가능성이 크거나 데이터 일관성을 강제로 유지해야 할 때 사용하며, 데이터베이스 락을 활용 하여 데이터를 보호한다람쥐.
'JPA' 카테고리의 다른 글
[JPA] 엔티티 설계시 주의점 (0) | 2021.11.26 |
---|---|
[JPA] Pageable 사용 시 Page<Entity>를 Page<DTO>로 변환하기(Builder, 람다식 사용) (1) | 2021.10.26 |
[JPA] 변경 감지(Dirty Checking) 와 병합(merge) (0) | 2021.09.16 |
[JPA] Spring Data JPA를 이용한 페이징 처리 (0) | 2021.08.19 |