-->
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: Issue with loading different type from database
PostPosted: Thu Jun 22, 2006 2:18 pm 
Beginner
Beginner

Joined: Fri Mar 12, 2004 1:02 pm
Posts: 23
Hi,

I'm trying to load a decent-sized object graph from the database using hibernate and I'm running into this error:

Code:
org.hibernate.property.BasicPropertyAccessor$BasicSetter set
SEVERE: IllegalArgumentException in class: com.test.domain.Container, setter method of property: miscItem
org.hibernate.property.BasicPropertyAccessor$BasicSetter set
SEVERE: expected type: com.test.domain.item.MiscItem, actual value: com.test.domain.item.AbstractItem$$EnhancerByCGLIB$$c5b6976c
org.hibernate.event.def.DefaultLoadEventListener onLoad
INFO: Error performing load command
.....



In my domain MiscItem extends from AbstractItem (just one of many Item implementations). I'm not sure how to go about debugging this problem since Hibernate was able to successfully save my object graph to the database without errors but when it retrieves it this issue pops up. Any help would be appreciated. Thanks in advanced!

-los


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 23, 2006 10:58 am 
Beginner
Beginner

Joined: Fri Mar 12, 2004 1:02 pm
Posts: 23
Followup:

I was wondering if this issue had to do with my mapping. Below is the mapping:

Code:
<hibernate-mapping>

    <class
        name="com.test.domain.item.AbstractItem"
        table="ITEM"
        abstract="true">

        <id name="itemId" type="long">
            <column name="ITEM_ID" not-null="true"/>
            <generator class="native"/>
        </id>
       
        <discriminator column="ITEM_TYPE" type="string"/>       
       
        <list name="barcodes" table="ITEM_BARCODES" lazy="false" cascade="save-update">
            <key column="ITEM_ID"/>
            <index column="BARCODE_INDEX"/>
            <many-to-many
                column="BARCODE_ID"
                class="com.test.domain.barcode.AbstractBarcode"/>
        </list>
       
        <subclass
            name="com.test.domain.item.MiscItem"
            discriminator-value="MiscItem">       
            <subclass
                name="com.test.domain.item.StockItem"
                discriminator-value="StockItem"/>             
        </subclass>

        <subclass
            name="com.test.domain.item.CoreItem"
            discriminator-value="CoreItem"/>   
           
    </class>
   
</hibernate-mapping>



When I save to the database, the ITEM table shows the correct subclasses (MiscItem, StockItem and CoreItem). But when I retrieve, it looks like Hibernate is trying to instantiate the AbstractItem class for MiscItem.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 23, 2006 2:39 pm 
Beginner
Beginner

Joined: Fri Mar 12, 2004 1:02 pm
Posts: 23
Followup 2:

I'm not sure if this is causing the problem but the code that tries to retrieve the misc item is using Java reflection-- it invokes the method "getMiscItem()" on the Container object, returned as an Item interface:

Code:
Item item = (Item)method.invoke(container, noArgs);


Item is the interface, AbstractItem is the base abstract class, and Misc/Core items are the concrete classes.

-los


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 23, 2006 3:16 pm 
Beginner
Beginner

Joined: Fri Mar 12, 2004 1:02 pm
Posts: 23
One last followup:

I've noticed that hibernate (via CGLib) is trying to create the wrong object. Seems like my code is trying to retrieve a Core Item but hibernate is thinking its a Misc Item. Below is the stacktrace:

Code:
org.hibernate.property.BasicPropertyAccessor$BasicSetter set
SEVERE: IllegalArgumentException in class: com.test.domain.Container, setter method of property: miscItem
org.hibernate.property.BasicPropertyAccessor$BasicSetter set
SEVERE: expected type: com.test.domain.item.MiscItem, actual value: com.test.domain.item.AbstractItem$$EnhancerByCGLIB$$d5b62e6x
org.hibernate.event.def.DefaultLoadEventListener onLoad
INFO: Error performing load command
org.hibernate.PropertyAccessException: IllegalArgumentException occurred while calling setter of com.test.domain.item.miscItem
        at org.hibernate.property.BasicPropertyAccessor$BasicSetter.set(BasicPropertyAccessor.java:104)
        at org.hibernate.tuple.AbstractEntityTuplizer.setPropertyValues(AbstractEntityTuplizer.java:330)
        at org.hibernate.tuple.PojoEntityTuplizer.setPropertyValues(PojoEntityTuplizer.java:191)
        at org.hibernate.persister.entity.AbstractEntityPersister.setPropertyValues(AbstractEntityPersister.java:3343)
        at org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:129)
        at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:842)
        at org.hibernate.loader.Loader.doQuery(Loader.java:717)
        at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:224)
        at org.hibernate.loader.Loader.loadEntity(Loader.java:1785)
        at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:48)
        at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:42)
        at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:2821)
        at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:370)
        at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:351)
        at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:122)
        at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:81)
        at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:871)
        at org.hibernate.impl.SessionImpl.immediateLoad(SessionImpl.java:829)
        at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:66)
        at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:111)
        at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.intercept(CGLIBLazyInitializer.java:160)
        at [b]com.test.domain.Container$$EnhancerByCGLIB$$d8ed955.getCoreItem(<generated>)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)[/b]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:585)


from the bolded line in the stacktrace, it looks like a call to "getCoreItem" is being called in the Container object, but the error message states that its trying to call "setMiscItem"... I don't know anything about CGLib or the internals of hibernate to understand if these calls are correct or not. Hopefully someone can shed some light into this. Thanks!

-los


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 23, 2006 5:42 pm 
Beginner
Beginner

Joined: Fri Mar 12, 2004 1:02 pm
Posts: 23
After putting in a bunch of printlns to track these dynamic calls, I see that Hibernate is instantiating my Container object, setting all of the necessary items. However, it tries to set my MiscItem with the superclass and I do not know why. Below is the code for Container:

Code:
public class Container {
//instance variables omitted
public void setContainerId(Long id) {
   System.out.println("containerId = " + id);
   this.containerId = containerId;
}
public void setMiscItem(MiscItem item) {
   System.out.println("miscItem = " + item);
   this.miscItem = item;
}
public void setCoreItem(CoreItem item) {
   System.out.println("coreItem = " + item);
   this.coreItem = item;

}
public void setStockItem(StockItem item) {
   this.stockItem = item;
}



I have an Item interface, an AbstractItem base class that implements Item, and the concrete classes MiscItem, StockItem and CoreItem (all extend AbstractItem).

The initial call to save the entire Container graph works and this is what shows in the table CONTAINER:

Code:

CONTAINER_ID     MISC_ITEM_ID     CORE_ITEM_ID     STOCK_ITEM_ID
262144                 131089                131085                 131091



For the CONTAINER table, the MISC/CORE/STOCK_ITEM_IDs are foreign keys to the ITEM table. The corresponding ITEM table looks like this:

Code:

ITEM_ID     ITEM_TYPE     
131089       MiscItem
131085       CoreItem
131091       StockItem




Now when I load data from the database, it loads CoreItem just fine but blows up on MiscItem... Here's a more detailed stacktrace:

Code:

org.hibernate.property.BasicPropertyAccessor$BasicSetter set
SEVERE: IllegalArgumentException in class: com.test.domain.Container, setter method of property: miscItem
org.hibernate.property.BasicPropertyAccessor$BasicSetter set
SEVERE: expected type: com.test.domain.item.MiscItem, actual value: com.test.domain.item.AbstractItem$$EnhancerByCGLIB$$d5b62e6x
org.hibernate.event.def.DefaultLoadEventListener onLoad
INFO: Error performing load command
org.hibernate.PropertyAccessException: IllegalArgumentException occurred while calling setter of com.test.domain.item.miscItem
        at org.hibernate.property.BasicPropertyAccessor$BasicSetter.set(BasicPropertyAccessor.java:104)

containerId = 262144
stockId = com.test.domain.item.StockItem@19fca0c

        at org.hibernate.tuple.AbstractEntityTuplizer.setPropertyValues(AbstractEntityTuplizer.java:330)
        at org.hibernate.tuple.PojoEntityTuplizer.setPropertyValues(PojoEntityTuplizer.java:191)
        at org.hibernate.persister.entity.AbstractEntityPersister.setPropertyValues(AbstractEntityPersister.java:3343)
        at org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:129)
        at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:842)
        at org.hibernate.loader.Loader.doQuery(Loader.java:717)
        at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:224)
        at org.hibernate.loader.Loader.loadEntity(Loader.java:1785)
        at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:48)
        at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:42)
        at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:2821)
        at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:370)
        at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:351)
        at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:122)
        at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:81)
        at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:871)
        at org.hibernate.impl.SessionImpl.immediateLoad(SessionImpl.java:829)
        at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:66)
        at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:111)
        at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.intercept(CGLIBLazyInitializer.java:160)
        at com.test.domain.Container$$EnhancerByCGLIB$$d8ed955.getCoreItem(<generated>)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:585)




With the system.outs, hibernate first tries to instantiate the Container object (correct containerId) and then populate it by calling all of the setter methods. The first call is to setStockItem() and that works fine. The second call is to setMiscItem() and this is where it blows up, telling me of a mismatch type with AbstractItem.

Since hibernate is doing everything behind the scenes, it is tough for me to figure out why hibernate is instantiating the AbstractItem class and setting it for MiscItem even though the tables show otherwise. Am I doing something wrong with the mapping?

Any help would be appreciated. Thanks!

-los


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jun 24, 2006 11:46 am 
Expert
Expert

Joined: Tue Dec 28, 2004 7:02 am
Posts: 573
Location: Toulouse, France
I'm not sure, because there's a lot to read here :). But maybe you could try and add a setter with the type Hibernate is looking for.

To sum up, if I followed your case correctly, you have this method :
setMiscItem(MiscItem)
{
//Some code
}

I'd try to add :
setMiscItem(AbstractItem a)
{
setMiscItem((MiscItem)a);
}

Is it better ?

_________________
Baptiste
PS : please don't forget to give credits below if you found this answer useful :)


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 30, 2006 5:34 pm 
Beginner
Beginner

Joined: Fri Mar 12, 2004 1:02 pm
Posts: 23
Hi,

Thanks for the reply. Your suggestion did not work (throws a java ClassCast). However I'm encountering a similar issue that is really really strange.

Using the same AbstractItem scheme, I have a subclass called AbstractDisposableItem, in which a concrete implementation is Groceries. My mapping looks like this:

Code:
<hibernate-mapping auto-import="false">
   
    <subclass
        name="com.test.domain.item.AbstractDisposableItem"
        extends="com.test.domain.item.AbstractItem"
        abstract="true">

        <subclass
            name="com.test.domain.item.disposable.Groceries"
            discriminator-value="Groceries"/>   
      
</subclass>
</hibernate-mapping>



When I save this to the database, the data looks good. When I retrieve it, I get weird class cast exceptions. When I system.out the Groceries object retrieved via Hibernate, I get "com.test.domain.item.disposable.Groceries@124ds43"
But when I do an explicit instanceof, Java tells me that this object is NOT of type Groceries. Whats worse, when I do a getClass().getSuperclass() on the Groceries object, Java tells me that it's direct superclass is AbstractItem. I would expect it to be of type AbstractDisposableItem. So I'm not sure how Hibernate instantiates these objects using different super classes.

-los


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 03, 2006 6:02 pm 
Regular
Regular

Joined: Tue Jun 22, 2004 8:01 pm
Posts: 106
Location: PowderTown, Utah, USA
Polymorphism and lazily loaded objects is a tricky subject. In a nutshell, Hibernate uses CGLIB generated proxies to automagically pull off lazy instantiation. When you ask for a lazy object, you don't get the object in question, but a wrapper object. That wrapper only knows the ID number (and maybe version) of the actual object. When you call a property accessor on the lazy object the object is realized (hydrated) behind the scenes for you. But you probably knew all that.

What you may not have known is that polymorphic lazy objects do not descend from each leaf node class. They descend from the superclass. So, if you have a tree like this: class A, subclass B, subclass C, and you ask for C as a lazy object, Hibernate creates a CGLIB wrapper that descends from A, not from C.

This is very important because it "breaks" instanceof and typecasting. The Hibernate team would probably bristle at the term "breaks," but it's the shortest way to put it. Basically, if you're using polymorphism and you want lazy loading, you trade it away at the cost of instanceof and typecasting. Personally I don't like things that take away fundamental nuances of the Java language, but I understand why.

The Hibernate crew would probably make the point (rightly so) that using typecasting probably means your object model needs work and that you should probably refactor anyway.

What this all means is that you will have to get more creative. For example, my base model objects all have a "queryInterface(Class clazz)" and "isInstance(Class clazz)" method for cases when typecasting is absolutely necessary. I've also successfully used the Visitor pattern (search the Hibernate patterns pages) to burrow past the proxies as well. The visitor pattern is also good coding practice anyway when navigating polymorphic collections. Think of it this way, Hibernate is helping you create more loosely coupled applications. My designs have actually improved as a result of this "problem."


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.