-->
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.  [ 17 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: One-to-one? relationship help
PostPosted: Tue Dec 27, 2005 9:41 am 
Newbie

Joined: Tue Dec 27, 2005 8:10 am
Posts: 7
Hi. I'm trying to use a special relationship between the same table. An oversimplified example would be:

The class hierarchy is:
Person
Man: Person
MarriedMan: Man
Woman: Person
MarriedWoman: Person

They are all mapped to the same table (people). The table would have common columns, and the one that's giving me problems: spouseID. There I would like to store a FK relationship to the person's wife or husband when he or she is married. E.G.
Code:
Id | Discr  | Name              | SpouseID
-------------------------------------------
1  |   M    | Robert            | NULL
2  |   W    | Laura             | NULL
3  |   MM   | Thomas            |  4
4  |   MW   | Anna              |  3


Hibernate version:
NHibernate 1.0.1

I mapped it like this:
Code:
<class name="person" table="people">
    <subclass name="man" discriminator-value="M">
        <subclass name="marriedman" discriminator-value="MM">
            <many-to-one name="wife" column="spouseID">
        </subclass>
    </subclass>

    <subclass name="woman" discriminator-value="W">
        <subclass name="marriedwoman" discriminator-value="MW">
            <many-to-one name="husband" column="spouseID">
        </subclass>
    </subclass>
</class>


"Wife" property is of type "marriedwoman" and "Husband" is of type "marriedman".
I created a new instance (of a couple) and saved it ok (with the table looking like the example above).
But, when I try to load the instance id 3 I get WrongClassException (of id 4 class marriedWoman).
If I give wife and husband each their own column, it works ok, but i would like to use the same column for both.
Any ideas?

Thanks a lot,

Nicolas
from Argentina


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 27, 2005 1:23 pm 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
Please post your code and logs.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 27, 2005 2:32 pm 
Newbie

Joined: Tue Dec 27, 2005 8:10 am
Posts: 7
Thanks for the response. Let me say first that this is a dumb-down example, I'm not really modelling married people :)
The classes:

Code:
    public abstract class Person
    {
        private int _id;

        public int Id
        {
            get { return _id; }
            set { _id = value; }
        }
   
        private string _name;
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
   
    }
    public class Man : Person
    {
    }
    public class Woman : Person
    {
    }
    public class MarriedMan : Man
    {
        private MarriedWoman _wife;

        public MarriedWoman Wife
        {
            get { return _wife; }
            set { _wife = value; }
        }
   
    }
    public class MarriedWoman : Woman
    {
        private MarriedMan _husband;

        public MarriedMan Husband
        {
            get { return _husband; }
            set { _husband = value; }
        }
    }


The mapping:
Code:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0" default-access="nosetter.camelcase-underscore" >
  <class name="SancorSeguros.Inversiones.Person, SancorSeguros.Inversiones" table="Person" discriminator-value="-1">
    <id name="Id" unsaved-value="0" access="nosetter.camelcase-underscore" >
      <generator class="identity" />
    </id>
    <discriminator column="type" />
    <property name="Name" />
    <subclass name="SancorSeguros.Inversiones.Man, SancorSeguros.Inversiones" discriminator-value="M">
      <subclass name="SancorSeguros.Inversiones.MarriedMan, SancorSeguros.Inversiones" discriminator-value="MM">
        <many-to-one name="Wife" column="spouseID" />
      </subclass>
    </subclass>
    <subclass name="SancorSeguros.Inversiones.Woman, SancorSeguros.Inversiones" discriminator-value="W">
      <subclass name="SancorSeguros.Inversiones.MarriedWoman, SancorSeguros.Inversiones" discriminator-value="WW">
        <many-to-one name="Husband" column="spouseID" />
      </subclass>
    </subclass>
  </class>
</hibernate-mapping>

The table contains the data specified above.
To make it fail:
Code:
Person person = (Person)sess.Load(typeof(Person), 3);


The log:
Code:
2005-12-27 15:29:03,078 [5808] INFO  NHibernate.Loader.Loader [(null)] &lt;(null)&gt; - SELECT person0_.Id as Id3_, person0_.type as type3_, person0_.Name as Name3_, person0_.spouseID as spouseID3_, marriedwom1_.Id as Id0_, marriedwom1_.spouseID as spouseID0_, marriedwom1_.Name as Name0_, marriedman2_.Id as Id1_, marriedman2_.spouseID as spouseID1_, marriedman2_.Name as Name1_, marriedman3_.Id as Id2_, marriedman3_.spouseID as spouseID2_, marriedman3_.Name as Name2_ FROM Person person0_ left outer join Person marriedwom1_ on person0_.spouseID=marriedwom1_.Id left outer join Person marriedman2_ on marriedwom1_.spouseID=marriedman2_.Id left outer join Person marriedman3_ on person0_.spouseID=marriedman3_.Id WHERE person0_.Id=@p0
2005-12-27 15:29:03,234 [5808] WARN  NHibernate.Util.ADOExceptionReporter [(null)] &lt;(null)&gt; - NHibernate.WrongClassException: Object with id: 4 was not of the specified sublcass: SancorSeguros.Inversiones.MarriedMan (loading object was of wrong class)
   at NHibernate.Loader.Loader.InstanceAlreadyLoaded(IDataReader rs, Int32 i, ILoadable persister, String suffix, Key key, Object obj, LockMode lockMode, ISessionImplementor session)
   at NHibernate.Loader.Loader.GetRow(IDataReader rs, ILoadable[] persisters, String[] suffixes, Key[] keys, Object optionalObject, Key optionalObjectKey, LockMode[] lockModes, IList hydratedObjects, ISessionImplementor session)
   at NHibernate.Loader.Loader.GetRowFromResultSet(IDataReader resultSet, ISessionImplementor session, QueryParameters queryParameters, IList hydratedObjects, Object optionalObject, Object optionalId, Key[] keys, Boolean returnProxies)
   at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Object optionalObject, Object optionalId, Object[] optionalCollectionKeys, Boolean returnProxies)
2005-12-27 15:29:03,250 [5808] ERROR NHibernate.Util.ADOExceptionReporter [(null)] &lt;(null)&gt; - Object with id: 4 was not of the specified sublcass: SancorSeguros.Inversiones.MarriedMan (loading object was of wrong class)




If I ask for the exact type, not the superclass, it works ok (but it is not really an option):
Code:
Person person = (Person)sess.Load(typeof(MarriedMan), 3);

Also, asking for ID #4 works ok:
Code:
Person person = (Person)sess.Load(typeof(Person), 4);


This is .Net 2.0, MS SQL Server 2000
I can see in the SELECT that it goes like a circular reference, joining the person table four times.

BTW, the data was added without FK checks, I wonder how will I make the double reference work...


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 27, 2005 4:02 pm 
Expert
Expert

Joined: Tue Aug 23, 2005 5:52 am
Posts: 335
The issue you'll probably be having with the specific code you're using to make it fail...

Code:
Person person = (Person)sess.Load(typeof(Person), 3);


...is that when using Load you're actually asking the NHIbernate to instantiate the specific type from the database where the type is in fact an abstract class. Abstract classes can't be instantiated, so it coughs up a hairball.

If you do the same thing using HQL you should be fine:

Code:
FROM Person p WHERE p.id = :Id


(setting :Id to equal the Id of the person you want to load)

NHibernate will then find any subclasses of Person that have the id and then return them, so your result will in fact contain the specific subclass which can be cast to Person.

No entirely sure that this addresses your initial quesion, but I hope it helps!

Symon.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 27, 2005 4:08 pm 
Newbie

Joined: Tue Dec 27, 2005 8:10 am
Posts: 7
Thanks Symon. I understand what you say, but I don't think that's quite true. I use Load(typeof(abstract_class)) in many other places and it doesn't seem to create any problems, instantiating the correct type every time. Also, while
Code:
Person person = (Person)sess.Load(typeof(Person), 3); 


doesn't work for id 3, it works for id 1,2 and 4.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 27, 2005 7:20 pm 
Expert
Expert

Joined: Tue Aug 23, 2005 5:52 am
Posts: 335
Hmm. OK - I stand corrected. Learn something new every day!

Cheers,

Symon.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 28, 2005 5:53 pm 
Expert
Expert

Joined: Fri Oct 28, 2005 5:38 pm
Posts: 390
Location: Cedarburg, WI
Did you try defining the class in the many-to-one tags? E.g.

Code:
<class name="person" table="people">
    <subclass name="man" discriminator-value="M">
        <subclass name="marriedman" discriminator-value="MM">
            <many-to-one name="wife" class="marriedwoman" column="spouseID">
        </subclass>
    </subclass>

    <subclass name="woman" discriminator-value="W">
        <subclass name="marriedwoman" discriminator-value="MW">
            <many-to-one name="husband" class="marriedman" column="spouseID">
        </subclass>
    </subclass>
</class>


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 28, 2005 9:13 pm 
Newbie

Joined: Tue Dec 27, 2005 8:10 am
Posts: 7
Nels_P_Olsen wrote:
Did you try defining the class in the many-to-one tags?


Yes, no luck :(


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 29, 2005 6:52 am 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
Looks like it's a bug, I'll look into it.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 29, 2005 7:06 am 
Newbie

Joined: Tue Dec 27, 2005 8:10 am
Posts: 7
Thanks!


Top
 Profile  
 
 Post subject: Any update on this
PostPosted: Wed Jan 04, 2006 4:26 am 
Regular
Regular

Joined: Tue Jan 03, 2006 7:21 am
Posts: 85
I too am facing a similar situation. Is this a bug in NHibernate?

Thanks
Job Samuel.


Top
 Profile  
 
 Post subject: More Info
PostPosted: Wed Jan 04, 2006 6:00 am 
Regular
Regular

Joined: Tue Jan 03, 2006 7:21 am
Posts: 85
This works correctly if we set the outer-join='false' on the 2 many-one relationship.
I debugged into the NHibernate and found that the cause of the problem was that it was instantiating MarriedWoman class for Id 3 (when it should have instantiated MarriedMan class) and hence we get an exception stating "{"Object with id: 4 was not of the specified sublcass: DAL.MarriedMan (loading object was of wrong class)" }".
This does seem to be a bug.

However setting outer-join='false' forces NHibernate to fire different SQL query to fetch the relationship and this seems to work.

Job Samuel.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 09, 2006 7:53 am 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
Ok, I investigated it, and the problem can't be fixed easily, it can be worked around by disabling outer-join fetching for many-to-one's, making classes lazy, or avoiding the reuse of spouseID column.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jan 15, 2006 12:35 pm 
Newbie

Joined: Tue Dec 27, 2005 8:10 am
Posts: 7
Thank you all for all the prompt responses.

Nicolas


Top
 Profile  
 
 Post subject:
PostPosted: Tue Oct 03, 2006 5:43 am 
Newbie

Joined: Mon Aug 28, 2006 9:04 am
Posts: 2
This problem solved in NHibernate?


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 17 posts ]  Go to page 1, 2  Next

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.