StaleObjectStateException 해결..

Yangray·2025년 4월 4일

SpringData JPA

StaleObjectStateException

StaleObjectStateException은 주로 JPA(hibernate)에서 동시성 문제로 발생하는 예외
특히 낙관적 락(Optimistic Locking)을 사용하고 있는 엔티티에서 발생할 가능성이 높다

원인

  1. 낙관적 락(Optimistic Locking) 사용 중 충돌 발생
  • @Version 필드를 가진 엔티티를 수정할 때, 같은 데이터를 여러 트랜잭션이 수정하려 하면 예외가 발생한다.
  1. 세션과 엔티티 상태 불일치
  • Hibernate의 세션이 관리하는 엔티티의 상태와 DB의 상태가 다를 때 예외가 발생할 수 있다.
  1. 동시에 여러 요청이 같은 데이터를 수정하려고 할 때
  • 예를 들어, 사용자 A와 B가 동시에 같은 데이터를 수정하면 먼저 커밋된 트랜잭션 이후의 요청은 실패한다.

해결방법

1. 낙관적 락(Optimistic Lock) 적용 및 충돌 시 처리

@Version 필드를 사용하여 낙관적 락을 적용하면 데이터 충돌을 감지할 수 있습니다.

@Entity
public class ExampleEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Version  // 버전 필드 추가
    private Integer version;

    private String name;
}

2. 비관적 락(Pessimistic Lock) 사용

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT e FROM ExampleEntity e WHERE e.id = :id")
ExampleEntity findByIdWithLock(@Param("id") Long id);




@Transactional
public void updateEntityWithLock(Long id, String newName) {
    ExampleEntity entity = entityRepository.findByIdWithLock(id);
    entity.setName(newName);
    entityRepository.save(entity);
}
  • PESSIMISTIC_WRITE를 사용하면 데이터를 가져오는 순간 다른 트랜잭션에서 해당 데이터 수정이 차단된다.
  • 단, 트랜잭션이 길어질 경우 성능 저하와 데드락 위험이 있으므로 주의해야 함.

3. 세션 동기화 문제 해결

만약 Hibernate의 세션이 오래 유지되면서 StaleObjectStateException이 발생하는 경우, 명시적으로 세션을 갱신하는 것도 방법이 될 수 있다

@PersistenceContext
private EntityManager entityManager;

@Transactional
public void refreshEntity(Long id) {
    ExampleEntity entity = entityManager.find(ExampleEntity.class, id);
    entityManager.refresh(entity);  // 세션 동기화
}

🔥결론

  • @Version을 활용하여 낙관적 락을 적용하고 예외 발생 시 재시도 로직 추가
  • 동시 수정이 빈번한 경우 비관적 락(Pessimistic Lock) 사용
  • 세션과 DB 상태 불일치가 의심된다면 entityManager.refresh() 활용
profile
시작은 미약하나 그 끝은 창대하리라

0개의 댓글