3

I'm trying to use NHibernate. I have these following tables:

    CREATE TABLE person(person_id INTEGER IDENTITY(1,1) NOT NULL,  
    person_first_name VARCHAR(55) NOT NULL,  
    person_last_name VARCHAR(55) NULL,   
    person_contacted_number INTEGER NOT NULL, 
    person_date_last_contacted DATETIME NOT NULL,
    person_date_added DATETIME NOT NULL,  
    CONSTRAINT PK_person PRIMARY KEY (person_id));

    CREATE TABLE person_order(order_id INTEGER IDENTITY(1,1) NOT NULL,  
    order_person_id INT NOT NULL,  
    CONSTRAINT PK_person PRIMARY KEY (order_id)),
    CONSTRAINT FK_person_order_person FOREIGN KEY(order_person_id)
    REFERENCES person (person_id);

Classes:

    public class Person
    {
        public virtual int person_id { get; set; }
        public virtual String person_first_name { get; set; }
        public virtual String person_last_name { get; set; }
        public virtual int person_contacted_number { get; set; }
        public virtual DateTime person_date_last_contacted { get; set; }
        public virtual DateTime person_date_added { get; set; }
        public virtual ISet<PersonOrder> person_orders { get; set; }
    }
    public class PersonOrder
    {
        public virtual int order_id { get; set; }
        public virtual int order_person_id { get; set; }
        public virtual Person Person { get; set; }
    }

hbm.xml:

  <class name="Person" table="`person`">
        <id name="person_id">
          <generator class="native"/>
        </id>
        <property name="person_first_name" />
        <property name="person_last_name" />
        <property name="person_contacted_number" />
        <property name="person_date_last_contacted" />
        <property name="person_date_added" type="LocalDateTime" />
        <set name="person_orders" table="`person_order`" cascade="all-delete-orphan" inverse="true">
          <key column="order_person_id"/>
          <one-to-many class="PersonOrder"/>
        </set>
  </class>

  <class name="PersonOrder" table="`person_order`">
     <id name="order_id">
      <generator class="native"/>
    </id>
    <many-to-one name="Person" column="order_person_id" cascade="save-update" />
  </class>

I want to save Person to database (cascade):

var newPerson = CreatePerson(); // return new Person
newPerson.person_orders = new HashedSet<PersonOrder> {new PersonOrder()};
session.Save(newPerson);

And have a error could not insert:

[TestConsoleApplication.PersonOrder][SQL: INSERT INTO [person_order] (order_person_id) VALUES (?); select SCOPE_IDENTITY()]

I think it's not correct one-to-many mapping for class. How to resolve this?

1 Answer 1

1

I would say, that the most suspected issue could be missing parent/Person setting on the Order itself.

Once we are not using cascading, NHibernate must know all the settings, independently. Other words, this is not enough *(simplified example, expecting IList instead of IEnumerable for person_orders)*:

var newPerson = CreatePerson(); // return new Person
newPerson.person_orders = new List<PersonOrder>();
// WRONG assignment
newPerson.person_orders.Add(new PersonOrder()); // first order
newPerson.person_orders.Add(new PersonOrder()); // second order

In this case, our new PersonOrder does not know about the Person, the reference. So we have to be sure, that it is assigned both ways (good practice anyhow):

// improve it
var order1 = new PersonOrder
{
    Person = newPerson,
};
var order2 = new PersonOrder
{
    Person = newPerson,
};
newPerson.person_orders.Add(order1); // first order
newPerson.person_orders.Add(order2); // second order

Once this is assured, then after session.Save(newPerson) and session save all orders ... the correct PersonId will be sent.

NOTE: Try to think about using the cascades, and marking the <set> with inverse="true" attribute. Honestly cannot remember scenario where not used this approach

EDIT: a bit different mapping I would suggest to change the mapping types. Instead of iesi ISet (which is really good if we need uniqueness) and the <set>, into standard IList<> and the bag mapping. I.e.:

Hbm like this :

<bag name="person_orders" table="`person_order`" 
     cascade="all-delete-orphan" inverse="true">
      <key column="order_person_id"/>
      <one-to-many class="PersonOrder"/>
</bag>

the C# like this

public virtual IList person_orders { get; set; }

And the insertion code like this:

var newPerson = CreatePerson(); // return new Person
var order = new PersonOrder
{
    Person = newPerson,
};
newPerson.person_orders = new List<PersonOrder> {order};
session.Save(newPerson);

This will work (I am almost sure, because I just reproduced as similar as possible)

Sign up to request clarification or add additional context in comments.

10 Comments

I do some changes: In Person public virtual ISet<PersonOrder> person_orders { get; set; } In hbm.xml <set name="person_orders" table="person_order" cascade="all-delete-orphan" inverse="true"> Use: var newPerson = CreatePerson(); newPerson.person_orders.Add(new PersonOrder()); session.Save(newPerson); And still have same error.
I see, Very good reasonable change! ;) But the point we have to do is to map the Person reference... So assure that you are assigning this "parent" to the child and vice versa. Please, could you update the question with the latest mapping? I will take a look (a bit later). I am sure we will find it! ;) we will manage to store it. I am doing this routine for ages ;) and working
How I understand, I always must do this: var order2 = new PersonOrder { Person = newPerson, }; newPerson.person_orders.Add(order1); Right?
Exactly. It is the best practice. This way we teach NHibernate about the relations, which are not "usual" in C# world. The fact, that we can go from both ends (from child to parent and back) is the SQL style, relational DB style... but it is a must for ORM ;)
Just a second! In case, that you have changed the mapping (cascacade, inverse) which is simply the right way, THEN: do not iterate children. do not call save for each of them. Form that moment, the cascade and NHibernate will do that magic for us. Check this: nhforge.org/doc/nh/en/index.html#manipulatingdata-flushing
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.