NHibernate cascading save
Asked Answered
M

2

2

This is trying to insert null into Comment.BlogArticleID.

The following GenericADOException appeared: "could not insert: [NHibernate__OneToMany.BO.Comment][SQL: INSERT INTO Comment (Name) VALUES (?); select SCOPE_IDENTITY()]"

The following Inner-Exception appeared: "Cannot insert the value NULL into column 'BlogArticleID', table 'Relationships_Test_OneToMany.dbo.Comment'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated."

I need a unidirectional mapping. The answer provided yet is talking about bi-directional mapping.

Does NHibernate cascade save works with native ID generator?

DB tables:

BlogArticle{ID, Name}, where in case of ID, Identitity=true.

Comment{ID, Name, BlogArticleID}, where in case of ID, Identitity=true.

Comment.hbm.xml

<hibernate-mapping
  xmlns="urn:nhibernate-mapping-2.2"
  assembly="NHibernate__OneToMany.BO"
  namespace="NHibernate__OneToMany.BO"
  default-access="property">

  <class name="Comment" table="Comment">
    <id name="ID">
      <generator class="native" />
    </id>

    <property name="Name" />
  </class>
</hibernate-mapping>

public class Comment
    {
        private int _id;
        public virtual int ID
        {
            get { return _id; }
            set { _id = value; }
        }

        public Comment()
        {
        }

        public Comment(string name)
        {
            this._name = name;
        }

        private string _name;
        public virtual string Name
        {
            get { return _name; }
            set { _name = value; }
        }   
    }

BlogArticle.hbm.xml

<hibernate-mapping 
  xmlns="urn:nhibernate-mapping-2.2"
  namespace="NHibernate__OneToMany.BO"
  assembly="NHibernate__OneToMany.BO"
  default-access="property">
  <class name="BlogArticle"  table="BlogArticle">
    <id name="ID">
      <generator class="native" />
    </id>

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

    <bag name="Comments" cascade="all" >
      <key column="BlogArticleID" />
      <one-to-many class="Comment" />
    </bag>
  </class>
</hibernate-mapping>


public class BlogArticle
    {
        private int _id;
        public virtual int ID
        {
            get { return _id; }
            set { _id = value; }
        }

        private string _name;
        public virtual string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        private IList _depts;
        public virtual IList Comments
        {
            get { return _depts; }
            set { _depts = value; }
        }   
    }

Main

class Program
{
    static void Main(string[] args)
    {
        BlogArticle ba = new BlogArticle();
        ba.Name = "Humanity";
        ba.Comments = new List<Comment>();
        ba.Comments.Add(new Comment("Comm1"));
        ba.Comments.Add(new Comment("Comm2"));
        ba.Comments.Add(new Comment("Comm3"));

        Repository<BlogArticle> rep = new Repository<BlogArticle>();
        rep.Save(ba);
    }
}

Repository

public class Repository<T> : IRepository<T>
    {
        ISession _session;

        public Repository()
        {
            _session = SessionFactoryManager.SessionFactory.OpenSession();
        }

        private void Commit()
        {
            if (_session.Transaction.IsActive)
            {
                _session.Transaction.Commit();
            }
        }

        private void Rollback()
        {
            if (_session.Transaction.IsActive)
            {
                _session.Transaction.Rollback();
                //_session.Clear();
            }
        }

        private void BeginTransaction()
        {
            _session.BeginTransaction();
        }

        public void Save(T obj)
        {
            try
            {
                this.BeginTransaction();

                _session.Save(obj);                

                this.Commit();
            }
            catch (Exception ex)
            {
                this.Rollback();

                throw ex;
            }            
        }

        void IRepository<T>.Save(IList<T> objs)
        {
            try
            {
                this.BeginTransaction();

                for (Int32 I = 0; I < objs.Count; ++I)
                {
                    _session.Save(objs[I]);
                }

                this.Commit();
            }
            catch (Exception ex)
            {
                this.Rollback();

                throw ex;
            }
        }

        void IRepository<T>.Update(T obj)
        {
            try
            {
                this.BeginTransaction();

                _session.Update(obj);

                this.Commit();
            }
            catch (Exception ex)
            {
                this.Rollback();

                throw ex;
            }
        }

        void IRepository<T>.Update(IList<T> objs)
        {
            try
            {
                this.BeginTransaction();

                for (Int32 I = 0; I < objs.Count; ++I)
                {
                    _session.Update(objs[I]);
                }

                this.Commit();
            }
            catch (Exception ex)
            {
                this.Rollback();

                throw ex;
            }
        }

        void IRepository<T>.Delete(T obj)
        {
            try
            {
                this.BeginTransaction();

                _session.Delete(obj);

                this.Commit();
            }
            catch (Exception ex)
            {
                this.Rollback();

                throw ex;
            }  
        }

        void IRepository<T>.Delete(IList<T> objs)
        {
            try
            {
                this.BeginTransaction();

                for (Int32 I = 0; I < objs.Count; ++I)
                {
                    _session.Delete(objs[I]);
                }

                this.Commit();
            }
            catch (Exception ex)
            {
                this.Rollback();

                throw ex;
            }
        }

        T IRepository<T>.Load<T>(object id)
        {
            return _session.Load<T>(id);
        }

        public IList<T> Get<T>(int pageIndex, int pageSize)
        {
            ICriteria criteria = _session.CreateCriteria(typeof(T));
            criteria.SetFirstResult(pageIndex * pageSize);
            if (pageSize > 0)
            {
                criteria.SetMaxResults(pageSize);
            }
            return criteria.List<T>();
        }

        public T Get<T>(object id)
        {
            return _session.Get<T>(id);
        }

        public IList<T> Get<T>()
        {
            return Get<T>(0, 0);
        }

        public IList<T> Get<T>(string propertyName, bool Ascending)
        {
            Order cr1 = new Order(propertyName, Ascending);

            IList<T> objsResult = _session.CreateCriteria(typeof(T)).AddOrder(cr1).List<T>();

            return objsResult;
        }

        public IList<T> Find<T>(IList<string> strs)
        {
            System.Collections.Generic.IList<NHibernate.Criterion.ICriterion> objs = new System.Collections.Generic.List<ICriterion>();
            foreach (string s in strs)
            {
                NHibernate.Criterion.ICriterion cr1 = NHibernate.Criterion.Expression.Sql(s);
                objs.Add(cr1);
            }
            ICriteria criteria = _session.CreateCriteria(typeof(T));
            foreach (ICriterion rest in objs)
                _session.CreateCriteria(typeof(T)).Add(rest);

            criteria.SetFirstResult(0);
            return criteria.List<T>();
        }

        public void Detach(T item)
        {
            _session.Evict(item);
        }        
    }
Mccully answered 6/2, 2010 at 16:44 Comment(3)
Check out #147104Turquoise
maybe <key column="BlogArticleID" not-null="true" />?Enwind
Did you try the changes in the link posted by Isaac Cambron? Also, with which NH version are you?Flush
A
9

The key column of the associated class (Comment.BlogArticleID) must be nullable in the database. NHibernate will insert rows leaving this column NULL and then perform an update to set the key.

Adding not-null="true" to the key element would not work, as this attribute is only used by the schema export tool.

Note that the failing insert includes a select for the generated identity for the new child row.

Ap answered 6/2, 2010 at 16:44 Comment(1)
Actually, it can´t be "not-null" in the mapping, not in the DataBase. I´m running Nhibernate 3.3 and my bag is a nullable and My relation Many to One is a nullable too, but the database is set to don´t accept null values and this works when I save cascadeBricklaying
F
1

you haven't mapped the Article to the Comment:

<hibernate-mapping
  xmlns="urn:nhibernate-mapping-2.2"
  assembly="NHibernate__OneToMany.BO"
  namespace="NHibernate__OneToMany.BO"
  default-access="property">

  <class name="Comment" table="Comment">
    <id name="ID">
      <generator class="native" />
    </id>

    <property name="Name" />
    <many-to-one name="BlogArticle" column="BlogArticleID" />   <----------
  </class>
</hibernate-mapping>

public class Comment
    {
        private int _id;
        public virtual int ID
        {
            get { return _id; }
            set { _id = value; }
        }

        public Comment() { }

        public Comment(int id, string name, BlogArticle article) <------------
        {
            this._id = id;
            this._name = name;
            this._blogArticle = article; <------------
        }

        private string _name;
        public virtual string Name
        {
            get { return _name; }
            set { _name = value; }
        }   

        private BlogArticle _blogArticle;       <------------
        public virtual BlogArticle Name       <------------
        {
            get { return _blogArticle; }       <------------
            set { _blogArticle= value; }       <------------
        }   

    }
Flush answered 13/2, 2010 at 13:25 Comment(7)
I need unidirectional mapping. Your solution is using Bidirectional mapping. Bidirectional is easier.Obscenity
well you didn't say so.... can you post the code that you use to make the insert?Flush
Please see the update. I think, there is nothing much to see in my insert code.Obscenity
And also, can you say something about the exception, "Unexpected row count: 0; expected: 1". It is occurring periodically.Obscenity
why are you assigning an Id to the Comment(s) generated when you have set <generator class="native" /> ? this might trigger erroneous behaviour (and maybe the exception you mention)Flush
I have made correction by not assigning ID to Comments and the following Inner-Exception appeared: "Cannot insert the value NULL into column 'BlogArticleID', table 'Relationships_Test_OneToMany.dbo.Comment'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated."Obscenity
NHibernate's steep learning curve is making me more and more frustrated.Obscenity

© 2022 - 2024 — McMap. All rights reserved.