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.  [ 8 posts ] 
Author Message
 Post subject: Bizarre ClassCastException When Attempting to Persist
PostPosted: Thu Jun 26, 2008 1:08 pm 
Regular
Regular

Joined: Fri Oct 05, 2007 1:17 pm
Posts: 78
I am building a Seam-JPA app powered by Hibernate, and I am attempting to unit test a class whose role it is to persist JPA entities. The class is your typical Order class, and it contains a 1:M mapping to an Item entity represented internally as a HashMap (for reasons relating to the characteristics of the feature) but converted to an ArrayList as follows:

Code:
   @OneToMany(fetch= FetchType.LAZY, cascade= CascadeType.PERSIST)
   @JoinColumn(name = "ORDER_ID", nullable = false)
   public List<Item> getItems() {
      return new ArrayList<Item>(itemMap.values());
   }


However, when I attempt to persist an Order instance with em.persist(), I get the following:

Code:
javax.persistence.RollbackException: Error while commiting the transaction
   at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:71)
   at com.myapp.persistence.test.PersistenceUtilityStoreOrderTest.testStoreOrder(PersistenceUtilityStoreOrderTest.java:47)
Caused by: java.lang.ClassCastException: java.util.ArrayList
   at org.hibernate.event.def.FlushVisitor.processCollection(FlushVisitor.java:34)
   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:131)
   at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:196)
   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:1000)
   at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
   at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
   at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:54)
   ... 23 more


I took a look at the source, and FlushVisitor.processCollection() attempts to cast the object to PersistentCollection. But instead, it is getting an ArrayList, presumably from me.

Any insight into what is going wrong with my mapping or implementation is much appreciated.

Thanks.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 27, 2008 2:07 am 
Newbie

Joined: Thu Jun 26, 2008 8:10 am
Posts: 4
Hello,
Can you try something like:

public List<Item> getItems() {
return Collections.unmodifiableList(new ArrayList<Item>(itemMap.values()));
}

This might return an instance of the java.util.List class which should be castable to PersistentCollection

_________________
Regards,
pravsemilo.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 27, 2008 4:16 am 
Expert
Expert

Joined: Thu Jul 05, 2007 9:38 am
Posts: 287
When you load an Entity which contains a collection from Hibernate the collection is set to an instance of Persistence Collection. You should never change that collection (http://hibernate.org/116.html#A16), since it does some magic pixie dust stuff to monitor which elements moved in or out.

Since you create your List on the fly in the getter you obviously throw away the PersistentCollection which causes trouble.

So your options are:

1.keep the PersistentCollection which you receive through the setter and use it to return the Hashmap content. Aproximately like this:

Code:
public void setItems(List alist){
list = aList;
fillMyHashMap
}

public List getItems(){
  return list;
}

public void someMethodchangingContentOfYourHasmap()
   changeHashMap;
   changeContentOfList // important, since Hibernate will use this for dirty checking
}


2. Properly persist your HashMap as an actual Map

3. Create a Liste Implementation, which also provides the Map interface and map this as a List. In this case you have to make sure that for changing content the List interface is used, because this is what gets intercepted by Hibernate.

HTH
Jens

_________________
Please rate useful posts.


Schauderhaft: Softwaredevelopment, Projectmanagement, Qualitymanagement and all things "schauderhaft"


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 27, 2008 9:50 am 
Expert
Expert

Joined: Tue May 13, 2008 3:42 pm
Posts: 919
Location: Toronto & Ajax Ontario www.hibernatemadeeasy.com
schauder wrote:
3. Create a Liste Implementation, which also provides the Map interface and map this as a List. In this case you have to make sure that for changing content the List interface is used, because this is what gets intercepted by Hibernate.


This sounds like a bit of work, but I have seen it done often, and when developers get used to it, it can save alot of hassle.

_________________
Cameron McKenzie - Author of "Hibernate Made Easy" and "What is WebSphere?"
http://www.TheBookOnHibernate.com Check out my 'easy to follow' Hibernate & JPA Tutorials


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 30, 2008 4:37 pm 
Regular
Regular

Joined: Fri Oct 05, 2007 1:17 pm
Posts: 78
Thanks for all the good information, schauder. Well, I tried that first option, or at least my interpretation of it, and found the following exception:

Code:
java.util.ConcurrentModificationException
   at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:449)
   at java.util.AbstractList$Itr.next(AbstractList.java:420)
   at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:296)
   at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:242)
   at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:219)
   at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
   at org.hibernate.engine.Cascade.cascade(Cascade.java:130)
   at org.hibernate.event.def.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:456)
   at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:334)
   at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:181)
   at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:121)
   at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:49)
   at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:131)
   at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:87)
   at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:38)
   at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:618)
   at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:592)
   at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:596)
   at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:213)
   at com.myapp.persistence.PersistenceUtility.store(PersistenceUtility.java:35)
   at com.myapp.persistence.test.PersistenceUtilityStoreOrderTest.testStoreOrder(PersistenceUtilityStoreOrderTest.java:46)


Here is the code I tried inside the getOrderItems getter method:

Code:
private List<OrderItem> getOrderItemList(List<T> internalOrderItemsFromClient, HashMap<String, T> map) {
      internalOrderItemsFromClient.clear();
      internalOrderItemsFromClient.addAll(map.values());

      return internalOrderItemsFromClient;
   }


In this code, internalOrderItemsFromClient is the List object I receive from Hibernate (or anyone else for that matter) in the setter method. Basically, I receive the collection and hold onto it. Then in the getter, I simply remove everything in the list and add what's in the map.

Clearly, this is causing issues for Hibernate, so I will need better clarification on how I am to proceed.

In an attempt to clarify what exactly I want to do, I want to manage my order items in a hashmap and perform all kinds of logic on the map. But when it is time to persist, I just want to store the values in the map rather than the entire map.

Please let me know if I need to be clearer. Any insight into resolving this last hurdle is appreciated.

Thanks.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 01, 2008 3:31 am 
Expert
Expert

Joined: Thu Jul 05, 2007 9:38 am
Posts: 287
You are really lucky you got that exception!

The 1st problem with your code is: you are modifiying the hashmap but aren't modifiying the list. This way hibernate does not realize the list has changed and won't save it!

The 2nd problem (which gives the exception) is: Hibernate realizes despite the 1st problem that it has to save the collection. It starts persisting, and callsy the getter. But now you copy over your stuff from the map, causing hibernate to register changes while it is saving.

To fix both problems: keep your hashmap and list sync'ed all the time. The getter must not change the content of the list.

_________________
Please rate useful posts.


Schauderhaft: Softwaredevelopment, Projectmanagement, Qualitymanagement and all things "schauderhaft"


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 04, 2008 5:22 pm 
Regular
Regular

Joined: Fri Oct 05, 2007 1:17 pm
Posts: 78
Thanks, schauder. I followed your tips and have my tests passing.

So how do I credit you with points or candy or whatever you do on here?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 07, 2008 9:07 am 
Expert
Expert

Joined: Tue May 13, 2008 3:42 pm
Posts: 919
Location: Toronto & Ajax Ontario www.hibernatemadeeasy.com
Kudos schauder! Great answer!

_________________
Cameron McKenzie - Author of "Hibernate Made Easy" and "What is WebSphere?"
http://www.TheBookOnHibernate.com Check out my 'easy to follow' Hibernate & JPA Tutorials


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 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.