2011-01-18 4 views
11

(제거 관련이없는 물건을)은 다음과 같이 나는 정의 된 두 개의 엔티티 빈을 다음과 같이정지 최대 절전 모드에서가 변경되지 않은 경우 컬렉션을 업데이트

@Entity 
@Table(...) 
public class MasterItem implements java.io.Serializable { 

    private Set<CriticalItems> criticalItemses = new HashSet<CriticalItems>(0); 

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "masterItem", orphanRemoval = true, 
      cascade = {javax.persistence.CascadeType.DETACH}) 
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE}) 
    public Set<CriticalItems> getCriticalItemses() { 
     return this.criticalItemses; 
    } 
} 

CriticalItems가 정의 :

@Entity 
@Table(...) 
public class CriticalItems implements java.io.Serializable { 

    private MasterItem masterItem; 

    @ManyToOne(fetch = FetchType.LAZY, optional = false, 
      cascade = {javax.persistence.CascadeType.DETACH}) 
    @Cascade({CascadeType.SAVE_UPDATE}) 
    @JoinColumn(name = "mi_item_id", nullable = false) 
    public MasterItem getMasterItem() { 
     return this.masterItem; 
    } 
} 

그리고에 내 DAO 코드 - 나는이 방법을 가지고있다.

public MasterItem load(int id) { 
    MasterItem results = (MasterItem) getSessionFactory().getCurrentSession() 
     .get("com.xxx.MasterItem", id); 

} 

public void save(MasterItem master) { 
    // master has been changed by the UI since it 
    getSessionFactory().getCurrentSession().saveOrUpdate(master); 
} 

내가 MasterItem을로드 할 때 올바르게로드된다. d는 지정된대로 데이터가있는 CriticalItems Set를로드합니다. 그런 다음이 데이터를 UI로 보내고 업데이트 된 복사본을 얻은 다음 계속 유지하려고합니다. 사용자는 MasterItem 개체의 필드를 업데이트하지만 그 안에있는 어떠한 항목도 건드리지 않습니다. 변경되지 않은 상태로 유지됩니다.

내 save() 메소드가 호출 될 때, Hibernate는 어떤 식 으로든 변경된 것이 없더라도 Set of CriticalItems의 각 항목에 대해 SQL 업데이트를 보내야한다고 주장하고 있습니다.

일부 파기 후, 여기 내가 생각하는 바가 있습니다. Hibernate는 saveOrUpdate()를 수행 할 때 MasterItem 객체가 detached 상태에 있음을보고 디스크에서 다시로드하려고 시도합니다. 그러나 이렇게 할 때 준비된 문 (시작시 Hibernate에 의해 자동 생성됨)을 사용하는 것처럼 보입니다.이 준비된 문은 CriticalItems 데이터에 조인을 시도하지 않습니다.

그래서 Hibernate는 전체 CriticalItems 세트를 가진 나의 업데이트 된 MasterItem 객체를 가지고 있지만, collections없이 그것의 "previousState"객체로 MasterItem을 사용한다. 따라서 모든 CriticalItem은 SQL을 통해 업데이트됩니다 (삽입되지 않음, 그 자체로 흥미 롭습니다).

이 동작을 일으키는 특수 효과에서 내가 뭔가를 했습니까? 나는 인터셉터를 사용하여 아이템이 실제로 변경되었거나, Hibernate의 디폴트 알고리즘을 오버라이드하도록 더티 플래그를 변경했다는 것을 알았다. 그러나 이것은 Hibernate가 내 간섭없이 독자적으로 처리해야하는 것으로 보인다.

어떤 통찰력도 인정 될 것입니다.

업데이트 : 의견을 바탕으로 saveOrUpdate()와 merge()의 차이점을 이해하고 있다고 생각합니다. 나는 saveOrUpdate()가 모든 경우에 SQL INSERT 또는 SQL UPDATE를 초래할 것이고, 객체가 영속 상태에서 변경된 경우에만 이론상 병합을 업데이트 할 것이라고 생각하지만이를 확인하기 위해 최대 절전 모드 SQL SELECT를 통해 먼저 객체를 다시로드해야합니다.

그래서 내 코드로 돌아가서 saveOrUpdate()를 merge()로 변경하면 작동 할 것이라고 생각했지만 실제로는 그렇지 않습니다.

내가 병합(), 내가

org.springframework.orm.hibernate3.HibernateSystemException: could not initialize proxy - no Session; nested exception is org.hibernate.LazyInitializationException: could not initialize proxy - no Session 

을 얻고 있었다를 사용하는 경우 그러나 나는 될 saveOrUpdate로 다시 변경 한 경우는 괜찮 았는데().

나는 마침내 왜 - 내 @Cascade 어노테이션 (우)에 CascadeType.MERGE을 포함시키지 않았 음을 알게되었습니다. 일단 내가 그것을 고쳤다면, 예외는 사라졌다.

+0

낙관적 인 잠금 열 (date od int 형식의 @version 주석이 달린 열) – lweller

답변

16

에 개정 열 (낙관적 잠금)을 추가하려고합니다. Christian Bauer and Gavin King's Java Persistence with Hibernate에서

(I가 최대 절전 모드 문서에서이 문제의 명확한 설명을 찾을 수 없습니다) :

업데이트는() 메소드는 항상, 에서 데이터베이스를 객체의 지속 상태에 대한 업데이트를 강제로 SQL UPDATE 스케줄링.
...
update()로 전달되기 전이나 후에 항목 개체가 수정되는지는 중요하지 않습니다.
...
최대 절전 모드 은 항상 개체를 더티로 처리하고 SQL UPDATE를 예약합니다. 플러시 중에 이 실행됩니다.

반면에 merge()은 데이터베이스를 먼저 쿼리하고 상태가 변경되지 않은 경우 업데이트를 수행하지 않습니다.

그래서 Hibernate가 데이터베이스를 먼저 조회하기를 원한다면 merge()을 사용할 필요가있다. (기본 동작은 엔티티에서 @org.hibernate.annotations.Entity(selectBeforeUpdate = true)을 지정하여 update()의 기본 동작을 오버라이드 할 수있다.)

+0

정확히 내가 무엇을 찾고 있었는지 - 감사합니다 백만! – Jay

+0

FYI - update() 또는 saveOrUpdate() 사이에 [차이점을 더 자세히 설명하는이 게시물]이 있습니다 (http://stackoverflow.com/questions/170962/nhibernate-difference-between-session-merge-and-session-saveorupdate)() 및 병합(). – Jay

1

은 실체

update()merge() 사이의 의미 론적 차이의
@Version 
Date lastModified; 
+0

FYI - 도움이되었지만 데이터베이스에 버전 열이 없습니다. – Jay

관련 문제