【问题标题】:How do I Insert or Update (or overwrite) a record using NHibernate?如何使用 NHibernate 插入或更新(或覆盖)记录?
【发布时间】:2010-09-24 11:15:57
【问题描述】:

无论它是否已经存在,我都需要向数据库写入一行。在使用 NHibernate 之前,这是通过存储过程完成的。该过程将尝试更新,如果没有行被修改,它将回退到插入。这很有效,因为应用程序并不关心记录是否存在。

使用 NHibernate,我发现的解决方案需要加载实体并修改它,或者删除实体以便插入新实体。应用程序必须关心记录是否已经存在。有办法解决吗?

身份证重要吗?

分配的 ID

该对象有一个关键字作为分配的 id,并且是表中的主键。

我了解 SaveOrUpdate() 将根据 Id 适当调用 Save() 或 Update() 方法。使用分配的 id,这将不起作用,因为 id 不是未保存的值。但是,可以将版本或时间戳字段用作指示符。实际上,这无关紧要,因为这仅反映内存中的对象是否与数据库中的记录相关联;它不指示该记录是否存在于数据库中。

生成的 ID

如果分配的 id 确实是问题的原因,我可以使用生成的 id 而不是关键字作为主键。这将避免 NHibernate 插入/更新问题,因为它总是会有效地插入。但是,我仍然需要防止重复的关键字。在关键字列上使用唯一索引,即使主键不同,它仍然会为重复关键字抛出异常。

另一种方法?

也许问题并不在于 NHibernate,而在于它的建模方式。与应用程序的其他领域不同,这更以数据为中心,而不是以对象为中心。很高兴 NHibernate 使它易于读/写并消除了存储过程。但是,不考虑现有值而简单地编写的愿望与对象身份模型的模型不太吻合。有没有更好的方法来解决这个问题?

【问题讨论】:

  • 所以你知道, saveOrUpdate() 实际上确实尝试从 oracle 加载对象。不知道为什么它不适用于您分配的 id 而不是生成的,但只要您在一个会话中完成所有操作,就不会花费您任何额外的时间。
  • 当对象在数据库中不存在时,我得到一个错误,说它无法更新对象。谷歌搜索发现 SaveOrUpdate() 不适用于分配的 ID。
  • 我已经更新了问题以更详细地解释问题并提高了我对 NHibernate 的理解

标签: nhibernate insert-update overwrite upsert


【解决方案1】:

我正在使用

    public IList<T> GetByExample<T>(T exampleInstance)
    {
        return _session.CreateCriteria(typeof(T))
                    .Add(Example.Create(exampleInstance))
                    .List<T>();
    }

    public void InsertOrUpdate<T>(T target)
    {
        ITransaction transaction = _session.BeginTransaction();
        try
        {
            var res=GetByExample<T>(target);
            if( res!=null && res.Count>0 )
                _session.SaveOrUpdate(target);
            else
               _session.Save(target); 
            transaction.Commit();
        }
        catch (Exception)
        {
            transaction.Rollback();
            throw;
        }
        finally
        {
            transaction.Dispose();
        }
    }

但是 FindByExample 方法返回所有对象,而不是具有确切 ID 的对象,您有什么建议?因为我只有对象作为参数,所以我无权访问其特定的 ID 字段,所以我不能使用session.get(Object.class(), id);

【讨论】:

  • “.Add(Example.Create(exampleInstance))”的“示例”是什么?
  • 没关系:它在使用 NHibernate.Criterion;命名空间。
  • 使用NHibernate;使用 NHibernate.Criterion;
【解决方案2】:

通常,NHibernate 可以依赖未保存的值来确定它是否应该插入或创建实体。但是,由于您正在分配 ID,因此 NHibernate 看起来您的实体已经被持久化了。因此,您需要依靠版本控制您的对象来让 NHibernate 知道它是一个新对象。请参阅以下链接了解如何对您的实体进行版本控制:

http://web.archive.org/web/20090831032934/http://devlicio.us/blogs/mike_nichols/archive/2008/07/29/when-flushing-goes-bad-assigned-ids-in-nhibernate.aspx

【讨论】:

  • 这个链接现在好像失效了。它是否存在于其他任何地方?
  • @StephenHolt:找到了!现在,如果我能记得三年前我在做什么,这让我感兴趣...... ;-) 我建议也将那个链接编辑到答案中(或者我可以)。
  • @Chris - 哈哈......我想你一直在等待链接被公开,现在你有了它,你可以继续并完成那个任务:-)我已经完成了修改,尽管它必须经过“同行评审”才能生效。
【解决方案3】:

使用 session.SaveOrUpdate(object) 方法。

【讨论】:

  • SaveOrUpdate() 不能在实体具有分配的 id 时使用。
  • 可以。请参阅我的答案的评论。
  • 如果对象有 Version 或 Timestamp 字段,它可以与分配的 id 一起使用。但这并不能解决问题。
【解决方案4】:

你可以的

Obj j = session.get(Object.class(), id);
if (j != null)
   session.merge(myObj);
else
   session.saveOrUpdate(myObj);

【讨论】:

  • 没有 merge() 方法。这是处于休眠状态但不是 NHibernate 的东西吗?
  • 如果我必须得到该对象,如果它存在,我可以删除它。不过我希望避免这种情况。
  • 如果它存在,为什么要删除它?为什么不直接覆盖它?合并处于休眠状态,我不知道NHibernate。在某种程度上,您必须通过 hibernate API 或手动检查对象是否存在。
  • SaveOrUpdate() 不能在实体具有分配的 id 时使用。
  • 其实可以的。这正是 saveOrUpdate() 的目的。当你有一个分配的 id 时不能使用 save(),没有一个不能使用 update(),而 saveOrUpdate() 绕过了这个问题。
【解决方案5】:

查询关键字 = x 的对象,取 FirstOrDefault。如果为 null,则添加新对象,如果存在,则更新您获得的对象并对其调用 saveOrUpdate。

【讨论】:

    【解决方案6】:

    这对我有用:

    实施

        public void InsertOrUpdate<TEntity, TId>(TEntity entity) where TEntity : IIdentificableNh<TId>
        {
            var anyy = session.Get<TEntity>(entity.Id);
            if (anyy != null)
            {
                session.Evict(anyy); //dispatch all data loaded, to allow updating 'entity' object.
                session.Update(entity);
            } 
            else
            {
                session.Save(entity);
            }
            
            session.Flush();
        }
    

    实体

    public class Caracteristica : IIdentificableNh<int>
    {
        public virtual int Id { get; set; }
    
        public virtual string Descripcion { get; set; }
    }
    

    我必须创建一个允许我访问 Id 属性值的接口 (IIdentificableNh)。

    使用示例:

    session.InsertOrUpdate<Caracteristica, int>(new Caracteristica { Id = 2, Descripcion = "Caracteristica2" });
    

    【讨论】:

      【解决方案7】:

      调用 hibernate.saveOrUpdate() 它将检查对象是否在数据库中,如果是则更新它,如果不是则保存(即插入)它。

      【讨论】:

      • SaveOrUpdate() 实际上并不检查数据库。它使用 Id、Version 或 Timestamp 字段来确定是否应调用 Save() 或 Update()。
      • 非常正确,但最重要的是,您可以让 Hibernate 确定调用这两种方法中的哪一种。从实现的角度来看,这只是语义。
      • 这是很重要的一点。如果您的会话超出范围,那么您很可能会失败......将主键复制为最常见的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-29
      • 2014-11-14
      • 2014-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多