Handling a one-to-many relationship with value typ

2019-05-10 22:16发布

问题:

I'm working on migrating an application to NHibernate, and I'm using Fluent NHibernate. I'm running into an issue mapping a collection of value types to an aggregate root.

I have a PhoneNumber value type that has a few properties — Number, NumberType, and Description. There are a number of objects in my domain that have collections of phone numbers.

In the database (SQL 2008), these values are held in different tables. So I might have Customers and CustomerPhoneNumbers as well as Vendors and VendorPhoneNumbers. The phone number tables are identical except for the foreign key that relates them to their parent.

The problem is that I would like to use a simple PhoneNumber value type without having to create CustomerPhoneNumber and VendorPhoneNumber types that have properties that relate them to their parent type, and I don't know how to accomplish that in NHibernate. Is this possible or do I need to change my domain objects to more closely match the underlying database schema?

UPDATE: A little more info
It looks like I'm having trouble even getting the basic map to work for retrieval. Here's a simplified example of what I have:

public class CustomerMap : ClassMap<Customer>
{
    public CustomerMap()
    {
        Table("Customers");

        Id(x => x.Id);
        Map(x => x.Name);
        Component(x => x.Address);
        HasMany(x => x.PhoneNumbers)
            .KeyColumn("CustomerId")
            .Cascade.All()
            .Table("CustomerPhoneNumbers");
    }
}

public class PhoneNumberMap : ClassMap<PhoneNumber>
{
    public PhoneNumberMap()
    {
        Id(x => x.Id);
        Map(x => x.Number, "PhoneNumber");
        Map(x => x.PhoneNumberType);
        Map(x => x.Description);
    }
}

It looks like the SQL isn't being generated properly. When I try to look into a customer's PhoneNumbers list, I get an ADO error that reveals that NHibernate is looking for a table named PhoneNumber.

It seems like it's not picking up the Table("CustomerPhoneNumbers") part and is defaulting to a table named the same thing as the object. Yet I can't specify a Table in the PhoneNumberMap, because it'll be different depending on which aggregate root we're pulling for.

回答1:

You can map the single object PhoneNumber to multiple tables using the entity-name mapping attribute. Sorry, no sample code - I do not use Fluent so I cannot help with that aspect. I could post sample hbm mappings if that would be helpful.

Entity-name basically replaces class name across the board. When you use entity-name you will also have to modify your session to accept a parameter to specify the entity name whenever you work with the objects via the session.

Documentation is here:

http://docs.jboss.org/hibernate/core/3.2/reference/en/html/mapping.html#mapping-entityname

EDIT Added hbm samples.

This maps a single object PhoneNumber to multiple tables

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="DomainModel.PhoneNumber, DomainModel"
    table="PhoneNumberVendors" entity-name="PhoneNumberVendor">
    <id name="_id" access="field" column="PhoneNumberId">
        <generator class="assigned"/>
    </id>
    <property name= "...">
    </class>
<class name="DomainModel.PhoneNumber, DomainModel"
    table="PhoneNumberCustomers" entity-name="PhoneNumberCustomer">
    <id name="_id" access="field" column="PhoneNumberId">
        <generator class="assigned"/>
    </id>
    <property name= "...">
    </class>
</hibernate-mapping>

Then to call for example an update on a PhoneNumber you modify the session syntax to the following so nhibernate knows which table to use:

_session.Update("PhoneNumberCustomer", myCustomerNumber) 

You will have to figure out if Fluent even supports this, if so how to do it. If not you can always use hbm files for the PhoneNumber object.