-->
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: Update foreign key value
PostPosted: Thu Mar 30, 2006 3:51 pm 
Newbie

Joined: Thu Mar 30, 2006 3:26 pm
Posts: 3
Background:
NHibernate Version: 1.0.2.0
DB: Sql Server 2000

Problem:

I have a table in which an enumerated list of values reside; tablename is "statusList". This table contains an identity column called "statusId" and a single, unique string column named "statusName". This table is represented by the following class:

Code:
public class StatusClass {

  public int ID
  {
      get { return _id; }
      set { _id = value; }
  }
  private int _id;

  public StatusCode Status
  {
      get { return _status; }
      set { _status = value; }
  }
  private StatusCode _status;
}


The StatusCode type is an enumeration. In order to map the string values in the statusName column, I have created a StatusType class that inherits from EnumStringType:

Code:
public class StatusType : EnumStringType
{
  public StatusType()
      : base(typeof(StatusCode), 50) {}
}


I then defined the mappings for this class to:

Code:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
   <class name="AssemblyName.StatusClass, AssemblyName" table="statusList">
      <id name="ID" type="int" column="statusId">
         <generator class="identity" />
      </id>
      <property name="Status" column="statusName"
         type="AssemblyName.StatusType, AssemblyName" />      
   </class>
</hibernate-mapping>


Now, the issue is in the many side of this where a statusId is assigned to an entity. So, let's say the entity is defined in an entity table. This table contains an entityId identity column, entityName string column, and statusId column that holds a foreign key to my statusList table. The object representing this then looks like:

Code:
public class Entity
{
  public int ID
  {
      get { return _id; }
      set { _id = value; }
  }
  private int _id;

  public String Name
  {
      get { return _name; }
      set { _name = value; }
  }
  private String _name;

  public StatusClass Status
  {
      get { return _status; }
      set { _status = value; }
  }
  private StatusClass _status;
}


And, the mapping file looks like:

Code:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
   <class name="AssemblyName.Entity, AssemblyName" table="entity">
      <id name="ID" type="int" column="entityId">
         <generator class="identity" />
      </id>
      <property name="Name" column="entityName" type="string" />
      <many-to-one name="Status" class="AssemblyName.StatusClass, AssemblyName"
         column="statusId" />
   </class>
</hibernate-mapping>


Now, when I get an entity from the database, the Status property contains a Status property that represents the appropriate StatusCode enumeration value. If, however, I update that StatusCode value and save the entity back, hibernate does not change the foreign key on the entity record. Instead, it changes the string value in the statusList table where that statusId resides.

So, if the entity record is:
entityId: 1
entityName: name
statusId: 1

And, the statusList table has (in the form of statusId, statusName on each line):
1, On
2, Off

If I do:
Code:
...
entityObj.Status.Status = StatusCode.Off;
session.save(entityObj);
...


the entity record does not change (still has 1 for a statusId), however the statusList table now looks like (assuming no unique name constraint):
1, Off
2, Off

How do I do this so that the statusId value on the entity record updates to "2" instead of updating the foreign table's value??? Thanks in advance for any help you may be able to provide.


Top
 Profile  
 
 Post subject: Re: Update foreign key value
PostPosted: Fri Mar 31, 2006 2:42 am 
Expert
Expert

Joined: Thu Jan 19, 2006 4:29 pm
Posts: 348
try
Code:
...
entityObj.Status = StatusClass.GetInstanceForCode(StatusCode.Off);
session.save(entityObj);
...


where the StatusClass.GetInstanceForCode returns appropriate instance of StatusClass

Gert


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 31, 2006 7:05 pm 
Newbie

Joined: Thu Mar 30, 2006 3:26 pm
Posts: 3
There is a "GetInstance()" method on EnumStringType. However, the type that I have inheriting from this class is StatusType, which is only used in the mapping file to tell Hibernate what to use to set the appropriate StatusCode enumeration value on my Status property for the Entity object (where the Status is of type StatusClass, not StatusType).

I'm not sure if that's the method to which you refer, but if it is, I don't have an instance of the object (as far as I can tell). Rather, I have an actual enumeration value on a StatusClass instance.

Now, from your code "GetInstanceForCode()" appears to be a static method. Since my current StatusClass class does not inherit from anything other than Object, I don't have such a method. Am I missing something in my inheritance tree? Should I be inheriting StatusClass from something else?


Top
 Profile  
 
 Post subject:
PostPosted: Sat Apr 01, 2006 1:31 am 
Expert
Expert

Joined: Thu Jan 19, 2006 4:29 pm
Posts: 348
noble1 wrote:
Now, from your code "GetInstanceForCode()" appears to be a static method. Since my current StatusClass class does not inherit from anything other than Object, I don't have such a method. Am I missing something in my inheritance tree? Should I be inheriting StatusClass from something else?


No, You must write the "GetInstanceForCode()" method Yourself. It is just a lookup method to find appropriate instance for given status code.

Gert


Top
 Profile  
 
 Post subject:
PostPosted: Sat Apr 01, 2006 4:11 pm 
Newbie

Joined: Fri Jan 20, 2006 12:47 pm
Posts: 16
Location: Ottawa, On
the reason your foreign key id is not changing is because you are updating the StatusClass object's Status property, not updating the Entity class's reference to a StatusClass object ... there is a big difference.

By updating the Status property with the following

entityObj.Status.Status = StatusCode.Off;

you are not updating the foreign key reference from Entity to StatusClass, you are updating the actual StatusClass object which is associated with the Entity. When you save the Entity object, the changes made to the StatusClass.Status property are cascaded via the many-to-one mapping in the Entity class and that is why the StatusClass which was representing On is updated to represent Off.

To change the cascading behaviour, add cascade="none" to the many-to-one mapping. I do this for all of my reference data mappings to ensure that it can never be changed via a referencing class .... that will fix your reference data being changed problem, but it still will not update the foreign key correctly.

To update the foreign key correctly, you have to either do as gert suggested and lookup the actual object which represents the Off Status (this requires an extra datbase access each lookup, unless you cache the StatusClass objects), or you can add an extra constructor to your StatusClass and simply construct the object you require and assign it to the Entity class's Status property.

E.g.

StatusClass s = new StatusClass(2); // this represents a status of off

Entity.Status = s;

session.SaveOrUpdate(entityObj); // this will now update the actual foreign key in the Entity table which references a StatusClass object

Instead of hard coding the id of a StatusClass which represents Off, you can create some constants which your app uses, as long as the reference objects are not dynamically created or deleted via your application.

e.g.

//this is in some constants class somewhere

public static readonly int STATUS_OFF = 2;

StatusClass s = new StatusClass(ConstantsClass.STATUS_OFF);

.... etc

I had a bit of trouble with this as well when I was first wrapping my head around ORM and how reference data should be handled ... hope this helps


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 03, 2006 10:14 am 
Newbie

Joined: Thu Mar 30, 2006 3:26 pm
Posts: 3
Thanks for your input. This makes great sense. I actually implemented what I thought was just a work-around for incorrect mapping by doing a lookup up front for the StatusClass objects and caching them. The only issue was in ensuring the set was done correctly (to update foreign key rather than foreign table). I still have to think about how to enforce that at design time (possibly through decoration to abstract the details of the mapping). Your recommendation on setting cascade to none will help enforce that at runtime; a good step to take.

There's is much for me to learn and I appreciate your help.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 03, 2006 10:30 am 
Expert
Expert

Joined: Thu Jan 19, 2006 4:29 pm
Posts: 348
noble1 wrote:
I still have to think about how to enforce that at design time (possibly through decoration to abstract the details of the mapping). Your recommendation on setting cascade to none will help enforce that at runtime; a good step to take.


Removing the setter of Status property in StatusClass would make impossible to make changes from code. You would need addtional constructor to be able to create new status types. And the mapping file would need to declare some access strategy for the property. Or, if usin net 2.0, You could make the property setter private or protected.

Gert


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.