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.  [ 9 posts ] 
Author Message
 Post subject: possible joined-subclass issue
PostPosted: Sun Dec 17, 2006 9:36 pm 
Newbie

Joined: Mon Aug 21, 2006 9:47 am
Posts: 12
Hello everybody,

I have a case with three classes.

Code:
using System;
namespace Vita.Pesa.Domain
{
   [Serializable]
   public class Company
   {
      private int _id;
      private ClassificatorValue _state;

      

      /// <summary>
      ///
      /// </summary>      
      public virtual ClassificatorValue State
      {
         get { return _state; }
         set { if (_state != value) _state = value; }
      }

      /// <summary>
      /// Gets the unique id of a domain object (Company).
      /// </summary>
      /// <value></value>
      public virtual int Id
      {
         get { return _id; }
      }


   }//end Company
}

using System;
namespace Vita.Pesa.Domain
{
   [Serializable]
   public class Client : Company
   {

      private int _id;

      private ClassificatorValue _state;

      
      /// <summary>
      /// Gets or sets the Client id.
      /// </summary>
      /// <value>The id.</value>
      public new virtual int Id
      {
         get { return _id; }
         //set { if (_id != value) _id = value; }
      }

      

      public new virtual ClassificatorValue State
      {
         get
         {
            return _state;
         }
         set
         {
            if (_state != value) _state = value;
         }
      }
      
   }//end Client
}

using System;

namespace Vita.Pesa.Domain
{
   [Serializable]
   public class Contact
   {

      private Client _client;
      private int _id;
      private ClassificatorValue _state;


      /// <summary>
      /// Gets or sets the client this contact belongs to.
      /// </summary>
      public virtual Client Client
      {
         get
         {
            return _client;
         }
         set
         {
            _client = value;
         }
      }

      public virtual int Id
      {
         get { return _id; }
         //set { if (_id != value) _id = value; }
      }

      

      public virtual ClassificatorValue State
      {
         get { return _state; }
         set { if (_state != value) _state = value; }
      }

   }//end Contact
}


And i have this mapping inbetween:

Code:
Company.hbm.xml:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   namespace="Vita.Pesa.Domain"
   assembly="Vita.Pesa" xmlns="urn:nhibernate-mapping-2.2">
   <class
      name="Vita.Pesa.Domain.Company,Vita.Pesa"
      lazy="true"
      table="Company">
      <id
         name="Id"
         access="field.camelcase-underscore"
         column="Id"
         type="Int32">
         <generator
            class="identity" />
      </id>
      <many-to-one
         name="State"
         class="ClassificatorValue"
         column="StateClvId"
         not-null="true" />
      <joined-subclass
         name="Vita.Pesa.Domain.Client,Vita.Pesa"
         lazy="true"
         extends="Vita.Pesa.Domain.Company,Vita.Pesa"
         table="Client">
         <key
            column="CompanyId" />
         <many-to-one
            name="State"
            class="ClassificatorValue"
            column="StateClvId" />
      </joined-subclass>
   </class>
</hibernate-mapping>

Contact.hbm.xml:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   namespace="Vita.Pesa.Domain"
   assembly="Vita.Pesa" xmlns="urn:nhibernate-mapping-2.2">
   <class
      name="Vita.Pesa.Domain.Contact,Vita.Pesa"
      lazy="true"
      table="Contact">
      <id
         name="Id"
         access="field.camelcase-underscore"
         column="Id"
         type="Int32"
         unsaved-value="0">
         <generator
            class="identity" />
      </id>
         <many-to-one
            name="Client"
            class="Client"
            column="ClientId" />
         <many-to-one
            name="State"
            class="ClassificatorValue"
            column="StateClvId"
            not-null="true" />

   </class>
</hibernate-mapping>


NHibernate version: 1.2.0 Beta 2

Now when i try to create a new Client (which inherits from Company) i get an object client, which has the ID from Company and the company object (when i cast ((Company)client).Id ) is 0.

Code:
//variables state are declared somewhere up in the code

Client client = Client.Create(state);
((Company)client).State = state;
session.Save(client);

Console.Write("client id: " + client.Id.ToString() + ", company id: " + ((Company)client).Id.ToString());


result: client id: 102, company id: 0

When checking from the database.

Newly added Company row has Id 102, newly added Client has Id 76.

Why is it not mapping the correct Id -s to the object back ?

Could this be a bug ?

Could this be something situated with the fact, that i have two classes that contain same properties. (Id, State) and the subclass is hiding the super class properties (with new keyword).

I use the subclass, and i access the superclass properties (State property) when i cast it to superclass.

I see no point in naming the properties according to the class or table names, since all classes have Id and State.

Why i brought in the Contact class ? Because the contact should have the reference to the client,but unfortunately this is not possible, since the client object has wrong Id value.

(This is how i discovered this). I have been trying to unit test it from several corners and i have also checked my mapping for tens of times,
I have not managed to resolve this.

ANy help is higly appriciated,
Is this information enough for analysis?
I gladly answer any extra information questions.

Taavi Kõosaar


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 18, 2006 3:45 am 
Newbie

Joined: Mon Aug 21, 2006 9:47 am
Posts: 12
Im speculating, that because i have the same properties on both superclass and subclass, it is not ommitting the values to the right object.

It is creating the rows in the database correctly, but fails to add the company id right. It should cast to the Company, before omitting to the superclass and it could freely add to the subclass.

This is the generated SQL (the actual model has more parameters than i have in the example):

Code:
NHibernate: INSERT INTO Company (VATIN, RegistrationCode, BusinessTypeClvId, StateClvId, Name) VALUES (@p0, @p1, @p2, @p3, @p4); select SCOPE_IDENTITY(); @p0 ='19827744', @p1 = '123456789', @p2 = '360', @p3 = '358', @p4 = '4home.ee'

NHibernate: INSERT INTO Client (ClientCardCode, PaymentDue, CreditLimit, StateClvId, ExtraInfo, StartDate, EndDate, CompanyId) VALUES (@p0, @p1, @p2, @p3, @p4,@p5, @p6, @p7); @p0 = '', @p1 = '', @p2 = '', @p3 = '358', @p4 = '', @p5 = '18.12.2006 10:00:21', @p6 = '', @p7 = '109'

client id: 109, company id: 0 (Console.WriteLine)

NHibernate: INSERT INTO Person (FirstName, LastName, Code, BirthDate, JobTitleClvId, SexClvId) VALUES (@p0, @p1, @p2, @p3, @p4, @p5); select SCOPE_IDENTITY(); @p0 = 'Jama', @p1 = 'JamaPerekonnanimi', @p2 = '1234567', @p3 = '', @p4 = '', @p5 = '359'
NHibernate: INSERT INTO Contact (ClientId, StateClvId, EmployeeId, StartDate, EndDate, PersonId) VALUES (@p0, @p1, @p2, @p3, @p4, @p5); @p0 = '109', @p1 = '358', @p2 = '', @p3 = '18.12.2006 10:00:21', @p4 = '', @p5 = '60'



Could a solution be to somehow set, what the joined-subclasses Id column is ? i havent found any option to set it unfortunately.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 18, 2006 4:42 am 
Newbie

Joined: Mon Aug 21, 2006 9:47 am
Posts: 12
OK, i created a even more simple test and now with different column and property names.

Mapping:

Code:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   namespace="Vita.Pesa.Domain"
   assembly="Vita.Pesa" xmlns="urn:nhibernate-mapping-2.2">
   <class
      name="TempTestClass"
      lazy="true"
      table="TempTestClass">
      <id
         name="TempId"
         column="Id"
         type="Int32">
         <generator
            class="identity" />
      </id>
      <property
         name="Name"
         type="String"
         column="Name"
         length="200"
         not-null="true" />
      <joined-subclass
         name="SubTempTestClass"
         lazy="true"
         extends="TempTestClass"
         table="SubTempTestClass"
         >
         <key
            column="TempTestClassId" />
         <property
         name="Name"
         type="String"
         column="Name"
         length="200"
         not-null="true" />
      </joined-subclass>
   </class>
</hibernate-mapping>

Code:

namespace Vita.Pesa.Domain
{
   public class TempTestClass
   {
      private int _tempId;
      private string name;


      public virtual  int TempId
      {
         get { return _tempId; }
         set { _tempId = value; }
      }

      public virtual  string Name
      {
         get { return name; }
         set { name = value; }
      }
   }
   public class SubTempTestClass : TempTestClass
   {
      private int _subTempId;
      private string _name;

      public virtual int SubTempId
      {
         get { return _subTempId; }
         set { _subTempId = value; }
      }

      public new virtual string Name
      {
         get { return _name; }
         set { _name = value; }
      }
   }
}

The Database schema:

CREATE TABLE [dbo].[TempTestClass](
   [TempId] [int] IDENTITY(1,1) NOT NULL,
   [Name] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
CONSTRAINT [PK_TempTestClass] PRIMARY KEY CLUSTERED
(
   [TempId] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]


CREATE TABLE [dbo].[SubTempTestClass](
   [SubTempId] [int] IDENTITY(1,1) NOT NULL,
   [Name] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
   [TempTestClassId] [int] NOT NULL,
CONSTRAINT [PK_SubTempTestClass] PRIMARY KEY CLUSTERED
(
   [SubTempId] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

GO
ALTER TABLE [dbo].[SubTempTestClass]  WITH CHECK ADD  CONSTRAINT [FK_SubTempTestClass_TempTestClass] FOREIGN KEY([TempTestClassId])
REFERENCES [dbo].[TempTestClass] ([TempId])
GO
ALTER TABLE [dbo].[SubTempTestClass] CHECK CONSTRAINT [FK_SubTempTestClass_TempTestClass]

The test code:

using (ISession session = NHibernateUtil.GetCurrentSession())
                  using (ITransaction tx = session.BeginTransaction())
                  {
                     SubTempTestClass testClass = new SubTempTestClass();

                     testClass.Name = "name";

                     session.SaveOrUpdate(testClass);

                     Assert.IsNotNull(testClass);
                     Assert.AreEqual(testClass.Name, "name", "Client.Name = 'name'");
                     Console.Write("SubTempClass id: " + testClass.SubTempId.ToString() + ", TempClass id: " + ((TempTestClass)testClass).TempId.ToString());
                     Assert.AreNotEqual(((TempTestClass)testClass).TempId, 0, "TempTestClass ID should not be 0!");
                     Assert.AreNotEqual(testClass.SubTempId, 0, "SubTempTestClass ID should not be 0!");

                     session.Delete(testClass);
                     tx.Commit();
                  }

Generated SQL by nHibernate:

NHibernate: INSERT INTO TempTestClass (Name) VALUES (@p0); select SCOPE_IDENTITY(); @p0 = 'name'
NHibernate: INSERT INTO SubTempTestClass (Name, TempTestClassId) VALUES (@p0, @p1); @p0 = 'name', @p1 = '17'





My result is, that SubTempTestClass.SubTempId = 0.

This should not happen. Basicly, it is not selecting the ID from the joined-subclass table. Why ? How could i make it return it ?

I am doing my mapping configuration wrong somewhere on both cases? I must be doing something wrong ?
The joined-subclass just aint working for me right now :(


Last edited by melborp on Mon Dec 18, 2006 6:45 am, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 18, 2006 6:40 am 
Expert
Expert

Joined: Thu Jan 19, 2006 4:29 pm
Posts: 348
melborp wrote:
My result is, that SubTempTestClass.SubTempId = 0.

This should not happen.

I am doing my mapping configuration wrong somewhere on both cases? I must be doing something wrong ?
The joined-subclass just aint working for me right now :(


You can not have two different Id-s for instance. Thus, as You can see, there is no <id> mapping for <joined-subclass>

The Id stored in <key> column of joind subclass must be equal to the Id of parent table - otherwise the NHibernate has no way to understand the the two rows belong together. So, when creating SubTempTestClass, do not specify column type to be Identity but integer.

Gert

_________________
If a reply helps You, rate it!


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 18, 2006 7:00 am 
Newbie

Joined: Mon Aug 21, 2006 9:47 am
Posts: 12
This would also mean i have to start taking care of auto incrementing the ID of SubTempClass or change the Id generation algorithm.

Why is it not implemented so, that you could set the id for parent and child table ?

If i have object Company, this will have one Id.
if i have object Client, which has the properties from Company (inherits from it) then it is pretty obvious i will have seperate id for Client.

Company is not client, client is company (has both attributes).

If this is so, it is more wiser for me to take over the generation of ID columns everywhere (in constructor initialize new Id for the object) ?

Taavi


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 18, 2006 7:06 am 
Expert
Expert

Joined: Thu Jan 19, 2006 4:29 pm
Posts: 348
melborp wrote:
This would also mean i have to start taking care of auto incrementing the ID of SubTempClass or change the Id generation algorithm.

Why is it not implemented so, that you could set the id for parent and child table ?

If i have object Company, this will have one Id.
if i have object Client, which has the properties from Company (inherits from it) then it is pretty obvious i will have seperate id for Client.

Company is not client, client is company (has both attributes).


Think as about computer memory:

You have an SubTempClass object, it is stored at specified location in memory. The memopry location is same, no metter if You treat is as Object, TempClass or SubTempClass.

It is simply so that one class instance can't have two primary key values.

Getr

_________________
If a reply helps You, rate it!


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 18, 2006 8:35 am 
Newbie

Joined: Mon Aug 21, 2006 9:47 am
Posts: 12
OK,

The solution is to have two different ID -s. I can still use database identity column when i disallow the insert and update to the Id column property in the SubTempClass.

The only uncomfortable thing is, i have to refresh the SubTempClass after the Save command. Otherwise the Id value is missing from SubTempClass instance.

I understand your point of view, but the fact is i have 2 tables i am getting the data from that maps to Client class which inherits from Company.

Client has all the logic from Company, but defnately exists sepereately as well.
I also have seperate meaning for Company (which does not affect Client). What i mean is i use Company object seperately as well.

OK, they can have same id in the instance, but once i start using the client object somewhere and commit the changes (e.g. create Contact object which is situated with Client) the ID sent to the database is incorrect.

Suddenly i have company id put somewhere an Client id should be.

In the object world i understand this, but in the database world i have normalized structure.

I should be able to map two tables to one class without any problems.

c# also allows hiding of properties with the new keyword. in the object oriented sense, it all works but it doesnt map back to the databse correctly, since nhibernate does not make any difference between the ID -s.

I understand, that it is also not meant to.

Taavi


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 18, 2006 9:38 am 
Expert
Expert

Joined: Thu Jan 19, 2006 4:29 pm
Posts: 348
You propably have mis-designed Your class structure:

The client should be a Role of Company, not inherited from Company.

If Client is inherited from company, the client must be a company. (but company might not be a client). This contradicts with

Quote:
Client has all the logic from Company, but defnately exists sepereately as well.


Gert

_________________
If a reply helps You, rate it!


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 19, 2006 6:54 am 
Newbie

Joined: Mon Aug 21, 2006 9:47 am
Posts: 12
You are totally correct, Gert.

I am dealing with domain design and databse design and trying to map those things together and it is not going that well. (database design is not done by me).

I have to agree, that company and client in our sense are not the same things. Unfortunately, our database designer has made it so, that the company table has all the company data in it and the client has all the client data in it, but without the Company attributes.

Its designed so, that all the data in the client object comes from company and client (so it is normalized and no duplication of data is being done). So the Company table plays two roles together.

in our business logic .. there is a company (a owner of our software).
And there is client (which is a legal entity) and has the attributes from client table and company table.

Company has clients.
Client also should look like a company.

there is something fishy in this and i couldnt come up a better mapping than with joined-subclass (i dident know of the one key limitation at that point, a logical limitation).

Thanks Gert,

Taavi


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