-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 
Author Message
 Post subject: Don't change the reference to a collection...
PostPosted: Fri Oct 20, 2006 4:07 pm 
Regular
Regular

Joined: Mon Jul 26, 2004 2:28 pm
Posts: 86
Location: Pensacola, Florida
Getting "org.hibernate.HibernateException: Don't change the reference to a collection with cascade="all-delete-orphan": foo.pkg.Order.references"

Looking at the Hibernate source, this is thrown if the owning entity key changes or if the currentPersister is not the same as the loadedPersister.

My code loads an Order (implements DocumentHolder) in one request and stores it in a HTTP session variable. It adds a Document to it and tries to save it in the next request. I make no other changes to the Order object and I've verified that the key hasn't changed accidentally somehow.

Under what circumstances does the persister change? How can I verify that it is changing? How do I prevent it from changing?

Thanks,

Jesse

(P.S. I've change package names and omitted the portions of the logs that deal with other related objects for brevity and confidentiality.)


Hibernate version:
3.1 (JBoss 4.0.3SP1)

Mapping documents:
Code:
<hibernate-mapping schema="hl_workflow" package="foo.pkg">
    <class name="Order" table="orders">
        <id name="id" column="order_id" unsaved-value="null">
            <generator class="native">
                <param name="sequence">sq_orders</param>
            </generator>
        </id>
        <version name="version"/>
        <many-to-one name="account" column="account_id" cascade="persist,merge,save-update"/>
        <set name="references" cascade="all-delete-orphan" lazy="true">
            <key column="order_id" not-null="true"/>
            <one-to-many class="OrderReference"/>
        </set>
        <!-- ... -->
    </class>
</hibernate-mapping>


Code between sessionFactory.openSession() and session.close():
Note: Using a Servlet Filter to initiate and commit/rollback a JTA UserTransaction. DocumentDao retrieves SessionFactory from JDNI and calls getCurrentSession()

Request 1:
Code:
    public String viewDocuments() {
        try {
            OrderBean order = getOrder();
            if(order != null) {
                getSession().setSelectedDocumentHolder(order);
                return "jbpm:task:" + getInstance().getId() + ":taskDocs.jsp";
            } else {
                FacesUtil.addMessage("There is no order associated with this task");
                return "failure";
            }
        } catch(Throwable t) {
            IncidentUtil.reportIncident("Error loading order for task " + getInstance(), t);
            return "error";
        }
    }


Request 2:
Code:
    public String upload() {
        InputStream in = null;
        try {
            UploadedFile file = getUploadedFile();
            if(file != null) {
                DocumentDao docDao = new DocumentDao();
                DocumentHolder holder = getInstance();

                if(holder instanceof Order) {
                    _log.debug("order.id=" + ((Order)holder).getId());
                }
               
                in = file.getInputStream();
                Document doc = new Document(file.getName(), file.getContentType(), in);
                for(String keyWord: _keyWords) doc.addKeyWord(keyWord);
                holder.addDocument(doc);
                docDao.save(holder);
                TransactionUtil.flush();
                FacesUtil.addMessage("Document saved successfully");
                _documentBeans = null;
                return "success";
            } else {
                FacesUtil.addMessage("Please specify a file");
                return "failure";
            }
        } catch(Throwable t) {
            try { TransactionUtil.rollback(); } catch(Throwable t2) {}
            IncidentUtil.reportIncident("Error saving document", t);
            return "error";
        } finally {
            if(in != null) {
                try { in.close(); } catch(Throwable t) {}
            }
        }
    }


Full stack trace of any exception that occurs:
Code:
14:41:27,869 ERROR Incident #1
Message: Error saving document
Exception: Root cause: org.hibernate.HibernateException: Don't change the reference to a collection with cascade="all-delete-orphan": foo.pkg.Order.references
        at org.hibernate.engine.Collections.prepareCollectionForUpdate(Collections.java:209)
        at org.hibernate.engine.Collections.processReachableCollection(Collections.java:168)
        at org.hibernate.event.def.FlushVisitor.processCollection(FlushVisitor.java:37)
        at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:101)
        at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:61)
        at org.hibernate.event.def.AbstractVisitor.processEntityPropertyValues(AbstractVisitor.java:55)
        at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:115)
        at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:195)
        at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:76)
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
        at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:905)
        at foo.pkg.TransactionUtil.flush(TransactionUtil.java:37)
        at foo.pkg.FooBean.upload(DocumentHolderBean.java:76)


Name and version of the database you are using:
Oracle 10g

The generated SQL (show_sql=true):
N/A

Debug level Hibernate log excerpt:
Code:
14:55:32,550 DEBUG [Cascade] processing cascade ACTION_SAVE_UPDATE for: foo.pkg.Order
14:55:32,557 DEBUG [Cascade] cascade ACTION_SAVE_UPDATE for collection: foo.pkg.Order.references
14:55:32,557 DEBUG [Cascade] done cascade ACTION_SAVE_UPDATE for collection: foo.pkg.Order.references
14:55:32,557 DEBUG [Cascade] deleting orphans for collection: foo.pkg.Order.references
14:55:32,557 DEBUG [Cascade] done deleting orphans for collection: foo.pkg.Order.references
14:55:32,558 DEBUG [Cascade] cascade ACTION_SAVE_UPDATE for collection: foo.pkg.Order.documents
14:55:32,559 DEBUG [CascadingAction] cascading to saveOrUpdate: foo.pkg.Document
14:55:32,559 DEBUG [AbstractSaveEventListener] persistent instance of: foo.pkg.Document
14:55:32,559 DEBUG [DefaultSaveOrUpdateEventListener] ignoring persistent instance
14:55:32,559 DEBUG [DefaultSaveOrUpdateEventListener] object already associated with session: [foo.pkg.Document#109]
14:55:32,559 DEBUG [Cascade] done cascade ACTION_SAVE_UPDATE for collection: foo.pkg.documents
14:55:32,559 DEBUG [Cascade] done processing cascade ACTION_SAVE_UPDATE for: foo.pkg.Order
14:55:32,559 DEBUG [Cascade] processing cascade ACTION_SAVE_UPDATE for: foo.pkg.Document
14:55:32,559 DEBUG [CascadingAction] cascading to saveOrUpdate: foo.pkg.DocumentContent
14:55:32,560 DEBUG [AbstractSaveEventListener] persistent instance of: foo.pkg.DocumentContent
14:55:32,560 DEBUG [DefaultSaveOrUpdateEventListener] ignoring persistent instance
14:55:32,560 DEBUG [DefaultSaveOrUpdateEventListener] object already associated with session: [foo.pkg.DocumentContent#109]
14:55:32,560 DEBUG [Cascade] done processing cascade ACTION_SAVE_UPDATE for: foo.pkg.Document
14:55:32,560 DEBUG [AbstractFlushingEventListener] dirty checking collections
14:55:32,560 DEBUG [CollectionEntry] Collection dirty: [foo.pkg.documents#1945]
14:55:32,560 DEBUG [AbstractFlushingEventListener] Flushing entities and processing referenced collections
14:55:32,564 DEBUG [DefaultFlushEntityEventListener] Updating entity: [foo.pkg.Order#1981]
14:55:32,564 DEBUG [Versioning] Incrementing: 0 to 1
14:55:32,565 DEBUG [Collections] Collection found: [foo.pkg.Order.references#1981], was: [foo.pkg.Order.references#1945] (uninitialized)


Top
 Profile  
 
 Post subject:
PostPosted: Sun Oct 22, 2006 12:45 am 
Regular
Regular

Joined: Wed Jul 27, 2005 2:33 am
Posts: 118
This error is thrown when the application changes the *reference* of a collection, which was returned by Hibernate. In your case i guess, you are changing the instance of your "references" collection present in the Order class. Can you post the implementation of the addDocument method?

Ideally it should be :

Code:
Order.addDocument(Document doc) {
  /*
   * If the "references" is not instantiated then create one.
   * Else, use the one, which Hibernate has created
   */
if (this.references == null) {
    this.references = new HashSet();
}
this.references.add(doc);


}


Top
 Profile  
 
 Post subject:
PostPosted: Sun Oct 22, 2006 8:03 pm 
Regular
Regular

Joined: Mon Jul 26, 2004 2:28 pm
Posts: 86
Location: Pensacola, Florida
Well, what's interesting is that the references collection and the documents collection are unrelated. The code is similar to the following:

Code:
public class Order {
    private Set<OrderReference> _references = new LinkedHashSet<OrderReference>();
    private SortedSet<Document> _documents = new TreeSet<Document>();
   
    public Set<OrderReference> getReferences() {
        return _references;
    }

    public void setReferences(Set<OrderReference> references) {
        _references = references;
    }

    public SortedSet<Document> getDocuments() {
        return _documents;
    }

    public void setDocuments(SortedSet<Document> documents) {
        _documents = documents;
    }

    public void addReferences(OrderReference reference) {
        if(reference != null) _references.add(reference);
    }

    public void addDocument(Document document) {
        if(document != null) _documents.add(document);
    }
}


Both my Document and Account class implement an interface named DocumentHolder that includes getDocuments and addDocument. I've had really bizarre things happen with Hibernate in the past using implicit polymorphism and generics. The other interesting thing is that in my JSF bean I'm adding the Document to both an Order and an Account. The Order and Account were loaded in a previous request; Order has a lazy many-to-one to Account and Account has a lazy one-many to Order (the former is initialized, the latter should not be). I'm wondering if the fact that the Order is reachable from the Account might have something to do with it . . . not sure. I'm goint to try just adding the doc to the Order, and then just adding it to the Account, and if neither of those work, getting rid of the DocumentHolder interface.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Oct 23, 2006 8:42 am 
Regular
Regular

Joined: Wed Jul 27, 2005 2:33 am
Posts: 118
The exception stacktrace mentions that the instance of foo.pkg.Order.references collection was changed(and not the documents collection). So check whether in your business flow, there is a call to setReferences method of the Order object.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Oct 23, 2006 9:32 am 
Regular
Regular

Joined: Mon Jul 26, 2004 2:28 pm
Posts: 86
Location: Pensacola, Florida
I put a logging statement in setReferences to output the argument's classname and System.identityHashCode (I didn't want to call toString because that forces initialization of lazy props). The only time there is ever a call to setReferences is in the first request when the Order is loaded. It is called twice during that request. The argument in the first call is the default value of the underlying field (an empty LinkedHashSet) and the second is with an uninitialized proxy for the lazy collection. Order.setReferences is never called in the second request. It's baffling.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Oct 23, 2006 10:24 am 
Regular
Regular

Joined: Mon Jul 26, 2004 2:28 pm
Posts: 86
Location: Pensacola, Florida
I think I've worked around the problem. Some combination of the following solved the problem:

1. Deleted the DocumentHolder interface
2. Removed "implements DocumentHolder" from Order and Account
3. Changed DocumentHolderBean (JSF managed bean) from an abstract base class to an interface
4. Changed OrderBean and AccountBean (JSF managed beans) from extending DocumentHolderBean to implementing DocumentHolderBean
5. In upload, rather than dealing with a DocumentHolder, I now deal with an Order object
6. I now lock() the Order prior to adding the document, and then save() it afterward

Something in all of that made the error go away. I suspect it had mostly to do with the DocumentHolder interface, since this is the third problem I've had with interfaces and generics with both Hibernate and Betwixt.

Anyway, thanks for the assist.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.