代码改变世界

NHibernate Issues之1899:ISession.SaveOrUpdateCopy()方法

2009-10-11 23:39 by 李永京, ... 阅读, ... 评论, 收藏, 编辑

本节内容

概览

这个系列是以博客形式整理关于NHibernate的Issues。记录一些零碎的小例子,通过零零碎碎的整理,可以巩固自己的知识和扩展我们的知识面。这些小例子也可以适当的在项目中呈现。

这次看看ISession.SaveOrUpdateCopy()方法。

实例

这个小例子可以分析到这个方法的使用。

1.Domain

定义一Domain,关联一些字典。

public class Parent
{
    public virtual int Id { get; set; }
    public virtual IDictionary<Key, Value> Relations { get; set; }
}

public enum Key
{
    One,
    Two
}

public enum Value
{
    ValOne,
    ValTwo
}

2.Mapping

使用Map映射字典类型,在RelationsTable表中,级联所有和删除孤单节点。

<class name="Parent">
    <id name="Id">
        <generator class="assigned" />
    </id>
    <map name="Relations" table="RelationsTable" cascade="all-delete-orphan">
        <key column="ParentID" />
        <index column="KeyId" type="Key" />
        <element column="Value" type="Value" />
    </map>
</class>

3.Test

在第一个Session中保存这个Domain,在第二个Session中获取这个Domain并关闭销毁这个Session,在第三个Session中保存或者更新复制这个Domain。

using (var session = OpenSession())
{
    var entityDomain = new Parent
    {
        Id = 1,
        Relations = new Dictionary<Key, Value>
                                         {
                                             {Key.One, Value.ValOne},
                                             {Key.Two, Value.ValTwo}
                                         }
    };
    session.Save(entityDomain);
    session.Flush();
}
Parent entity;
using (var session = OpenSession())
{
    entity = session.Get<Parent>(1);
    session.Close();
    session.Dispose();
}
using (var session2 = OpenSession())
{
    entity = (Parent)session2.SaveOrUpdateCopy(entity);
}

4.结果

看看上面测试输出的SQL语句吧,一清二楚:

--插入数据
INSERT INTO Parent (Id) VALUES (@p0);@p0 = 1
INSERT INTO RelationsTable (ParentID, KeyId, Value) VALUES (@p0, @p1, @p2);@p0 = 1, @p1 = 0, @p2 = 0
INSERT INTO RelationsTable (ParentID, KeyId, Value) VALUES (@p0, @p1, @p2);@p0 = 1, @p1 = 1, @p2 = 1
--ISession.Get()方法
SELECT parent0_.Id as Id0_0_ FROM Parent parent0_ WHERE parent0_.Id=@p0;@p0 = 1
--ISession.SaveOrUpdateCopy()方法
SELECT parent0_.Id as Id0_0_, relations1_.ParentID as ParentID2_, relations1_.Value as Value2_,
relations1_.KeyId as KeyId2_ FROM Parent parent0_ left outer join RelationsTable relations1_ on
parent0_.Id=relations1_.ParentID WHERE parent0_.Id=@p0;@p0 = 1

分析

在NHibernate API文档中对ISession.SaveOrUpdateCopy()方法这样的解释:

Name Description
SaveOrUpdateCopy(Object)
Copy the state of the given object onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance. If the given instance is unsaved or does not exist in the database, save it and return it as a newly persistent instance. Otherwise, the given instance does not become associated with the session.
SaveOrUpdateCopy(Object, Object)
Copy the state of the given object onto the persistent object with the given identifier. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance. If there is no database row with the given identifier, save the given instance and return it as a newly persistent instance. Otherwise, the given instance does not become associated with the session.

简单的就是说:将传入的对象的状态也就是属性赋给Session缓存中相同键值的对象上,如果该对象不存在则从数据库加载,传入的对象并不进行持久化。如果数据库不存在这个对象,则进行保存。

所以,当我们调用ISession.SaveOrUpdateCopy()方法时,先把传的对象赋值到新的对象(如果传的参数为空,则抛出“attempt to create merge event with null entity”异常),然后发出一条SQL查询数据库:

  • 数据库里有这个对象并相同,什么都不做。
  • 数据库里有这个对象,其属性改变了,就发送SQL更新数据库
  • 数据库没有这个对象,则保存这个对象到数据库。

我想这样做的目的就是保证对象的原子性,不可能别人已经动过数据库了,你还拿一个脏对象来操作。大家可以修改上面的代码测试一下。

那么ISession.SaveOrUpdateCopy()方法在哪里使用呢?

在Web程序中应该没什么用处,因为我们常常使用session-per-request策略,请求之后Session就关闭了也没什么用了。但是在WPF程序中,经常在几个Session中操作对象,操作不慎,会出现各种各样的异常。比如我们如何把这个对象从一个Session中附加到另外一个Session中持久化使用呢。