title: Stop Hibernate from updating collections when they have not changed
body: <p>I have two entity beans defined as follows (unrelated stuff removed):</p>
  
  <pre><code>@Entity
  @Table(...)
  public class MasterItem implements java.io.Serializable {
  
    private Set&lt;CriticalItems&gt; criticalItemses = new HashSet&lt;CriticalItems&gt;(0);
  
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "masterItem", orphanRemoval = true,
              cascade = {javax.persistence.CascadeType.DETACH})
      @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE})
      public Set&lt;CriticalItems&gt; getCriticalItemses() {
          return this.criticalItemses;
      }
  }
  </code></pre>
  
  <p>CriticalItems is defined as follows:</p>
  
  <pre><code>@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;
      }
  }
  </code></pre>
  
  <p>And in my DAO code - I have these methods:</p>
  
  <pre><code>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);
  }
  </code></pre>
  
  <p>When I load a MasterItem, it loads correctly, and also loads the CriticalItems Set with data, as directed.  Then, I send this data to my UI, and get an updated copy back, which I then try to persist.   The user updates fields in the MasterItem object, but does not touch the CriticalItems Set or anything in it - it remains unmodified.</p>
  
  <p>When my save() method is invoked, Hibernate is insisting on sending SQL updates for each item in the Set of CriticalItems, even though none of them have changed in any way.  </p>
  
  <p>After some digging, here's what I think is happening.  When I do a saveOrUpdate(), Hibernate sees my MasterItem object is in a detached state, so it tries to reload it from disk.  However, when doing so, it appears to be using a prepared statement (which was auto-created by Hibernate at start-up) and this prepared statement does not attempt to join to the CriticalItems data.</p>
  
  <p>So Hibernate has my updated MasterItem object with a full Set of CriticalItems, but uses a MasterItem without collections as its "previousState" object.  Thus, all CriticalItems get updated via SQL (not inserted, which is interesting in itself).</p>
  
  <p>Did I do something in my annotations that caused this behavior?  I know I can use an Interceptor to find out of the item has really changed, or changed the dirty flag to override Hibernate's default algorithm - but this seems to be something that Hibernate should just handle on its own without my intervention.</p>
  
  <p>Any insight would be appreciated.</p>
  
  <p><strong>UPDATE:</strong>
  Based on comments, I think I understand the difference between saveOrUpdate() and merge().  I realize that saveOrUpdate() will result in either an SQL INSERT or an SQL UPDATE in all cases, and that merge, in theory, will only issue updates if the object has changed from it's persistent state, but in order to determine that, Hibernate has to reload the object first via SQL SELECT.</p>
  
  <p>So, I thought I could just go back into my code and change saveOrUpdate() to merge() and it would work, but that wasn't quite the case.</p>
  
  <p>When I used merge(), I was getting</p>
  
  <pre><code>org.springframework.orm.hibernate3.HibernateSystemException: could not initialize proxy - no Session; nested exception is org.hibernate.LazyInitializationException: could not initialize proxy - no Session
  </code></pre>
  
  <p>but it worked fine if I changed back to saveOrUpdate(). </p>
  
  <p>I finally found out why - I didn't include <code>CascadeType.MERGE</code> in my <code>@Cascade</code> annotation (ugh).  Once I fixed that, the exception went away.</p>