如何使用NHibernate插入或更新(或覆盖)记录?

时间:2008-11-28 15:10:49

标签: nhibernate insert-update overwrite upsert

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

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

Id是否重要?

已分配ID

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

据我所知,SaveOrUpdate()会根据Id调用Save()或Update()方法。使用指定的ID,这将不起作用,因为id不是未保存的值。但是,版本或时间戳字段可以用作指示符。实际上,这是不相关的,因为这只反映了内存中的对象是否与数据库中的记录相关联;它不表示数据库中是否存在记录。

生成的标识

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

另一种方法?

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

7 个答案:

答案 0 :(得分:7)

我正在使用

    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);

答案 1 :(得分:6)

通常,NHibernate可以依赖unsaved-value来确定它是应该插入还是创建实体。但是,由于您要分配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

答案 2 :(得分:0)

使用session.SaveOrUpdate(object)方法。

答案 3 :(得分:0)

你可以做到

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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

这对我有用:

实施

    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" });

答案 6 :(得分:-1)

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