1

2 개의 동시 스레드가 동시에 (스프링) 트랜잭션 서비스를 입력합니다.엔티티를 삭제할 때 staleObjectStateException을 피하십시오.

Hibernate를 사용하면 서비스 메소드가 일부 엔티티를로드하고 처리 한 다음 해당 엔티티를 찾아서 DB에서 삭제합니다. 의사 코드는 다음과 같습니다 :

@Transactional 
public MyEntity getAndDelete(String prop) { 
    List<MyEntity> list = (List<MyEntity>)sessionFactory 
     .getCurrentSession() 
     .createCriteria(MyEntity.class) 
     .add(Restrictions.eq("prop", prop)) 
     .list(); 

    // process the list, and find one entity 
    MyEntity entity = findEntity(list); 
    if (entity != null) { 
     sessionFactory.getCurrentSession().delete(entity); 
    } 
    return entity; 
} 

경우 동시에 두 개의 스레드가 같은 매개 변수가 동일한 개체는 모두 delete를 호출합니다 "발견"할 것이다을 전달합니다. 세션 중 하나가 닫히면 org.hibernate.StaleObjectStateException이 실패합니다.

두 스레드 모두 예외가 발생하지 않고 엔티티를 반환하고 싶습니다. ,

@Transactional 
public MyEntity getAndDelete(String prop) { 
    List<MyEntity> list = (List<MyEntity>)sessionFactory 
     .getCurrentSession() 
     .createCriteria(MyEntity.class) 
     .add(Restrictions.eq("prop", prop)) 
     .list(); 

    // process the list, and find one entity 
    MyEntity entity = findEntity(list); 
    if (entity != null) { 
     // reload the entity with "select ...for update" 
     // to ensure the exception is not thrown 
     MyEntity locked = (MyEntity)sessionFactory 
      .getCurrentSession() 
      .load(MyEntity.class, entity.getId(), new LockOptions(LockMode.PESSIMISTIC_WRITE)); 
     if (locked != null) { 
      sessionFactory.getCurrentSession().delete(locked); 
     } 
    } 
    return entity; 
} 

내가 최대 절전 모드의 API에 따라 이후 load() 대신 get()를 사용이를 위해, 나는 엔티티를 삭제하기 전에, 다음과 같은 ("선택 ... 업데이트"로) 잠금 시도 get은 세션에 이미있는 경우 엔티티를 반환하고 load는 다시 읽어야합니다.

두 스레드가 동시에 위의 방법을 입력하면 그 중 하나가 잠금 단계를 차단하고 첫 번째 스레드가 트랜잭션을 닫으면 두 번째 스레드가 org.hibernate.StaleObjectStateException을 발생시키면서 awocken됩니다. 왜?

잠긴로드가 null을 반환하지 않는 이유는 무엇입니까? 내가 어떻게 이걸 얻을 수 있니?

답변

1

이 문제를 조사하는 데 시간을 할애하여 결국 어떤 일이 발생했는지 알았습니다.

PESSIMISTIC_WRITE 잠금은 세션에 이미로드 된 엔터티를 "잠금"하려고 시도하지만 DB에서 개체를 다시 읽지 않습니다. 호출을 디버깅하면서 entity == lockedtrue (Java 용어로) 반환됨을 확인했습니다. 두 변수 모두 동일한 인스턴스를 가리 킵니다.

최대 절전 모드로 강제로 엔터티를 다시로드하려면 먼저 세션에서 제거해야합니다. 그렇지 않으면 org.hibernate.ObjectNotFoundException가 발생 될 수 있기 때문에,

@Transactional 
public MyEntity getAndDelete(String prop) { 
    List<MyEntity> list = (List<MyEntity>)sessionFactory 
     .getCurrentSession() 
     .createCriteria(MyEntity.class) 
     .add(Restrictions.eq("prop", prop)) 
     .list(); 

    // process the list, and find one entity 
    MyEntity entity = findEntity(list); 
    if (entity != null) { 

     // Remove the entity from the session. 
     sessionFactory.getCurrentSession().evict(entity); 

     // reload the entity with "select ...for update" 
     MyEntity locked = (MyEntity)sessionFactory 
      .getCurrentSession() 
      .get(MyEntity.class, entity.getId(), new LockOptions(LockMode.PESSIMISTIC_WRITE)); 
     if (locked != null) { 
      sessionFactory.getCurrentSession().delete(locked); 
     } 
    } 
    return entity; 
} 

PESSIMISTIC_WRITE의 MUT는 get 대신 load와 함께 사용할 수 :

다음 코드는 트릭을 수행합니다.

+0

세션 가져 오기가 더 이상 사용되지 않습니다. 그것없이 다시로드하는 방법을 알고 있습니까? – Alison

관련 문제