Non-Null Foreign Key is mapped as nullable in NHibernate

Posts   
 
    
fholznag
User
Posts: 4
Joined: 13-Mar-2012
# Posted on: 13-Mar-2012 23:59:28   

[3.1 Final + 3.5 Beta]

Hi everyone, we have a simple many-to-one relationship in which one or more addresses are related to a single addressee (sorry for the naming). The Addressee entity contains a unique id of type Guid. The Address entity contains a field named AddresseeId of type Guid which is defined as not optional. The database column mapping for the AddresseeId field is defined as not nullable. Without adding a relation between these two entities, LLBLGen creates the NHibernate mapping for the Address entity as expected:


    <class name="Address" table="[dbo].[Address]" optimistic-lock="version" >
        <id name="Id" column="[Id]" access="field.camelcase-underscore" >
            <generator class="guid.comb"/>
        </id>
        <property name="AddresseeId" column="[AddresseeId]" access="field.camelcase-underscore" not-null="true"/>
    </class>

As soon as we add the many-to-one relation between Address and Addressee in LLBLGen, the NHibernate mapping file changes like that:


    <class name="Address" table="[dbo].[Address]" optimistic-lock="version" >
        <id name="Id" column="[Id]" access="field.camelcase-underscore" >
            <generator class="guid.comb"/>
        </id>
        <many-to-one name="Addressee" access="field.camelcase-underscore" fetch="select" cascade="none">
            <column name="[AddresseeId]"/>
        </many-to-one>
    </class>

The not-null="true" attribute has vanished. If I would have to map this manually, I would write


<many-to-one name="Addressee" access="field.camelcase-underscore" fetch="select" cascade="none" not-null="true">

When generating Fluent NHibernate mappings, the not-null attribute is missing, too.

Is this by design?

Thx for your help!

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 14-Mar-2012 11:16:41   

We'll check it out.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 14-Mar-2012 11:27:25   

Isn't a property different from a navigator? Looking at the v3.2 docs: http://nhforge.org/doc/nh/en/index.html#mapping-declaration-manytoone

it shows there's no not-null attribute. It might be possible to put it there according to the XSD, not sure (have to look it up) but not-null on a navigator is ambiguous: if 'Order' doesn't have a 'Customer' assigned, because not-null is true, and it results in an error, it means an order always has to have a Customer loaded in memory. But as NHibernate doesn't have FK fields, it's not possible to set them without assigning a 'Customer', so if the FK fields are not nullable, you have to assign a customer to successfully save an order.

I recon you get a NULL error when saving an address if it doesn't have an addressee assigned to it, and if you add the not-null attribute, you'll get an NHibernate error?

Frans Bouma | Lead developer LLBLGen Pro
fholznag
User
Posts: 4
Joined: 13-Mar-2012
# Posted on: 14-Mar-2012 12:29:19   

Otis wrote:

Isn't a property different from a navigator? Looking at the v3.2 docs: http://nhforge.org/doc/nh/en/index.html#mapping-declaration-manytoone

it shows there's no not-null attribute. It might be possible to put it there according to the XSD, not sure (have to look it up) but not-null on a navigator is ambiguous: if 'Order' doesn't have a 'Customer' assigned, because not-null is true, and it results in an error, it means an order always has to have a Customer loaded in memory. But as NHibernate doesn't have FK fields, it's not possible to set them without assigning a 'Customer', so if the FK fields are not nullable, you have to assign a customer to successfully save an order.

I recon you get a NULL error when saving an address if it doesn't have an addressee assigned to it, and if you add the not-null attribute, you'll get an NHibernate error?

Thank you for your prompt response!

The not-null attribute indeed isn't mentioned in the NHibernate docs. But it's defined in the 2.2-XSD. Fluent also supports setting it on "References".

After manually adding the not-null attribute to the mapping, the NHibernate schema exporter creates a not-null constraint on the AddresseeId column of the Address table in the database and we get a NHibernate.PropertyValueException when trying to save an Address without an Addressee assigned. This is the behaviour we were trying to achieve in this case. There may be other cases like in your Order example where the Order may exist without a customer.

Without the not-null attribute in the NHibernate mapping, our application is able to save an Address to the database without assigning an Addressee. This renders the Address useless (in our case) because it's orphaned and will be deleted automatically if the cascade definition of the related Address collection in Addressee is "all-delete-orphan".

A little bit off topic: The original Hibernate (without N) docs describe the not-null attribute in many-to-one mappings by saying "enables the DDL generation of a nullability constraint for the foreign key columns ". Maybe the NHibernate (with N) people have simply forgotten to document it? simple_smile

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 14-Mar-2012 15:58:23   

fholznag wrote:

Otis wrote:

Isn't a property different from a navigator? Looking at the v3.2 docs: http://nhforge.org/doc/nh/en/index.html#mapping-declaration-manytoone

it shows there's no not-null attribute. It might be possible to put it there according to the XSD, not sure (have to look it up) but not-null on a navigator is ambiguous: if 'Order' doesn't have a 'Customer' assigned, because not-null is true, and it results in an error, it means an order always has to have a Customer loaded in memory. But as NHibernate doesn't have FK fields, it's not possible to set them without assigning a 'Customer', so if the FK fields are not nullable, you have to assign a customer to successfully save an order.

I recon you get a NULL error when saving an address if it doesn't have an addressee assigned to it, and if you add the not-null attribute, you'll get an NHibernate error?

Thank you for your prompt response!

The not-null attribute indeed isn't mentioned in the NHibernate docs. But it's defined in the 2.2-XSD. Fluent also supports setting it on "References".

After manually adding the not-null attribute to the mapping, the NHibernate schema exporter creates a not-null constraint on the AddresseeId column of the Address table in the database and we get a NHibernate.PropertyValueException when trying to save an Address without an Addressee assigned. This is the behaviour we were trying to achieve in this case. There may be other cases like in your Order example where the Order may exist without a customer.

Ok, though we didn't add all attributes for the nhibernate schema exporter to function, as it's limited anyway and the designer (which you already work with) has a better DDL SQL export function. Though you need this nhibernate feature?

Without the not-null attribute in the NHibernate mapping, our application is able to save an Address to the database without assigning an Addressee. This renders the Address useless (in our case) because it's orphaned and will be deleted automatically if the cascade definition of the related Address collection in Addressee is "all-delete-orphan".

Even if the FK field in Address is not null? That sounds weird to me (but could be the case)

A little bit off topic: The original Hibernate (without N) docs describe the not-null attribute in many-to-one mappings by saying "enables the DDL generation of a nullability constraint for the foreign key columns ". Maybe the NHibernate (with N) people have simply forgotten to document it? simple_smile

I think they indeed didn't document it (as with many things, unfortunately).

If it's only used for FK constraint generation, and you can use our DDL SQL export (from the designer), please use that. However if NHibernate inserts a row in Address even though the FK field is not null but no addressee is set (so it inserts a default value) it is indeed required and we'll add it (also if you need the nhibernate schema exporter).

The reason you likely would want to use the designer's ddl sql export function (and ship the script instead of using NHibernate's feature) is that for some types there's simply not enough information in the nhibernate mapping files to be able to create proper DDL SQL, also for schemas etc. it's less ideal, as we don't generate the schema/catalog names separately in the mapping files.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 15-Mar-2012 10:50:11   

Additionally, keep in mind that the generated nhibernate mappings don't contain information for DDL SQL generation like specific db types etc. so only the data needed for NHibernate to function.

Could you please answer the question above in my previous post ? thanks. simple_smile

Frans Bouma | Lead developer LLBLGen Pro
fholznag
User
Posts: 4
Joined: 13-Mar-2012
# Posted on: 15-Mar-2012 11:12:08   

Otis wrote:

Could you please answer the question above in my previous post ? thanks. simple_smile

Sorry. Will give you an answer as soon as possible. Need a little more time to think about it and consider some alternatives.

fholznag
User
Posts: 4
Joined: 13-Mar-2012
# Posted on: 15-Mar-2012 19:04:33   

Well. I must admit that (in my "normal" life) I'm a Java programmer and have been using Hibernate for quite a few years. So, when this Dotnet project came up, it seemed to be a natural decision to use NHibernate for the O/R layer. I came across LLBLGen, because I simply needed a tool to generate classes from my Hibernate mappings. That was then...

Meanwhile I found out, that NHibernate is by far not as mature as Hibernate for Java is. And LLBLGen is not a simple code generator. wink

Back to the problem:

However if NHibernate inserts a row in Address even though the FK field is not null but no addressee is set (so it inserts a default value) it is indeed required and we'll add it (also if you need the nhibernate schema exporter).

NHibernate doesn't create any default value for the FK field. The field contents is either set by ourselves by assigning an Addressee to an Address or it's null.

If it's only used for FK constraint generation, and you can use our DDL SQL export (from the designer), please use that.

The NHibernate schema export and especially the update tool is really bad. I used it, because I was not aware of LLBLGen's DDL capabilities. The DDL script that LLBLGen creates, contains the desired not-null-constraint on the FK field of the Address table. That's good and we'll definitely use the DDL from LLBLGen in the future.

There is only one little flaw which results from not generating the not-null attribute in the NHibernate mapping: It's the location of the error handling in case I try to save an Address without Adressee. If the not-null attribute would be present in the NHibernate mapping, the NHibernate framework would throw an exception before even calling the database. It says "not-null property references a null or transient value" But I can live with that, because the error will hopefully occur only in extremely rare situations.

So you don't have to add code for the not-null attribute of foreign keys in LLBLGen's NHibernate mapping generator (event though it would look more logical to me wink ).

Thanks again for your help. And thanks for showing me another capabilty of LLBLGen.