Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modeling one to zero or one relationships (Z cardinality)

I'm struggling to find the best way to model 1 : 0,1 relationships ("may have one" or "has at most one"). I believe this is called Z cardinality.

For example, suppose I have two classes Widget and WidgetTest. Not all Widgets are tested and the test is destructive so there can be at most one WidgetTest per Widget. Also assume that it's inappropriate to add the WidgetTest fields to Widget.

I would like my public interface to be:

Widget
    WidgetTest { get; set; }

WidgetTest
    Widget { get; }

Model 1: Widget has a WidgetTest property and in the database the Widget table has a uniquely constrained foreign key to WidgetTest. My DBA argues that this would allow a WidgetTest record to exist without a Widget.

WidgetTable
    WidgetTestId (FK, UQ)

Model 2: Widget has a private collection of WidgetTest and enforces the 0,1 relationship by adding or removing a single object from the collection controlled by a public WidgetTest property. The database models this as 1:m with WidgetTest having a uniquely constrained foreign key to Widget. I argue that this means adopting the model to fit the database schema (i.e. more work for me).

WidgetTestTable
    WidgetId (FK, UQ)

Which model is better? Which is easier to achieve with NHibernate? Or is there a third way?

Edit ... Here's what I ended up with:

public class Widget
{
    // This is mapped in NH using a access strategy
    private IList<WidgetTest> _widgetTests = new List<WidgetTest>(1);

    public WidgetTest
    {
        get { return _widgetTests.FirstOrDefault(); }
        set
        {
            _widgetTests.Clear();
            if (value != null)
            {
                _widgetTests.Add(value);
            }
         }
     }
}
like image 209
Jamie Ide Avatar asked Feb 05 '10 16:02

Jamie Ide


People also ask

What is cardinality in relationship model?

The cardinality of a relationship is the number of related rows for each of the two objects in the relationship. The rows are related by the expression of the relationship; this expression usually refers to the primary and foreign keys of the underlying tables.

What is a 1 to 1 relationship database?

In a relational database, a one-to-one relationship exists when one row in a table may be linked with only one row in another table and vice versa. It is important to note that a one-to-one relationship is not a property of the data, but rather of the relationship itself.

What are the different types of cardinality?

In other words, cardinality describes a fundamental relationship between two entities or objects. There are three relationship types or cardinalities: one-to-one, one-to-many, and many-to-many.

What are the different types of cardinal relationships?

The three main cardinal relationships are one-to-one, one-to-many, and many-many. A one-to-one example would be one student associated with one mailing address.


2 Answers

My approach has been to model a one-to-many relationship in the mappings, but to constrain the "many" to a single item. This allows the optional one-to-one, and also guarantees that your WidgetTest instance is persisted when you save the Widget. For example:

public class Widget
{
    /// <summary>
    /// This property is ignored by the NHibernate mappings.
    /// </summary>
    public virtual WidgetTest WidgetTest { get; set; }

    /// <summary>
    /// For easier persistence with NHibernate, this property repackages the
    /// WidgetTest property as a list containing a single item. If an
    /// attempt is made to set this property to a list containing more than
    /// one item, an exception will be thrown. But why bother? Just use the
    /// WidgetTest property.
    /// </summary>
    public virtual IList<WidgetTest> WidgetTests
    {
        get
        {
            IList<WidgetTest> widgetTests = new List<WidgetTest>();
            if (this.WidgetTest != null)
            {
                widgetTests.Add(this.WidgetTest);
            }
            return widgetTests;
        }
        set
        {
            if (value != null && value.Count > 1)
            {
                throw new Exception("The WidgetTests collection may not contain more than one item.");
            }
            else if (value != null && value.Count == 1)
            {
                this.WidgetTest = value[0];
            }
            else
            {
                this.WidgetTest = null;
            }
        }
    }
}
like image 59
nw. Avatar answered Sep 25 '22 20:09

nw.


When you say "assume that it's inappropriate to add the WidgetTest fields to Widget", do you mean in your domain objects or in the database. If you are happy for the fields to be in the same table in the database, how about mapping WidgetTest as a component of Widget? Have the NHibernate mapping file look like:

<class name="Widget" table="Widget">
    ...
    <property name="WidgetProperty"/>
    ...
    <component name="WidgetTest" class="WidgetTest">
        <property name="WidgetTestProperty"/>
    </component>
</class>

Giving the table structure:

WidgetTable
    WidgetProperty
    WidgetTestProperty

Which would still let you have the public interface you've specified, however, WidgetTest would become a value object which you may or may not want.

like image 43
Graham Avatar answered Sep 25 '22 20:09

Graham