-->
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.  [ 7 posts ] 
Author Message
 Post subject: one-to-one mapping problem
PostPosted: Fri Oct 28, 2005 9:12 pm 
Newbie

Joined: Fri Oct 28, 2005 8:23 pm
Posts: 12
Hi,
I have been working through the tutorials and sample apps for the past week. But I havent been able to find an example of what I am trying to do and havent had much success in my experimentaiton.

Heres the skinny... I have a person class (id, firstname, lastname, cellphone). I have a phone class (id, areacode, prefix, postfix). These two clasess releate to a table for each (person and phone). Simple enough...

What I would like to do is create a one-to-one relationship. Anytime I create a person and add a cellphone (instance of phone) to them. I would like to have the id of the phone record to have its reference (id) stored in the cellphone column of the person table.

All the examples I have seen appear to do this logic in reverse...they would store the personId in the phone table and then add some sort of 'type' (cell, home, etc) to the phone class.

Ultimately I would like to have multiple phone numbers attached to a person cellphone and homephone would be attributes of person. The would each be columns in the person table that would hold onto the Id from the phone table.

Heres my code...If someone could point me in the right direction it would be greatly appreciated.

One other question...Guid? Is this the best way to do things or should I let MSSQL auto number my PK's?

Code:

Person.cs

using System;

namespace SS
{
   
   public class Person
   {
      private Guid personId;
      private string firstName;
      private string lastName;
      private Phone cellPhone;
      
      public Person()
      {
         
      }
      public Guid PersonId
      {
         get { return personId; }
         set { personId = value; }
      }
      public string FirstName
      {
         get { return firstName; }
         set { firstName = value; }
      }
      public string LastName
      {
         get { return lastName; }
         set { lastName = value; }
      }
      public Phone CellPhone
      {
         get { return cellPhone; }
         set { cellPhone = value; }
      }
   }
}




Phone.cs


using System;

namespace SS
{
   
   public class Phone
   {
      private Guid phoneId;
      private string areaCode;
      private string prefix;
      private string postfix;
      
      public Phone()
      {
         
      }
      public Guid PhoneId
      {
         get { return phoneId; }
         set { phoneId = value; }
      }
   
      public string AreaCode
      {
         get { return areaCode; }
         set { areaCode = value; }
      }

      public string Prefix
      {
         get { return prefix; }
         set { prefix = value; }
      }

      public string Postfix
      {
         get { return postfix; }
         set { postfix = value; }
      }
   }
}


Person.hbm.xml

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
   <class name="SS.Person, SS" table="Person">
      <id name="PersonId" column="PersonId" type="Guid" unsaved-value="00000000-0000-0000-0000-000000000000">
         <generator class="guid" />
      </id>
      
      <property name="FirstName" column="FirstName" type="String" />
      <property name="LastName" column="LastName" type="String" />
            
      <one-to-one name="CellPhone" class="SS.Phone, SS" cascade="all" constrained="true" property-ref="PhoneId"/>
      

   </class>

</hibernate-mapping>



Phone.hbm.xml

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
   <class name="SS.Phone, SS" table="Phone">
      <id name="PhoneId" column="PhoneId" type="Guid" unsaved-value="00000000-0000-0000-0000-000000000000">
         <generator class="guid" />
      </id>
      <property name="AreaCode" column="AreaCode" type="String" />
      <property name="Prefix" column="Prefix" type="String" />
      <property name="Postfix" column="Postfix" type="String" />
      
   </class>

</hibernate-mapping>


my tables...

CREATE TABLE [Person] (
   [PersonID] [uniqueidentifier] NOT NULL ,
   [FirstName] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
   [LastName] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
   [CellPhone] [uniqueidentifier] NULL ,
   CONSTRAINT [PK_Person] PRIMARY KEY  CLUSTERED
   (
      [PersonID]
   )  ON [PRIMARY]
) ON [PRIMARY]
GO


CREATE TABLE [Phone] (
   [PhoneId] [uniqueidentifier] NOT NULL ,
   [AreaCode] [varchar] (3) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
   [Prefix] [varchar] (3) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
   [Postfix] [varchar] (4) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
   CONSTRAINT [PK_Phone] PRIMARY KEY  CLUSTERED
   (
      [PhoneId]
   )  ON [PRIMARY]
) ON [PRIMARY]
GO











code im testing with:

Configuration cfg = new Configuration();
cfg.AddAssembly("SS");
ISessionFactory factory = cfg.BuildSessionFactory();
ISession session = factory.OpenSession();
ITransaction transaction = session.BeginTransaction();

Person newUser = new Person();
newUser.FirstName = "JosephFirst";
newUser.LastName = "SmithLast";
         
Phone newCellPhone = new Phone();
newCellPhone.AreaCode = "410";
newUser.CellPhone = newCellPhone;

session.Save(newUser);
transaction.Commit();
session.Close();



Thanks again for you input

Sean


Top
 Profile  
 
 Post subject:
PostPosted: Sun Oct 30, 2005 11:59 am 
Contributor
Contributor

Joined: Thu May 12, 2005 9:45 am
Posts: 593
Location: nhibernate.org
Read: <one-to-one> Documentation

There is a special requirement (on the id) when trying to use "primary key associations"...

_________________
Pierre Henri Kuaté.
Get NHibernate in Action Now!


Top
 Profile  
 
 Post subject:
PostPosted: Mon Oct 31, 2005 7:39 am 
Beginner
Beginner

Joined: Mon Oct 03, 2005 4:59 am
Posts: 26
Location: Cambridge, UK
I know this forum isn't really for database design advice, but I can't help myself. As someone who has had to maintain and debug many databases designed the way you describe, I beg you, don't do it.

In the first place, what you describe, "Ultimately I would like to have multiple phone numbers attached to a person," is really a many to one relationship. Trying to model it as a column in the person table will bring you grief, in the form of columns like cellphone2, cellphone3, etc., and that way lies madness.

In the second place, even if you insist on a one-to-one relationship, e.g. if you have some special reason to restrict a person to one cellphone number, it still (IMO) shouldn't be a column in the person table. It should be another table with a foreign key to personid, and either the fk should also be the the primary key of the other table, or there should be a unique constraint on the FK. The documentation KPixel pointed to describes both these approaches.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Oct 31, 2005 9:47 pm 
Newbie

Joined: Fri Oct 28, 2005 8:23 pm
Posts: 12
I reread the documentation. I guess I should have explained that I had read it but wasnt really clear on how to implement what the doc had shown.

KPixel, You commented: "There is a special requirement (on the id) when trying to use "primary key associations"..."

From the doc I took it to understand the "primary key association" was to be implemented if I wanted to have my Phone PK and my Person PK share the same key. I want to store the information in a seperate column (cellphone) a reference to the Phone PK.

For this reason I implemented the "unique foreign key associations". Is this the correct one to use?

I have added the following to my Person.hbm.xml

Code:
<many-to-one name="CellPhone" class="SS.Phone, SS" column="CellPhone" unique="true" />


This will now allow me to store the pk of the phone in the person.

A few more questions...


1. Do you have to save the instance of cellphone manually before you save the person? In my experimentation it seems that you do...I get an exception about an unsaved transient if I dont. I would think the save would take care of the whole save since I set the instance of the phone to the users cell phone. I guess Im wondering if therre is a better / cleaner way to do this.

This is how im saving things currently

Code:
Person newUser = new Person();
newUser.FirstName = "JosephFirst";
newUser.LastName = "SmithLast";

Phone newCellPhone = new Phone();
newCellPhone.AreaCode = "410";
   
newUser.CellPhone = newCellPhone; //Would'nt this add it to the 'dirty set'?
session.Save(newCellPhone); //I have to save the cellphone first

session.Save(newUser);  //Why doesnt this persist the cell at the same time?




2. Whenever I attempt to add the one-to-one relationship to the phone (to make it bidirectional, according to the doc). Using this:

Code:
<one-to-one name="PhoneIdAsOtherProperty" class="SS.Person, SS" property-ref="CellPhone"/>

If I return phone id through antother method. I get an exception:

Code:

[ArgumentException: There is a problem with your mappings.  You are probably trying to map a System.ValueType to a <class> which NHibernate does not allow or you are incorrectly using the IDictionary that is mapped to a <set>. 

A ValueType can not be used with IdentityKey.  The thread at google has a good description about what happens with boxing and unboxing ValueTypes and why they can not be used as an IdentityKey: http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&threadm=bds2rm%24ruc%241%40charly.heeg.de&rnum=1&prev=/groups%3Fhl%3Den%26lr%3D%26ie%3DUTF-8%26oe%3DUTF-8%26q%3DSystem.Runtime.CompilerServices.RuntimeHelpers.GetHashCode%26sa%3DN%26tab%3Dwg
Parameter name: key]



As you can see im lost. what should the appropiate property (name) be for the one-to-one mapping?


gekannt, I agree, with your comments. This probally wasnt the best example but I do feel there are situations that would require this approach.


Thanks again for you input



Sean


Top
 Profile  
 
 Post subject:
PostPosted: Tue Nov 01, 2005 4:55 am 
Contributor
Contributor

Joined: Thu May 12, 2005 9:45 am
Posts: 593
Location: nhibernate.org
1) I think that you need to set: cascade="all|save-update"

2) Can you give the type of "PhoneIdAsOtherProperty" and "CellPhone" in your class?

_________________
Pierre Henri Kuaté.
Get NHibernate in Action Now!


Top
 Profile  
 
 Post subject:
PostPosted: Tue Nov 01, 2005 6:21 pm 
Hi,
Cellphone is defined in the Person class as:

Code:
public Phone CellPhone
      {
         get { return cellPhone; }
         set { cellPhone = value; }
      }



I just created PhoneIdAsOtherProperty as a method that just returns the ID. It seems once I define something as an id in my xml mapping file I cant define it as a property too? So I just created a different method to return it. I didnt really expect this to work but was running out of ideas on how to make the one-to-one relationship 'bidirectional' on the phone class.

Code:

public Guid PhoneIdAsOtherProperty
      {
         get { return phoneId; }
         set { phoneId = value; }
      }



Do I need to define cellphone on the phone class? Im just not clear on what I should be using as the 'property' on the one-to-one relationship in the phone xml file.

The cascade worked like a charm :-)

Thanks

Sean


Top
  
 
 Post subject:
PostPosted: Thu Nov 03, 2005 11:24 am 
Contributor
Contributor

Joined: Thu May 12, 2005 9:45 am
Posts: 593
Location: nhibernate.org
I don't have much experience with <one-to-one> mapping, but it looks like you should write:
Code:
<one-to-one name="CellPhone" class="SS.Person, SS" property-ref="PhoneIdAsOtherProperty"/>

_________________
Pierre Henri Kuaté.
Get NHibernate in Action Now!


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