Search code examples
sql-serverxmlnhibernatesubclassnhibernate-mapping

NHibernate xml inheritance subclass could not find column


I'm working with NHibernate for the first time. I have a table per class hierarchy (abstract super class). When I try to save a subclass (student) I get the error message:

[SqlException (0x80131904): Invalid column name 'Firstname' etc...]

My HBM file:

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="xx"
                   namespace="xx.Models">
  <class name="User" table="[User]" lazy="false">
    <id name="UserID" column="UserID">
      <generator class="native" />
    </id>
    <discriminator column="Type" type="String"/>

    <property name="Email" column="Email" />

    <subclass name="Student" discriminator-value="0">
      <property name="Firstname" column="Firstname" />
    </subclass>

    <subclass name="Company" discriminator-value="1">
      <property name="Name" column="Name" />
    </subclass>

  </class>
</hibernate-mapping>

SQL Code:

CREATE TABLE [dbo].[User] (
UserID                  INT             NOT NULL IDENTITY(1,1),
Type                    CHAR(1)         NOT NULL,
Email                   VARCHAR(255)    NOT NULL,
PRIMARY KEY(UserID)
);

CREATE TABLE [dbo].[Student] (
UserID                  INT             NOT NULL,
Firstname               VARCHAR(255)    NOT NULL,
PRIMARY KEY(UserID),
FOREIGN KEY(UserID) REFERENCES [User](UserID)               
);

CREATE TABLE [dbo].[Company] (
UserID                  INT             NOT NULL,
Name                    VARCHAR(255)    NOT NULL,
PRIMARY KEY(UserID),
FOREIGN KEY(UserID) REFERENCES [User](UserID),
);

I am probably missing a configuration setting, the database is MS SQL, can anyone help me out?


Solution

  • UPDATE (related to updated question)

    In a nutshell, we need mapping <joined-subclass> instead of <discriminator>

    The point is, that we use mapping for 8.1.1. Table per class hierarchy while we need

    8.1.2. Table per subclass

    (few cites)

    A table-per-subclass mapping would look like:

    <class name="IPayment" table="PAYMENT">
        <id name="Id" type="Int64" column="PAYMENT_ID">
            <generator class="native"/>
        </id>
        <property name="Amount" column="AMOUNT"/>
        ...
        <joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
            <key column="PAYMENT_ID"/>
            ...
        </joined-subclass>
        <joined-subclass name="CashPayment" table="CASH_PAYMENT">
            <key column="PAYMENT_ID"/>
            ...
        </joined-subclass>
        <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
            <key column="PAYMENT_ID"/>
            ...
        </joined-subclass>
    </class>
    

    Four tables are required. The three subclass tables have primary key associations to the superclass table (so the relational model is actually a one-to-one association).

    Similar to this we need mapping like this:

    <class name="User" table="[User]" lazy="false">
        <id name="UserID" column="UserID">
          <generator class="native" />
        </id>
    
        <joined-subclass name="Sutdent" table="Student">
           <key column="UserID"/>
           <property name="Firstname" column="Firstname" />
           ...
        </joined-subclass>
    
    </class>
    

    ORIGINAL part (related to original question)

    In this case, the error should be really clear: There is missing column 'Firstname'. Because with the table per class hierarchy we simply must have all columns in our table. It is ONE table per complete hierarchy. No other place/table where to persist data...

    8.1.1. Table per class hierarchy

    Exactly one table is required. There is one big limitation of this mapping strategy: columns declared by the subclasses may not have NOT NULL constraints.

    And in case, that you are asking: How can NHibernate update DB schema for me? please check this:

    How to update database table schemas with NHibernate schema generation?

    (small extract, see the above link for all details)

    Configuration cfg ...
    cfg.Configure();
    ...
    var update = new SchemaUpdate(cfg);
    update.Execute(true, false);