-->
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: Denormalized Table per Class Hierarchy Problem
PostPosted: Thu Dec 01, 2005 8:51 pm 
Beginner
Beginner

Joined: Wed May 25, 2005 7:49 pm
Posts: 25
My database looks like this (yes, I know, but it's a legacy database):
Code:
EMP_ID|EMP_LEVEL_NAME|SLS_NBR|SLS_F_NAME|SLS_L_NAME|MGR_NBR   |MGR_F_NAME|MGR_L_NAME|VP_NBR|VP_F_NAME|VP_L_NAME

1     |SLS           |1      |BOB       |SMITH     |400       |STEVE     |JONES     |372   |LARRY    |JOHNSON


Here's the problem: When the collections are being loaded, it fires another query to find the children. However, since a given entity has its "NBR" field populated the same as its children, it gets returned. Hibernate tries to put it in the collection, and I get a WrongClassException.

Let me give an example: For the above data, when the salespeople for the Manager bean are being loaded, it queries for rows where MGR_NBR = 400. However, Steve Jones is returned, and the exception is thrown since it is expecting a Salesperson bean but gets a Manager bean.

Some questions:
    1) Why doesn't Hibernate only look for the correct classes and disregard the wrong classes? (e.g. Only return rows with EMP_LEVEL_NAME = 'SLS').
    2) If it doesn't disregard incorrect classes, is there a way to change my mapping documents so that I don't get this error?

I would greatly appreciate any help.

Hibernate version: 3.0.5

Mapping documents:
Code:
<?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 package="beans">
   <class name="Employee" table="EMPLOYEE">
      <id name="dbID" column="EMP_ID" />
      <discriminator column="LEVEL_NAME" />
      <subclass name="Salesperson" discriminator-value="SLS">
         <property name="salespersonID" column="SLS_NBR" />
         <property name="firstName" column="SLS_FIRST_NAME" />
         <property name="lastName" column="SLS_LAST_NAME" />
         <many-to-one name="manager" column="MGR_NBR" lazy="false" />
      </subclass>
      <subclass name="Manager" discriminator-value="MGR">
         <property name="managerID" column="MGR_NBR" />
         <property name="firstName" column="MGR_FIRST_NAME" />
         <property name="lastName" column="MGR_LAST_NAME" />
         <set name="salespersons" lazy="false">
            <key column="MGR_NBR" />
            <one-to-many class="Salesperson" />
         </set>
         <many-to-one name="vicePresident" column="VP_NBR" lazy="false" />
      </subclass>
      <subclass name="VicePresident" discriminator-value="VP">
         <property name="firstName" column="VP_FIRST_NAME" />
         <property name="lastName" column="VP_LAST_NAME" />
         <set name="managers" lazy="false">
            <key column="VP_NBR" />
            <one-to-many class="Manager" />
         </set>
      </subclass>
</hibernate-mapping>

Code between sessionFactory.openSession() and session.close():
Code:
      Session session = MetricsDA.currentSession(getConnection());
      Criteria query = session.createCriteria(Employee.class);
      List list = query.list();
      return list;

Full stack trace of any exception that occurs:
Code:
org.hibernate.WrongClassException: Object with id: 901 was not of the specified subclass: beans.Salesperson (loaded object was of wrong class)
   at org.hibernate.loader.Loader.instanceAlreadyLoaded(Loader.java:890)
   at org.hibernate.loader.Loader.getRow(Loader.java:846)
   at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:305)
   at org.hibernate.loader.Loader.doQuery(Loader.java:412)
   at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:218)
   at org.hibernate.loader.Loader.loadCollection(Loader.java:1434)
   at org.hibernate.loader.collection.OneToManyLoader.initialize(OneToManyLoader.java:111)
   at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:488)
   at org.hibernate.event.def.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:60)
   at org.hibernate.impl.SessionImpl.initializeCollection(SessionImpl.java:1430)
   at org.hibernate.collection.AbstractPersistentCollection.forceInitialization(AbstractPersistentCollection.java:280)
   at org.hibernate.engine.PersistenceContext.initializeNonLazyCollections(PersistenceContext.java:796)
   at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:223)
   at org.hibernate.loader.Loader.doList(Loader.java:1593)
   at org.hibernate.loader.Loader.list(Loader.java:1577)
   at org.hibernate.loader.criteria.CriteriaLoader.list(CriteriaLoader.java:111)
   at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1322)
   at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:300)
   at DA.test(DA.java:67)
   at DA.main(DA.java:61)

Name and version of the database you are using:
Oracle9i Enterprise Edition Release 9.2.0.3.0

The generated SQL (show_sql=true):
Code:
select this_.EMP_ID as EMP1_0_, this_.SLS_NBR as SLS3_0_0_, this_.SLS_FIRST_NAME as SLS4_0_0_, this_.SLS_LAST_NAME as SLS5_0_0_, this_.MGR_NBR as MGR6_0_0_, this_.MGR_FIRST_NAME as MGR7_0_0_, this_.MGR_LAST_NAME as MGR8_0_0_, this_.VP_NBR as VP9_0_0_, this_.VP_FIRST_NAME as VP10_0_0_, this_.VP_LAST_NAME as VP11_0_0_, this_.EMP_LEVEL_NAME as EMP2_0_ from EMPLOYEE this_


Top
 Profile  
 
 Post subject: A guess..
PostPosted: Thu Dec 01, 2005 9:15 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
The first thing I'd try in that mapping is to be specific about the many-to-one classes. I'm going off guesswork here, but try these:

1) In the two <set>s, add inverse="true".
2) In the two <many-to-one>s, add the appropriate class="" attributes. You're allowing them to map to any class. Use Manager or VicePresident as appropriate, maybe that will cuase it to not try any employee type.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 01, 2005 9:51 pm 
Beginner
Beginner

Joined: Wed May 25, 2005 7:49 pm
Posts: 25
Thanks for the help, tenwit, but the same thing happens. I'm wondering if Hibernate doesn't support a data model like this?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 14, 2005 2:46 pm 
Newbie

Joined: Tue Feb 08, 2005 3:30 am
Posts: 6
Location: Bangalore
I had the same problem, the root of the problem is I think hibernate is not filtering the data based on the discriminator values, but I found a work around for that. Change your mapping file to this.
Code:
<?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 package="beans">
   <class name="Employee" table="EMPLOYEE">
      <id name="dbID" column="EMP_ID" />
      <discriminator column="LEVEL_NAME" />
      <subclass name="Salesperson" discriminator-value="SLS">
         <property name="salespersonID" column="SLS_NBR" />
         <property name="firstName" column="SLS_FIRST_NAME" />
         <property name="lastName" column="SLS_LAST_NAME" />
         <many-to-one name="Employee" column="MGR_NBR" lazy="true" />
      </subclass>
      <subclass name="Manager" discriminator-value="MGR">
         <property name="managerID" column="MGR_NBR" />
         <property name="firstName" column="MGR_FIRST_NAME" />
         <property name="lastName" column="MGR_LAST_NAME" />
         <set name="Employee" lazy="true">
            <key column="MGR_NBR" />
            <one-to-many class="Salesperson" />
         </set>
         <many-to-one name="Employee" column="VP_NBR" lazy="true" />
      </subclass>
      <subclass name="VicePresident" discriminator-value="VP">
         <property name="firstName" column="VP_FIRST_NAME" />
         <property name="lastName" column="VP_LAST_NAME" />
         <set name="Employee" lazy="true">
            <key column="VP_NBR" />
            <one-to-many class="Manager" />
         </set>
      </subclass>
</hibernate-mapping>

Basically, the changes are the references within the subclasses to the superclass which is Employee in this case with lazy="true".

Please note: If you don't change the references from subclasses to "Employee", you will get rid of the error, BUT when you try to execute the the code manager.getSalesPerson() more generally if you try a method <subclass>.get<subclass>(), then it will give you a ClassCastExcpetion.
My though on the class cast Exception is:
Hibernate, when it gets the subclass, it will get it as the the super class and if your mapping is to the subclass, then it gets a classcastException.

I hope this helps.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 14, 2005 7:55 pm 
Beginner
Beginner

Joined: Wed May 25, 2005 7:49 pm
Posts: 25
It didn't work. While the error went away, it is still putting itself into the set of children. I'm just going to solve the problem by using a view and returning a correct data model.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 14, 2005 8:45 pm 
Beginner
Beginner

Joined: Wed May 25, 2005 7:49 pm
Posts: 25
I found a relatively straightforward way around this problem. By using a where attribute in the <set> elements, and setting it equal to the discriminator value, it only selects the desired types.

However, this might be something Hibernate should take care of automatically.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 15, 2005 5:18 am 
Newbie

Joined: Tue Feb 08, 2005 3:30 am
Posts: 6
Location: Bangalore
Can you please post the snippet of the DTD for the benefit of others. Thanks


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 15, 2005 10:13 am 
Beginner
Beginner

Joined: Wed May 25, 2005 7:49 pm
Posts: 25
No problem. So the only difference from the mapping document from before is that the sets within the Manager and VicePresident subclasses now have where clauses limiting the levels that should go into the sets.
Code:
<?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 package="beans">
   <class name="Employee" table="EMPLOYEE">
      <id name="dbID" column="EMP_ID" />
      <discriminator column="LEVEL_NAME" />
      <subclass name="Salesperson" discriminator-value="SLS">
         <property name="salespersonID" column="SLS_NBR" />
         <property name="firstName" column="SLS_FIRST_NAME" />
         <property name="lastName" column="SLS_LAST_NAME" />
         <many-to-one name="manager" column="MGR_NBR" lazy="false" />
      </subclass>
      <subclass name="Manager" discriminator-value="MGR">
         <property name="managerID" column="MGR_NBR" />
         <property name="firstName" column="MGR_FIRST_NAME" />
         <property name="lastName" column="MGR_LAST_NAME" />
         <set name="salespersons" lazy="false" where="LEVEL_NAME='SLS'">
            <key column="MGR_NBR" />
            <one-to-many class="Salesperson" />
         </set>
         <many-to-one name="vicePresident" column="VP_NBR" lazy="false" />
      </subclass>
      <subclass name="VicePresident" discriminator-value="VP">
         <property name="firstName" column="VP_FIRST_NAME" />
         <property name="lastName" column="VP_LAST_NAME" />
         <set name="managers" lazy="false" where="LEVEL_NAME='MGR'">
            <key column="VP_NBR" />
            <one-to-many class="Manager" />
         </set>
      </subclass>
</hibernate-mapping>


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.