title: UnsupportedOperationException when merging an existing Hibernate model object?
body: <p>After an upgrade from Hibernate 3 to 4, we're working through a few kinks that popped up along the way. One that has us particularly stumped is an UnsupportedOperationException, where an existing object is pulled from the database, tweaked, and merged.</p>
  
  <p><em>The problem</em> is that Hibernate appears to be adding an object to an <code>AbstractList</code></p>
  
  <p>This only seems to happen to one particular object type, when saved in our DAO, but as best as we can tell:</p>
  
  <ol>
  <li>We're not using any sublist() or asList() methods that would cause an immutable instance to be created.</li>
  <li>Examining the object that's being saved (which is enormous and has many children) I don't <em>think</em> that any of its children items are <code>AbstractList</code> types.</li>
  </ol>
  
  <p>Here are the code snippets around the stack points:</p>
  
  <p><strong>HibernateDao.save():</strong></p>
  
  <pre><code>@Transactional
  public void save(T item) {
      try {
          getSessionFactory().getCurrentSession().merge(item);
      } catch (Exception ex) {
          LOGGER.debug("Unable to merge", ex);
          LOGGER.warn("Unable to merge item, saving instead. (Of type " + item.getClass() + ")");
          getSessionFactory().getCurrentSession().saveOrUpdate(item);
      }
  }
  </code></pre>
  
  <p>Our <strong>User</strong> item, which is being saved, has a number of children items defined like so:</p>
  
  <pre><code>@OneToMany(cascade = CascadeType.ALL)
  @LazyCollection(LazyCollectionOption.FALSE)
  private Map&lt;String, Project&gt; associatedProjects = new HashMap&lt;String, Project&gt;();
  </code></pre>
  
  <p>The <code>Project</code> class has other similarly-annotated children, but everything has <code>CascadeType.ALL</code>, and <code>LazyCollectionOption.FALSE</code> defined.</p>
  
  <p>Here is the (quite tall) stack trace:</p>
  
  <p><em>Note that our code begins with <code>com.company.application</code></em></p>
  
  <pre><code>06/04 18:15:45 DEBUG [Thread-19258] hibernate.HibernateDao.save- Unable to merge
  java.lang.UnsupportedOperationException
          at java.util.AbstractList.add(AbstractList.java:148)
          at java.util.AbstractList.add(AbstractList.java:108)
          at org.hibernate.collection.internal.PersistentBag.add(PersistentBag.java:292)
          at org.hibernate.type.CollectionType.replaceElements(CollectionType.java:496)
          at org.hibernate.type.CollectionType.replace(CollectionType.java:563)
          at org.hibernate.type.AbstractType.replace(AbstractType.java:178)
          at org.hibernate.type.TypeHelper.replaceAssociations(TypeHelper.java:261)
          at org.hibernate.event.internal.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:398)
          at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:221)
          at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:282)
          at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:151)
          at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:914)
          at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:896)
          at org.hibernate.engine.spi.CascadingAction$6.cascade(CascadingAction.java:288)
          at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:380)
          at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323)
          at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
          at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:409)
          at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:350)
          at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:326)
          at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
          at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:165)
          at org.hibernate.event.internal.DefaultMergeEventListener.cascadeOnMerge(DefaultMergeEventListener.java:439)
          at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:308)
          at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:151)
          at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:914)
          at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:896)
          at org.hibernate.engine.spi.CascadingAction$6.cascade(CascadingAction.java:288)
          at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:380)
          at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323)
          at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
          at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:409)
          at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:350)
          at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:326)
          at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
          at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:165)
          at org.hibernate.event.internal.DefaultMergeEventListener.cascadeOnMerge(DefaultMergeEventListener.java:439)
          at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:308)
          at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:151)
          at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:914)
          at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:896)
          at org.hibernate.engine.spi.CascadingAction$6.cascade(CascadingAction.java:288)
          at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:380)
          at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323)
          at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
          at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:409)
          at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:350)
          at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:326)
          at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
          at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:165)
          at org.hibernate.event.internal.DefaultMergeEventListener.cascadeOnMerge(DefaultMergeEventListener.java:439)
          at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:308)
          at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:151)
          at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:76)
          at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:904)
          at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:888)
          at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:892)
          at com.company.hibernate.HibernateDao.save(HibernateDao.java:129)
          at sun.reflect.GeneratedMethodAccessor62.invoke(Unknown Source)
          at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
          at java.lang.reflect.Method.invoke(Method.java:601)
          at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318)
          at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
          at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
          at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
          at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
          at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
          at $Proxy53.save(Unknown Source)
          at com.company.application.UserManager.save(UserManager.java:46)
          at sun.reflect.GeneratedMethodAccessor67.invoke(Unknown Source)
          at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
          at java.lang.reflect.Method.invoke(Method.java:601)
          at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318)
          at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
          at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
          at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
          at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
          at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
          at $Proxy66.save(Unknown Source)
          at com.company.application.UserOperationController.saveUser(UserOperationController.java:533)
  </code></pre>
  
  <p><strong>We're not sure where the AbstractList is coming from,</strong> or how we're responsible. Are there any potential pitfalls when working with Hibernate 4 (this issue is new since the upgrade) that could result in partially-unmodifiable objects? Or that would cause Hibernate to act in a way that results in it attempting to create unmodifiable instances of objects?</p>