2010-02-17 8 views
4

다음 코드는 Spring + Hibernate를 사용하여 Item 개체를 db에 삽입하려고 시도합니다. 항목에는 기본 키로 정수 ID 필드가 있고 고유 제한 조건 (단순화 된 예)이 적용되는 name 열이 있습니다.
항목의 ID가 null (항목이 일시적 임)이지만 name 필드의 고유 제한 조건으로 인해 삽입이 여전히 실패 할 수 있음을 알고 있습니다.삽입 실패 후 Hibernate 세션 상태 복구

try { 
    getHibernateTemplate().save(myItem); 
    getHibernateTemplate().flush(); // exception thrown here if the name is not unique 
} 
catch (DataIntegrityViolationException e) { 
    Item itemFromDb = (Item) getHibernateTemplate() 
    .find("from Item item where item.name = ?", 
      myItem.getName()).get(0); 
    // 
    // copy some properties from myItem to itemFromDb 
    // 
    getHibernateTemplate.update (itemFromDb) 
} 

는 내가 삽입이 실패 할 경우 업데이 트를 발행하려고 그 이유는, 모두 하나의 트랜잭션에서 여러 항목에 대한 루프에서 실행하려면이 코드가 필요합니다.

그러나 삽입이 실패한 후 최대 절전 모드 세션이 이상한 상태이며 db에서 항목을 가져 오기 위해 select 문을 실행하면 원래 예외가 발생합니다.

삽입이 실패한 후에 session.evict()을 사용해 보았지만 아무 소용이 없었습니다. 어떤 생각?

이것은 선택 실패 후 콘솔에 표시됩니다. Hibernate는 select 문에서 실패하지만 여전히 이전의 insert 문에 대한 정보를 출력한다.

WARN [http-8080-1] hibernate.util.JDBCExceptionReporter (JDBCExceptionReporter.java:77) - SQL Error: 2627, SQLState: 23000 
ERROR [http-8080-1] hibernate.util.JDBCExceptionReporter (JDBCExceptionReporter.java:78) - Violation of UNIQUE KEY constraint 'unq_Item_name'. Cannot insert duplicate key in object 'items'. 
ERROR [http-8080-1] event.def.AbstractFlushingEventListener (AbstractFlushingEventListener.java:301) - Could not synchronize database state with session 
org.hibernate.exception.ConstraintViolationException: could not insert: [com.sample.Item] 
    at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:71) 

P. 최대 절전 모드의 saveOrUpdate 메소드에 대해 알려주지 마라.

+0

아마도 삽입/업데이트 전에 선택을하는 것이 더 좋을까요? –

답변

3

하이버 네이트는 데이터베이스 예외가 발생하면 세션에 대해 아무 것도 보증하지 않는다. 롤백해야합니다. Bozho의 제안을 이해한다면, 삽입이 실패하지 않도록 먼저 이름 열에서 읽어야한다고 말하고 있습니다. 나 한테 의미가있어.

+0

당신의 대답은 그리 낙관적이지는 않지만, 불행히도 저는 그것이 틀린 것 같아요, 특히 예외 후에 세션 상태에 관한 부분이 맞다고 생각합니다. 그래서 나는 그걸로 표시하고있다. – Yoni

+0

그런 유감스러운 죄송합니다. 합성 열쇠가 당신을 도울까요? 유일성 제약 조건을 가진 기본 키와 키를 모두 포함 할 수 있습니다. 결과 SQL은 여전히 ​​유사하지만 코드를 단순화하고 L2 캐시 (쿼리 캐싱이 필요하지 않음)를보다 잘 활용할 수 있다고 생각합니다. – ShabbyDoo

1
getHibernateTemplate().merge(myItem); 

병합 - 동일한 식별자를 갖는 영구 객체에 지정된 오브젝트의 상태를 복사한다. 현재 세션과 연관된 지속 인스턴스가 없으면로드됩니다. 지속 인스턴스를 돌려줍니다. 지정된 인스턴스가 저장되지 않은 경우 복사본을 저장하고 새로 영구 인스턴스로 반환하십시오. 주어진 인스턴스는 세션과 연관되지 않습니다. 이 작업은 연관이 cascade = "merge"로 매핑되는 경우 연관된 인스턴스에 계단식으로 연결됩니다.

+0

@Bozho, 답장을 보내 주셔서 감사합니다. 이 점을 이해하면 저장/업데이트하기 전에 개체를 선택해야하며 성능상의 이유로이를 피하려고합니다. 항목이 일시적이기 때문에 merge는 기존의 식별자와 동일한 식별자를 가지고 있다면 이미 수행 한 (그리고 실패한) 내용을 저장하려고 시도합니다 ( – Yoni

+0

). – Bozho