Home

Castle Stronghold

Primary key mapping

Regular ActiveRecord types must have a primary key. We favor single keys instead of composite keys, but both are supported. If you have control over the database schema, try to add a surrogate primary key to tables with composite keys.

Single Primary Key

We consider a single primary key a column used as a row identifier. It can be assigned, auto generated (by the database) or use one of the strategies to generated non duplicate values.

To declare a primary key in your class all you need to do is create a property and use the PrimaryKeyAttribute. This attribute allows you to inform the generation strategy and defaults to native if none is informed (native means the auto generation supported by your database). As example, consider the following table script:


CREATE TABLE Entity (
    [id] [int] IDENTITY (1, 1) NOT NULL
) ON [PRIMARY]

This table would be easily mapped to an ActiveRecord class:


using Castle.ActiveRecord;

[ActiveRecord]
public class Entity : ActiveRecordBase
{
    private int id;
    
    [PrimaryKey(PrimaryKeyType.Native)]
    private int Id
    {
        get { return id; }
        set { id = value; }
    }
}

For this case, the PrimaryKeyType could be omitted as it will default to Native anyway. ActiveRecord will correctly assume that the column name is Id. If the column had a different name, for example EntityId, you could use:


    private int id;
    
    [PrimaryKey(PrimaryKeyType.Native, "EntityId")]
    private int Id
    {
        get { return id; }
        set { id = value; }
    }

You do not need a setter for the primary key, but NHibernate needs to set the value somehow. You can then specify the Access for it, for example:


    private int id;
    
    [PrimaryKey(Access=PropertyAccess.FieldCamelcase)]
    private int Id
    {
        get { return id; }
    }

Using sequences

If your database supports sequences you need to use PrimaryKey.Sequence and inform the sequence name. For example:


    private int id;
    
    [PrimaryKey(PrimaryKeyType.Sequence, SequenceName="entitysequence")]
    private int Id
    {
        get { return id; }
        set { id = value; }
    }

Different strategies

Different strategies can be used to generate primary key values. Please refer to NHibernate documentation for more on them. However, we need to inform that some strategies require more parameters. If you use one of those, you can use the Params property to inform the parameters. For example:


    private String id;
    
    [PrimaryKey(PrimaryKeyType.UuidHex, Params="format=D,seperator=-")]
    public String Id
    {
        get { return id; }
        set { id = value; }
    }

You just need to specify a sequence of name=value separated by commas to Params property.

Please refer to the Reference Manual's Attributes article for further information.

Composite Primary Keys

composite keys, also known as natural keys, consist of a set of columns that define the identifier of a row.

Quick Note

Composite keys are highly discouraged. Use only when you have no other alternative.

To use composite keys with ActiveRecord you need to do two things:

  1. Create a class to hold the properties and fields for the columns that make up the key.
    • mark the class as Serializable
    • Override Equals and GetHashCode
  2. Declare the property on your ActiveRecord type and use the CompositeKeyAttribute.

To show an example, consider the following table script:


CREATE TABLE Users (
    [OrgID] [int] NOT NULL,
    [UserID] [int] NOT NULL,
    [GroupID] [int] NOT NULL,
    [Name] [varchar] (50) NULL,
    [Address] [varchar] (50) NULL,
    [City] [varchar] (50) NULL,
    [State] [varchar] (50) NULL
) ON [PRIMARY]

The following is the definition of the composite key class ProductSupplierKey and next is the ActiveRecord type:


using Castle.ActiveRecord;

[Serializable]
public class UserKey
{
    private int orgID;
    private int userID;
    
    [KeyProperty]
    public int OrgID
    {
        get { return orgID; }
        set { orgID = value; }
    }

    [KeyProperty]
    public int UserID
    {
        get { return userID; }
        set { userID = value; }
    }
    
    public override int GetHashCode()
    {
        return orgID ^ userID;
    }

    public override bool Equals(object obj)
    {
        if (this == obj)
        {
            return true;
        }
        UserKey key = obj as UserKey;
        if (key == null)
        {
            return false;
        }
        if (ident1 != key.orgID || ident2 != key.userID)
        {
            return false;
        }
        return true;
    }
}

[ActiveRecord("Users")]
public class User : ActiveRecordBase
{
    private UserKey key;
    
    [CompositeKey]
    public UserKey Key
    {
        get { return key; }
        set { key = value; }
    }
}
Warning

See the implications below. As was mentioned above, Composite keys are discouraged. However, if you must use them, you will have to address additional complexity in how your model is mapped to the database.

Implications of using composite keys

An assigned identifier (like all CompositeKeys and string PrimaryKeys) cannot be used to determine whether an instance is detached or transient - since its value is assigned by the application, it is never null. Therefore, you must use another strategy, otherwise NHibernate will misbehave around the way it persists the instance to the database.

To ensure that the data is persisted properly, you must choose one of two methods for managing persistence:

  1. Use the VersionAttribute to set the UnsavedValue. Normally, the UnsavedValue is used with the PrimaryKeyAttribute, where the UnsavedValue is checked by NHibernate to determine the state of the instance... if the field or property is equal to the UnsavedValue, then the object has not yet been persisted. However, because the field or property marked with the CompositeKeyAttribute cannot have an UnsavedValue that is understood by NHibernate, another field or property must be used - the one that was marked by the VersionAttribute. This allows the use of the Save method.
  2. Do not use the Save method. Instead, use the Create and Update methods to force NHibernate to perform the persistence.

Relations with composite keys

Because a composite key is by nature multi-field, there are additional requirements when building the relations between objects that include these keys. The largest part of those requirements is that the HasMany, BelongsTo and HasAndBelongsToMany attributes will use different properties to determine the Columns, ColumnKeys and ColumnKeyRefs.

To continue the example used above, we are going to redefine the User class, and add Org and Group classes, and include their relationships.

First, we will start off with some DDL to create the Org and Group tables, as well as the association table for the many-to-many relationship between Users and Groups.


CREATE TABLE Orgs (
    [ID] [int] NOT NULL,
    [Name] [varchar] (50) NULL
) ON [PRIMARY]

CREATE TABLE Groups (
    [ID] [int] NOT NULL,
    [Name] [varchar] (50) NULL
) ON [PRIMARY]

CREATE TABLE UserGroups (
    [OrgID] [int] NOT NULL,
    [UserID] [int] NOT NULL,
    [GroupID] [int] NOT NULL
) ON [PRIMARY]

Next, we will redefine the User class, adding the appropriate markup to maintain the mapping relationships.


using Castle.ActiveRecord;

ActiveRecord("Users")]
public class User : ActiveRecordBase
{
    private UserKey key;
    private ISet groups;
    private Org org;

    public User()
    {
        groups = new HybridSet();
    }

    public User(UserKey userKey) : this()
    {
        key = userKey;
    }

    [CompositeKey]
    public UserKey Key
    {
        get { return key; }
        set { key = value; }
    }

    [HasAndBelongsToMany(typeof(Group),
        Table="UserGroups",
        ColumnRef="GroupID",
        CompositeKeyColumnKeys=new string[]{"OrgID","UserID"},
        Lazy=true,
        Cascade=ManyRelationCascadeEnum.SaveUpdate)
    public ISet Groups
    {
        get { return groups; }
    }

    [BelongsTo("OrgID", Insert=false, Update=false)
    public Org Org
    {
        get { return org; }
        set { org = value; }
    }
}

Note the CompositeKeyColumnKeys array, these are the fields that make up the foreign composite key in the association table.

Another interesting item when dealing with composite keys, is when you want to build a relationship using only a single field of the composite key as the foreign key in a traditional one-to-many or many-to-one relationship. You can see the "one" side of that mapping in the definition of the "Org" property - setting the Insert/Update to false is not optional. This prevents the other side of the relation from attempting to insert or update a portion of the composite key (in this case, the "OrgID" field).

Now, we will define the other two classes, to illustrate the other side of the relationships.


[ActiveRecord("Orgs")
public class Org : ActiveRecordBase
{
    private int id;
    private ISet users;

    public Org()
    {
        users = new HybridSet();
    }

    [PrimaryKey(PrimaryKeyType.Native)]
    public int ID
    {
        get { return id; }
        set { id = value; }
    }

    [HasMany(typeof(User), Lazy=true)
    public ISet Users
    {
        get { return users; }
    }
}

[ActiveRecord("Groups")]
public class Group : ActiveRecordBase
{
    private int id;
    private ISet users;

    public Group()
    {
        users = new HybridSet();
    }

    [PrimaryKey(PrimaryKeyType.Native)]
    public int ID
    {
        get { return id; }
        set { id = value; }
    }

    [HasAndBelongsToMany(typeof(User),
        Table="UserGroups",
        CompositeKeyColumnRefs=new string[]{"OrgID","UserID"},
        ColumnKey="GroupID",
        Lazy=true,
        Inverse=true,
        Cascade=ManyRelationCascadeEnum.SaveUpdate)
    public ISet Users
    {
        get { return users; }
    }
}

On the side of the relationship without the composite key, use an array of column refs, while on the side of the relationship with the composite key, use an array of column keys.

Google
Search WWW Search castleproject.org