【问题标题】:Why does NHibernate delete then insert composite-elements on select?为什么 NHibernate 删除然后在 select 上插入复合元素?
【发布时间】:2020-03-17 20:46:35
【问题描述】:

有人可以向我解释一下关于 NHibernate 如何处理复合元素的这个小谜团。

我的课程看起来像这样;

public class Blog
{
    public virtual int Id
    {
        get;
        private set;
    }

    public virtual ISet<Comment> Comments
    {
        get;
        set;
    }
}

public class Comment
{
    public virtual string CommentText
    {
        get;
        set;
    }

    public virtual DateTime Date
    {
        get;
        set;
    }
}

和这样的映射;

<class name="Blog" table="blog">
    <id name="Id" column="id" unsaved-value="0">
      <generator class="hilo"/>
    </id>

    <set name="Comments" table="blog_comments">
      <key column="blog_id" />
      <composite-element class="Comment">
        <property name="CommentText" column="comment" not-null="true" />
        <property name="Date" column="date" not-null="true" />
      </composite-element>
    </set>

  </class>

但是当我执行这样的选择时;


using (ITransaction transaction = session.BeginTransaction())
{
    Blog blog = session.CreateCriteria(typeof(Blog))
                  .SetFetchMode("Comments", FetchMode.Eager)
                  .Add(Expression.IdEq(2345))            
                  .UniqueResult();

    transaction.Commit();
}

NHibernate 发出一个带有连接的选择以获取带有帖子的博客,但随后删除所有 cmets,然后插入 cmets!为什么要这样做?如果我不使用事务,那么它将只执行选择,而不是我期望的 DELETE 和 INSERT。我错过了什么?我正在使用 NHibernate 2.0

【问题讨论】:

    标签: nhibernate


    【解决方案1】:

    我认为您需要在 Comment 上覆盖 Equals() 和 GetHashCode()。 NHibernate 没有用于实体相等的 ID,因此您必须定义使评论实体与另一个评论相等的原因。

    可能是错的:)


    编辑

    来自nhibernate.info (8.2)

    注意:如果定义复合元素的 ISet,正确实现 Equals() 和 GetHashCode() 非常重要。

    以及从nhibernate.info (4.3) 实现 Equals / GetHashCode 的示例

    public class Cat
    {    
        ...
        public override bool Equals(object other)
        {
            if (this == other) return true;
    
            Cat cat = other as Cat;
            if (cat == null) return false; // null or not a cat
    
            if (Name != cat.Name) return false;
            if (!Birthday.Equals(cat.Birthday)) return false;
    
            return true;
        }
    
        public override int GetHashCode()
        {
            unchecked
            {
                int result;
                result = Name.GetHashCode();
                result = 29 * result + Birthday.GetHashCode();
                return result;
            }
        }    
    }
    

    【讨论】:

    • 谢谢,我已经在我的完整版本中实现了这些,但是在覆盖 GetHashCode 时肯定做错了,你提供的示例就像一个魅力。再次感谢您。
    【解决方案2】:

    我的问题是,如果您只需要进行选择,为什么要提交?我相信它删除所有 cmets 的原因是,当您在事务上调用 commit 时,博客对象及其关联的 cmets 被缓存在用于创建事务的会话中。当您调用提交时,您将保存会话中的所有对象,从而导致保存回数据库。我不清楚为什么要删除 cmets,但保存对象是正确的行为。

    我也stumbled upon this today:

    NHibernate 正在删除我的整个 收集并重新创建它 更新表格。

    这通常发生在 NHibernate 时 无法确定更改了哪些项目 在集合中。常见原因有:

    • 用新的集合实例完全替换持久集合
    • 向 NHibernate 传递一个手动构造的对象并对其调用 Update。
    • 对持久集合进行序列化/反序列化显然也会导致此问题。
    • 使用 inverse="false" 更新 a - 在这种情况下,NHibernate 无法构造 SQL 来更新单个集合项。

    因此,为了避免这个问题:

    • 将您从 NHibernate 获得的同一个集合实例传回给它(不一定在同一个会话中),
    • 尝试使用其他集合代替 ( 或 ),或
    • 尝试使用 inverse="true" 属性。

    【讨论】:

    • 我认为提交只会更新已更改的实体 - 因为没有人知道它为什么会尝试更新任何东西?我也读了这个页面ayende.com/Blog/archive/2008/12/28/…
    • 我同意你不应该使用隐式事务,我只是不清楚如果没有任何改变,你为什么要提交事务。无论如何,我喜欢这个问题,而且答案非常丰富!
    • -1 因为你应该总是有明确的事务,没有提交你会得到警告。
    猜你喜欢
    • 1970-01-01
    • 2015-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多