Friday, February 10, 2012

Mapping-by-Code - composite identifiers

Recently we've talked about surrogate primary keys. Let's move on to different foreign key types supported by NHibernate. We have composite keys available in two different ways, depending on whether we have the key represented in object model as a component (ComponentAsId) or just as several properties in the entity (ComposedId).

ComponentAsId(x => x.Key, m =>
{
m.Property(x => x.KeyPart);
// etc.
});

ComposedId(m =>
{
m.Property(x => x.KeyPart);
// other key parts
});

ComponentAsId is an equivalent of <composite-id> XML element with name and class attributes. ComposedId is an equivalent of <composite-id> without attributes. Both in ComponentAsId and ComposedId we need to specify properties taking part in the key and we do it in a standard way, known i.e. from Component mapping. In both cases a way to specify unsaved-value and mapped attributes is missing.

ComponentAsId is unfortunately broken in NHibernate 3.2 - it ignores the property name pointing to key component. It will be fixed in 3.3, in the meantime we have to use XML mapping here or modify the HbmMapping class directly, like this:

mapping.RootClasses.Single(x => x.Name == typeof(EntityWithComponentAsId).Name).CompositeId.name = "Key";

Fluent NHibernate's equivalents

Composite identifiers are supported well, too, in both variants.

// ComposedId equivalent
CompositeId()
.KeyProperty(x => x.KeyPart)
.KeyReference(x => x.KeyReference)
.UnsavedValue("string?")
.Access.Field();

// ComponentAsId equivalent
CompositeId(x => x.Key)
.KeyProperty(k => k.KeyPart)
.KeyReference(k => k.KeyReference)
.Not.Mapped()
.UnsavedValue("string?")
.Access.Field();

We have KeyProperty as an equivalent of key-property element in XML (mapped with Property in mapping-by-code). We have KeyReference for key-many-to-one (mapped with ManyToOne in mapping-by-code). We have Mapped method as an direct equivalent of mapped attribute from XML as well as UnsavedValue method for unsaved-value attribute (but why it is string-typed?).

There's also one more method in chain - ComponentCompositeIdentifier. It seems to be an alternative syntax for ComponentAsId case, but I couldn't make it produce valid XML mapping and the functionality is already covered, so I'll ignore it.

2 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Really useful explanation, thank you very much, just used to map a fancy lovely table on a legacy db :)

    ReplyDelete