NHibernate - 将特定属性标记为“脏”

时间:2010-11-19 00:08:05

标签: nhibernate dto transient

我正在开发一个NHibernate项目,并且有关于更新瞬态实体的问题。

基本上工作流程如下:

  1. 创建DTO(投影)并通过电汇发送给客户端。这包含来自实体的一小部分属性。
  2. 客户端发回已更改的DTO
  3. 将DTO属性映射回相应的enitity,以便NH生成并执行UPDATE语句。
  4. 保存实体
  5. 第4点是我遇到问题的地方。目前我可以使用session.Merge()方法实现此更新,但是必须首先在更新之前从db(假设没有2LC)加载实体。因此,将触发select和update语句。

    我想要做的是创建实体的瞬态实例,从DTO映射新值,然后让NH仅使用我更改的属性生成SQL语句。附加选择应该是不必要的,因为我已经拥有实体ID和SET子句所需的值。这可能在NH吗?

    目前使用session.Update(),所有属性都将包含在update语句中,并且由于未包含在DTO中的未初始化属性而引发异常。

    基本上我需要一种方法来指定哪些实体属性是脏的,因此只有这些属性包含在更新中。

    ==编辑==

    例如......

    public class Person
    {
        public virtual int PersonId { get; set; }
        public virtual string Firstname { get; set; }   
        public virtual string Nickname { get; set; }    
        public virtual string Surname { get; set; } 
        public virtual DateTime BirthDate { get; set; }     
    }
    

    和测试用例。

    // Create the transient entity
    Person p = new Person()
    p.id = 1;
    
    using (ISession session = factory.OpenSession())
    {
        session.Update(p);
    
        // Update the entity – now attached to session    
        p.Firstname = “Bob”;
    
        session.Flush();
    }
    

    我希望生成类似于'UPDATE Persons SET Firstname ='Bob'WHERe PersonID = 1'的SQL语句。相反,由于BirthDate未初始化,我得到一个DateTime超出范围异常。它不应该需要BirthDate,因为它不是SQL语句所必需的。也许这是不可能的?

    == / EDIT ==

    提前致谢, 约翰

1 个答案:

答案 0 :(得分:4)

动态更新是您正在寻找的。在映射文件(hbm.xml)中:

<class name="Foo" dynamic-update="true">
   <!-- remainder of your class map -->

请注意这可能导致的潜在问题。假设你有一些域逻辑,说FirstName或Nickname不能为null。 (完全做到这一点。)两个人同时更新Jon“Jonboy”Jonson。一个删除他的FirstName。因为动态更新是真的,所以更新语句只会使Jon无效,而记录现在是“Jonboy”Jonson。另一个同步更新删除了他的昵称。目的是Jon Jonboy。但只有昵称的空值才会被发送到数据库。您现在拥有没有FirstName或Nickname的记录。如果dynamic-update为false,则第二次更新会将其设置为Jon Jonboy。也许这在你的情况下不是问题,但设置dynamic-update =“true”会产生影响,你应该仔细思考这些影响。

更新:感谢您的代码。这有帮助。基本问题是NHibernate没有足够的信息。当你说session.Update(p)时,NHibernate必须将一个断开的实体与当前会话相关联。它有一个非默认的PK。所以NHibernate知道它是一个更新而不是插入。当你说session.Update(p)时,NHibernate将整个实体视为脏并将其发送到数据库。 (如果你使用session.Merge(obj),NHibernate从数据库中选择实体并将obj与它合并。)这不是你真正的意思。您希望将对象与当前会话关联,但将其标记为干净。 API有些不直观。您可以使用session.Lock(obj,LockMode.None),如下所示。

using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
    var p = new Person {PersonId = 1};
    session.Lock(p, LockMode.None); // <-- This is the secret sauce!
    p.Firstname = "Bob";
    // No need to call session.Update(p) since p is already associated with the session.
    tx.Commit();
}

(N.B.vynamic-update =“true”在我的映射中指定。)

这导致以下SQL:

UPDATE Person
SET    Firstname = 'Bob' /* @p0_0 */
WHERE  PersonId = 1 /* @p1_0 */