views:

268

answers:

2

Hi, i've being banging my head against the desk all day with the following Nhibernate problem.

Each bank account has one (and only one) set of rates associated with it. The primary key of the bank account table, BankAccountID is also a foreign key and the primary key in the AccountRate table.

public class BankAccount
{
    public virtual int BankAccountId { get; set; }
    public virtual string AccountName { get; set;}
    public virtual AccountRate AccountRate {get;set;}
}

public class AccountRate
{
    public virtual int BankAccountId { get; set; }
    public virtual decimal Rate1 { get; set; }
    public virtual decimal Rate2 { get; set; }
}

I have the following HBM mappings for BankAccount:

<class name="BankAccount" table="BankAccount">
<id name ="BankAccountId" column="BankAccountId">
  <generator class="foreign">
    <param name="property">
      AccountRate
    </param>
  </generator>
</id>
<property name ="AccountName" column="AccountName" />
<one-to-one name="AccountRate" class="AccountRate" constrained="true" cascade="save-update"/>
</class>

and the following for AccountRate:

<class name="AccountRate" table="AccountRate">
<id name ="BankAccountId" column="BankAccountId">
  <generator class="native" />
</id>
<property name ="Rate1" column="Rate1" />
<property name ="Rate2" column="Rate2" />
</class>

An existing BankAccount object can be read from the database with no problem. However, when a new BankAccount is created , the insert statement fails with;

Cannot insert the value NULL into column 'BankAccountId'

The issue appears to be that the child object , AccountRate is created first. Since it hasn't yet got an identifier from its Parent , the insert fails.

I think i'm correct in saying that if the AccountRate property on BankAccount was a collection i could use the following ?

Inverse=True

in order to force the parent to be inserted first.

Can anyone help me with this? i Really don't want to use a collection , there is only a unidirectional one-to-one relationship between these tables.

Thanks

Paul

A: 

One quick way around this would be to make the BankAccountId nullable. NHibernate should insert a record in the BankAccount table with a null id, then update the id.

In the past I've stumbled on a way to make it do the initial insert with the correct id instead of null. Unfortunately, I can't for the life of me remember what I changed.

David Hogue
A: 

Ok , I think i've fixed the issue. It appears that my problem is a classic one-to-one association with shared primary-key values. The answer was found by a good nights sleep and then referring to page 192-193 of 'Nhibernate in Action' .

First there were a number of errors that needed correction. This required modification of both the classes and the HBM files.

Firstly each class needs to contain a property of the others class type, so i needed to add a BankAccount property to the AccountRate class.

public class BankAccount 
{ 
    public virtual int BankAccountId { get; set; } 
    public virtual string AccountName { get; set;} 
    public virtual AccountRate AccountRate {get;set;} 
} 

public class AccountRate 
{ 
    public virtual int BankAccountId { get; set; } 
    public virtual decimal Rate1 { get; set; } 
    public virtual decimal Rate2 { get; set; } 
    Public virtual BankAccount BankAccount {get;set;}
} 

I'd also made a error in the BankAccount HBM file , i shouldn't have made the generator class foreign. That should have been on the AccountRate class. The constraint also needed to be removed from the one-to-one linkage. The new BankAccount HBM file is as follows.

<class name="BankAccount" table="BankAccount">  
<id name ="BankAccountId" column="BankAccountId">  
  <generator class="native">  
</id>  
<property name ="AccountName" column="AccountName" />  
<one-to-one name="AccountRate" class="AccountRate" cascade="all"/>  
</class>  

Next the AccountRate HBM needs the generator class set to foreign , and a 'one-to-one' tag added to complete the relationship between the classes.

<class name="AccountRate" table="AccountRate">          
<id name ="BankAccountId" column="BankAccountId">          
  <generator class="foreign">
        <param name="property">BankAccount</param>
  </generator>         
</id>          
<property name ="Rate1" column="Rate1" />          
<property name ="Rate2" column="Rate2" /> 
<one-to-one name="BankAccount" class="BankAccount" constrained="true" />       
</class>

Thanks to all those who took the time to look at this problem. I guess it's all part of the curve.

Paul

TalkDotNet