-->
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.  [ 4 posts ] 
Author Message
 Post subject: ClassCastException with inheritance + implicit polymorphism
PostPosted: Mon Jan 09, 2006 11:24 am 
Newbie

Joined: Mon Jan 09, 2006 10:43 am
Posts: 3
Location: Brussels, Belgium
Hi there,

Can someone please explain me the following.

I'm creating a Hibernate 3.1 java project with 2 mapped classes, Child and Parent. Child derives from Parent. Parent derives from GrandPa, an abstract base class. The two classes map to respective tables called child and parent, on a vanilla mysql 4.1.

If I execute a query by example finder on the Child, everything works nicely. If I execute a finder on the Parent, I get "ClassCastException".

Debugging the Hibernate code, one finds that org.hibernate.impl.SessionImpl has loaded both Parent and Child as implementors of Parent, but Parent is not of type Child (org.hibernate.tuple.PojoInstantiator, lines 110 and 111). Note that curiously, I'm executing a finder on the Parent at this point.

One also notices that the debugger goes twice into Example.getEntityMode(), and only throws up on the second turn.

The following did not help:
- migrated back to Hibernate 3.0: same issue
- added entity-name="..." attribute in <class> element in hbm file, as suggested in the docs chapter "Mapping a class more than once"

The following did help:
- break inheritance between Parent and Child, such that Parent and Child become siblings in the class hierarchy
- in the hbm files of both classes, set polymorphism="explicit"

I'll gladly send the Eclipse (IBM RAD 6.0) project to anyone who wants to make an attempt at reproducing the behaviour.

Somehow I get the impression that this sort of behaviour is either not very well documented, or represents a bug.

Thanks in advance,



Ulrich Kroener



Hibernate version: 3.1

Mapping documents:
=============
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost/mspsample</property>
<property name="connection.username">root</property>
<property name="connection.password">yeahright</property>
<property name="connection.pool_size">1</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>

<property name="show_sql">true</property><!-- Echo all executed SQL to stdout -->

<mapping resource="be/abvv/test/hiberissue/Parent.hbm.xml"/>
<mapping resource="be/abvv/test/hiberissue/Child.hbm.xml"/>
</session-factory>
</hibernate-configuration>

=============
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="be.abvv.test.hiberissue.Parent" table="parent">
<composite-id name="id" class="be.abvv.test.hiberissue.ParentKey">
<key-property name="nn" type="long">
<column name="nn" />
</key-property>
</composite-id>
<property name="foo" type="long">
<column name="foo" not-null="true" />
</property>
</class>
</hibernate-mapping>

=============

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="be.abvv.test.hiberissue.Child" table="Child">
<composite-id name="id" class="be.abvv.test.hiberissue.ParentKey">
<key-property name="nn" type="long">
<column name="nn" />
</key-property>
</composite-id>
<property name="foo" type="long">
<column name="foo" not-null="true" />
</property>
<property name="bar" type="long">
<column name="bar" not-null="true" />
</property>
</class>
</hibernate-mapping>

Code between sessionFactory.openSession() and session.close():

/*
*
*/
package be.abvv.test.hiberissue;

import java.util.List;

import junit.framework.TestCase;

/**
*
*/
public class ParentTest extends TestCase {

public void testFind() {
String msgPrefix = null;
Parent found = null;
List foundList = null;

Parent crit = new Parent();
crit.setFoo(1L);

// find
msgPrefix = "find failed: ";
try {
foundList = Parent.find(crit);
assertTrue( msgPrefix + "null returned", foundList != null);
assertTrue( msgPrefix + "zero items found", !foundList.isEmpty());
} catch (RuntimeException e) {
e.printStackTrace();
assertTrue(msgPrefix + e.getMessage(), false);
} finally {
HibernateUtil.closeAndCommitAllSessions(); // close session in any case
}

}

}

====================

package be.abvv.test.hiberissue;

import java.io.Serializable;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;

/**
*/

public class Child extends Parent {
public Child() {
}

public Child(ParentKey id) {
super(id);
}
private long bar;
public long getBar() {
return this.bar;
}
public void setBar(long linr) {
this.bar = linr;
}
public static Child findById(Child searchData) {
try {
return (Child)HibernateUtil.getCurrentSession().get(searchData.getClass(),searchData.getIdentifier());
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Unable to find"+ searchData.getName()+" with id:"+searchData.getIdentifier()+" error:"+e+"-"+e.getMessage());
}
}
/..
public static final String name = "Child";
String getName() {
return name;
}
Serializable getIdentifier() {
return new ParentKey(getId());
}

}

========================


package be.abvv.test.hiberissue;

import java.io.Serializable;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;

/**
*/

public class Parent extends GrandPa {
public Parent() {
}
public Parent(ParentKey id) {
this.id = id;
}
private ParentKey id;
private long foo;
public ParentKey getId() {
return this.id;
}
public void setId(ParentKey id) {
this.id = id;
}
public long getFoo() {
return this.foo;
}
public void setFoo(long linr) {
this.foo = linr;
}
public static List find(Parent searchData) {
Criteria crit = HibernateUtil.getCurrentSession().createCriteria(searchData.getClass());
Example exampleQBE = Example.create(searchData);

exampleQBE.ignoreCase(); // for strings, ignore case
exampleQBE.enableLike(MatchMode.ANYWHERE); // for strings, this will give "like '%foo%'"
exampleQBE.excludeZeroes(); // do not add java nulls as search criteria
exampleQBE.excludeProperty("id"); // You need to exclude the PROPERTY of the primary key,

crit.add(exampleQBE);

try {
return crit.list();
} catch (HibernateException e) {
throw new RuntimeException("Unable to perform find:"+e+"-"+e.getMessage());
}
}

// ...
public static final String name = "Parent";
String getName() {
return name;
}

Serializable getIdentifier() {
return new ParentKey(getId());
}

}

==========================

package be.abvv.test.hiberissue;

import java.io.Serializable;

/**
* ParentKey Composite primary key class for Parent
*/

public class ParentKey implements Serializable {
private long nn;

public ParentKey() {
}
public ParentKey(ParentKey id) {
this.setNn(id.getNn());
}
public ParentKey(long nn) {
this.nn = nn;
}

public long getNn() {
return this.nn;
}
public void setNn(long nn) {
this.nn = nn;
}

// ...

}

==========================

/*
*
*/
package be.abvv.test.hiberissue;

import java.io.Serializable;

import org.hibernate.HibernateException;

/**
*
*/
public abstract class GrandPa {

abstract String getName();
abstract Serializable getIdentifier();

public void save() {
try {
HibernateUtil.getCurrentSession().save(this);
} catch (HibernateException e) {
throw new RuntimeException("Unable to save "+getName()+" "+e +"/n"+e.getMessage());
}

}

// .. public void update() , delete()

}
==============================================
package be.abvv.test.hiberissue;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

/**
* Utility class for hibernate. Enables configuration and session caching. By
* using the thread local pattern it ensures that session are not shared across
* threads.
*/
public class HibernateUtil {
/**
* Hibernate config file. Is static and shared across threads
*/
public static final String CONFIG_FILE = "/hibernate.cfg.xml";

/**
* Session factory holds the configuration data loaded by the config file
*/
private static SessionFactory internalSessionFactory;

/**
* Static variable linked to a thread holds the transaction
*/
private static final ThreadLocal transactionCache = new ThreadLocal();

/**
* Static variable linked to the thread holds the session.
*/
private static final ThreadLocal sessionCache = new ThreadLocal();

/**
* gets a hibernate session using the class defaults to initializes the
* session. When creating a session a transaction will also be started. To close
* the session and commit the transaction use <code>closeAndCommitAllSessions</code> when
* needed you can also use <code>closeAndRollbackAllSessions</code>
*
* @return Session to the database
*/
public static Session getCurrentSession() {
return getCurrentSession(internalSessionFactory);
}

/**
* Returns a hibernate session. First checks the cache, if nothing is found
* in the cache creates a new session. When creating a session a transaction will also be started. To close
* the session and commit the transaction use <code>closeAndCommitAllSessions</code> when
* needed you can also use <code>closeAndRollbackAllSessions</code>
*
* @param sessionFactory
* SessionFacrtory to be used when creating the session.
* @return Session to the database
*/
public static Session getCurrentSession(SessionFactory sessionFactory) {
try {


//Retrieves the session from the "thread cache"
Session s = (Session) sessionCache.get();
if (s == null || !s.isOpen()) {
//Checks if the factory was initialized
if (sessionFactory == null) {
//Initializes the factory with the configuration file
sessionFactory = new Configuration().configure(CONFIG_FILE).buildSessionFactory();
internalSessionFactory=sessionFactory;
}
//Cache was empty or session was closed.Creating new session
s = sessionFactory.openSession();
Transaction t = s.beginTransaction();
//Stores the session and the transaction in the cache
sessionCache.set(s);
transactionCache.set(t);
}

return s;
} catch (Exception e) {
throw new RuntimeException("Unable to retrieve Hibernate Session:" + e.getMessage());
}
}


}



Full stack trace of any exception that occurs:
java.lang.ClassCastException: be.abvv.test.hiberissue.Parent
at org.hibernate.criterion.Example.getEntityMode(Example.java:247)
at org.hibernate.criterion.Example.toSqlString(Example.java:177)
at org.hibernate.loader.criteria.CriteriaQueryTranslator.getWhereCondition(CriteriaQueryTranslator.java:316)
at org.hibernate.loader.criteria.CriteriaJoinWalker.<init>(CriteriaJoinWalker.java:86)
at org.hibernate.loader.criteria.CriteriaLoader.<init>(CriteriaLoader.java:67)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1473)
at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:298)
at be.abvv.test.hiberissue.Parent.find(Parent.java:87)
at be.abvv.test.hiberissue.ParentTest.testFind(ParentTest.java:29)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:85)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:58)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:60)
at java.lang.reflect.Method.invoke(Method.java:391)
at junit.framework.TestCase.runTest(TestCase.java:154)
at junit.framework.TestCase.runBare(TestCase.java:127)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:118)
at junit.framework.TestSuite.runTest(TestSuite.java:208)
at junit.framework.TestSuite.run(TestSuite.java:203)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:436)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:311)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

Name and version of the database you are using: MySQL 4.1, mysql Ver 14.7 Distrib 4.1.15, for Win32 (ia32)

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

Debug level Hibernate log excerpt: WARN


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 09, 2006 11:40 am 
Expert
Expert

Joined: Tue Nov 23, 2004 7:00 pm
Posts: 570
Location: mostly Frankfurt Germany
I am not sure why this fails here but why do you actually implement it this way.

Hibernate supports inheritance and it is quite understandable explained in the reference. If you implement it the "Hibernate way" your example will work.

Best Regards

Sebastian

Please rate, if this helped.

_________________
Best Regards
Sebastian
---
Training for Hibernate and Java Persistence
Tutorials for Hibernate, Spring, EJB, JSF...
eBook: Hibernate 3 - DeveloperGuide
Paper book: Hibernate 3 - Das Praxisbuch
http://www.laliluna.de


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 09, 2006 12:22 pm 
Newbie

Joined: Mon Jan 09, 2006 10:43 am
Posts: 3
Location: Brussels, Belgium
Hello Sebastian.

Thanks for your post.

You may be referring to chapter 9 of the reference manual.

Some of the options therein are problematic for us, because we have a legacy database that we cannot change at will. This means no discriminator columns. And all columns are not-null.

So from the above, you can gather that 9.1.1, 9.1.3, 9.1.4 will not work.

We would like to implement it in the way that we put forward, because the DB tables are quite repetitive, not to say denormalized.

For instance, address information is
- contained as a <component> inside of a "Membership" table
- contained in a historical address table
- contained in several other tables

The most succint object model is quite simply one which looks very much like the one provided in the example.

This brings us to most of the other options in chapter 9. The database columns for each type of address have different names. This excludes 9.1.5 and 9.1.2.

Which brings us to 9.1.6. We do not need and cannot use the <any> element, because we have no common superclass, and cannot use discriminator columns. If we now just look at 9.1.6 without the <any> element, you basically get the sample code.

Again, we cannot tell the DB folks that they need to change their DB, because that would impact their historic (Cobol+RPG) code.

Have I missed anything?

TIA,



Ulrich


Top
 Profile  
 
 Post subject:
PostPosted: Sun Mar 09, 2008 3:05 am 
Newbie

Joined: Wed Dec 26, 2007 4:24 am
Posts: 4
to ukroener:
========

I had the same problem with hibernate 3.2.5.ga - Parent and Child classes, mapped into 2 different tables, without discriminator column. Which caused the ClassCastException while retrieving by example the Parent. Setting 'polymorphism="explicit"' on the Child mapping file, without breaking the actuall java inheritance, solved the problem.


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