2014-01-22 16 views
0

을 저장하는 동안 다음 코드org.hibernate.StaleObjectStateException : 도메인 클래스

studentInstance.addToAttempts(studentQuizInstance) 
studentInstance.merge() 
studentInstance.save(flush:true) 

하고 위의 코드의 마지막 줄에 다음과 같은 예외를 throw는

org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.easytha.Quiz#1] 

나는 몇 가지를 본 한 스레드의 동일한 문제를 논의하고 그 그들에 따르면 studentInstance.withTransactionstudentInstance.withTransaction을 사용하여 노력하고 또한 서비스의 범위를 요청하지만 지금까지 도움이 변경되었습니다.

이것은 20-30 명이이 코드를 동시에 호출 할 때만 발생하기 때문에 확실히 스레드 문제입니다.

+0

나는 비슷한 상황에 처해있었습니다. studentInstance에서 호출하기 전에 studentQuizInstance에서 .merge()를 호출하면 어떻게됩니까? – marko

+0

이 같은 상황에 대한 깔끔한 디버깅 트릭 몇 가지 있습니다 (이것은 날 비슷한 문제를 해결하는 데 도움이 생각) - http://stackoverflow.com/questions/536601/what-are-your-favorite-grails-debugging -tricks – marko

답변

1

여기에서 핵심적인 문제는 관계가 양방향이며 양면이 변경되고 버전이 지정된다는 것입니다. addToAttempts을 호출 할 때 hasMany 속성에 의해 생성 된 attempts 컬렉션은 null 인 경우 인스턴스가 추가되고 인스턴스의 학생 필드가 소유 학생으로 설정되어있는 경우 새 비어있는 컬렉션으로 초기화되어 인 메모리 상태는 나중에 데이터베이스에서 모든 것을 다시로드하는 것과 같습니다. 그러나 버전 관리 (낙관적 잠금)를 사용하도록 설정하면 양측이 변경되었으므로 버전 충돌이 발생합니다. 따라서 동시 사용자 두 명 사이의 콜렉션과 겹치는 경우이 오류가 발생합니다. 그리고 실제적입니다. 명시 적으로 잠금을 설정하지 않거나 낙관적 잠금을 사용하지 않으면 이전 업데이트를 잃을 위험이 있습니다.

하지만 이것은 모두 인공적입니다. 이것은 다 대다 (many-to-many)처럼 보이므로 원하는 것은 학생과 시도를 가리키는 조인 테이블에 새로운 행을 추가하는 것입니다. Grails는 Hibernate가 변경을 감지하는 콜렉션을 설정함으로써 이것을 수행하지만, 이것은 실제로 부작용을 이용한다. 또한 대규모 컬렉션의 경우 매우 비쌉니다. 내가 addToAttempts에 대한 전화에 대해 위에서 한 부분을 버렸다. 거기에 이미 인스턴스가있는 경우 데이터베이스가 필요하지 않더라도 데이터베이스에서 모든 인스턴스가 검색됩니다. Grails는 N 개의 이전 요소 (N은 매우 큰 숫자 일 수 있음)를로드하고 새로운 N + 1 st을 추가합니다. 따라서 Hibernate는 새로운 요소를 감지 할 수 있습니다. 원하는 것은 하나의 행을 삽입하는 것이고, 많은 양의 데이터베이스 트래픽으로 끝납니다.

수정 사항은 mergewithTransaction 호출 또는 여기 또는 다른 곳에서 찾은 다른 임의적 인 내용으로 분산되어서는 안됩니다. 동시 액세스를 제거하는 것입니다. 완전히 인공적이기 때문에 운이 좋았습니다. 이 이야기를 들으십시오. 잠깐 동안 그랬던 것처럼 슬픈 듯이 현재의 Grails와 관련성이 있습니다. 컬렉션을 제거하고 훨씬 더 합리적인 방법으로 대체하는 방법을 설명합니다. http://www.infoq.com/presentations/GORM-Performance

+0

그러나이 경우이 작업은 항상 실패해야합니다. – Sap

+0

아니요, 조인 테이블에 새 레코드를 추가하는 경우 병행 성 문제가 없습니다. 한 FK가 공유 시도를 가리키고 다른 하나는 현재 학생을 가리 킵니다. 다른 동시 삽입은 해당 학생의 ID를 사용합니다. –

+0

http://grails.org/plugin/spring-security-core 플러그인에서 명시 적으로 조인 테이블을 도메인 클래스로 매핑하는 예를 볼 수 있습니다.이 방법을 사용하여 많은 사용자와 역할을 매핑합니다. 'BuildConfig.groovy' ('mavenRepo 'http : //repo.spring도 포함)의'plugins' 섹션에'compile'을 추가하면 : spring-security-core : 2.0-RC2 "io/milestone/"을'repositories' 블록에서) 실행하고'grails compile'을 실행하면,'grails s2-quickstart test User Role'에서 UserRole.groovy 도메인 클래스를 볼 수 있습니다 .. –

관련 문제