2014-08-27 7 views
7

컨트롤러 로직 :JPA와 StaleObjectStateException를 해결하고 최대 절전 모드하는 방법

def updateObject() { 

    Object o = Object.get(params.id as Long) 

    o.otherObjects.clear() 

    objectDataService.saveObject(o.id) 

    OtherObject newObject = new OtherObject; 

    o.addToOtherObjects(newObject) 

    objectDataService.saveObject(o.id) 

} 

ServiceLogic

def saveObject(long profileId) { 
    o.save(flush:true) 
} 

이 그냥 작동 할 경우 90 %에

을 무슨.

문제

ERROR errors.GrailsExceptionResolver - StaleObjectStateException occurred when processing request: [GET] /controller/updateObject - parameters: 
stuff[]: data 
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.path.Object#1]. 
Stacktrace follows: 
Message: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.path.Object#1] 

내가 관련된 질문을 읽고 당신은 아는 merge 전화를 발견했다. 그것은 약 50 %의 경우를 해결했지만 전부는 아닙니다.

+0

이 로직을 서비스 메소드로 옮긴다면 모든 트랜잭션이 동일하게 처리되었으므로 여기서 더 나아지지 않을까요? –

+0

서비스를 오버로드하지 않으려합니다. 이해가 되겠습니까? –

+0

비즈니스 로직을 서비스로 옮기고 컨트롤러를 가능한 한 얇게 만드는 것이 더 좋습니다 (코드의 줄이 작아짐에 따라 씬이 줄어 듭니다). 이렇게하면 필요할 때마다 코드를 재사용하고 테스트에도 많은 시간을 절약 할 수 있습니다. –

답변

2


은 우리에게 몇 가지 다른 방법은 마지막으로 정기적으로 발생하는에서 StaleObjectException 해결 :

object = object.refresh() 

그들에게 우리의 StaleObjectExceptions의 대부분을 해결 검색 한 후 개체를 새로 고침. 특히 누군가가 다른 곳에서 같은 대상에 대해 작업하고 일부 구성원을 변경했을 가능성이있는 경우 (대부분의 문제점은 수집 단원에게 도착 함).

전체 프로젝트의 안정성 :

wrongly linked resources 

우리는 우리가 실제로 필요하지 않은 특정 리소스 파일에 404을했고, 따라서 몇 시간 동안 그것을 무시했습니다. 누락 된 파일은 세션이 열린 상태로 유지되도록하여 StaleObjects를 좌우로 조작합니다.

따라서 일반적인보다 더 직면 사람에게 힌트로서 (일부 StaleObjects은 항상 발생할 수 있습니다 - 답변 위 참조) StaleObjectExceptions을 : 모든 자원이 제대로 연결되어 있는지 확인하고 개발자 도구 (크롬 F12)는하지 않습니다 누락 된 파일을보고하십시오.

4

StaleObjectStateException은 다른 프로젝트에서 자연히 발생할 수 있습니다. 두 개의 동시 트랜잭션이 동일한 엔티티 버전을로드 할 때마다 해당 엔티티가 변경되어 낙관적 인 잠금 충돌 실패로 인해 마지막 실행 스레드로 실패하게됩니다. 귀하의 경우에는

이 문제를 용이하게 할 수 JPA 경계의 외부에 별도의 호출은,이 :

Object o = PObject.lock(profileId) 

각 트랜잭션은 쓰레드 범위이며 세션 내에서 발생하는, 그래서 두 개의 경쟁 스레드가 계속됩니다 같은 엔티티 ID의 2 개의 Object 참조 낙관적 인 잠금 목표는 다른 명시 적 잠금 메커니즘없이 효과적으로 작동합니다.

수정 된 엔터티 참조를 saveObject 버전으로 보내면이 예외가 계속 발생하면 두 개의 경쟁 스레드가 동일한 엔터티를 수정할 가능성이 높음을 의미합니다.

이 시나리오에서는 optimistic fail-fast approach이 아닌 대기를 포함하므로이 경우 pessimistic lock을 사용하면 더 나은 결과를 얻을 수 있습니다.

+0

내 코드가 업데이트되었습니다. .lock() 호출이 더 이상 존재하지 않습니다. 나는 여전히 통화의 20 % 정도의 시간 동안 발생한다. 그것은 동시 적이 지 않습니다. (나는 그것을 혼자서 하나의 사용자로 만들어서 동시 편집을하지 않습니다.) –

5

StaleObjectStateException는 :

으로 이미이 예외에 대한 주석. 플러시 (명시 적 또는 암시 적) 중에 잘못된 버전이 지정된 도메인 개체가 세션에있는 경우 버전 번호가 충돌하지만 데이터베이스에 유지되지 않습니다. 그런 다음 버전 번호가 데이터베이스의 버전과 일치하지 않고 StaleObjectStateException을 던지므로 hibernate가 유효하고 다시 저장되고 플러시 될 때 최대 절전 모드로 간주합니다.

이렇게하면 해결할 수 있습니다.

  1. 당신은 그 다음 1의 경우이 프로그램을 사용하여 업데이트해야, 업데이트를하기 전에 버전 값을 찾을 수있다. 특정 업데이트 작업.
  2. @version 주석을 사용합니다[email protected] 소개

:

낙관적 잠금의 버전 번호 메커니즘은 @Version 주석을 통해 제공됩니다. 예 : @Version 주석 여기

@Entity 
public class Flight implements Serializable { 
... 
    @Version 
    @Column(name="OPTLOCK") 
    public Integer getVersion() { ... } 
} 

은 버전 속성은 OPTLOCK 컬럼에 매핑하고, 엔티티 관리자는 덮어 쓰기 될 업데이트 손실 업데이트 충돌을 검출하고 예방하는 데 사용되는 마지막 커밋 승리 전략 @version

Grails에 대한 자세한 내용은 아래 링크를 참조하십시오. Git 허브 코드가 있습니다. test for GRAILS-8937: HibernateOptimisticLockingFailureException

+0

당신은 당신의 솔루션을 자세히 설명 할 수 있습니까? 어떻게 @version 어노테이션을 사용하고 첫 번째 솔루션에서 무엇을 업데이트합니까? –

+1

@ SebastianFlückiger : 그 예외를 해결하기위한 접근 방식입니다. 버전 사용법을 이해하기 위해 최대 절전 모드 프레임 워크에 대한 자세한 정보를 확인하십시오. 버전에 대한 세부 정보도 업데이트되었습니다. 다른 링크는 Grails 관련 솔루션에 대해 설명합니다. 테이블에서 코드를 작성하여 해당 버전을 업데이트하십시오. – Rudra