【问题标题】:NHibernate - flagging specific properties as 'dirty'NHibernate - 将特定属性标记为“脏”
【发布时间】:2011-05-12 09:09:56
【问题描述】:

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

基本上工作流程如下:

  1. 创建一个 DTO(投影)并通过网络发送给客户端。这具有来自实体的一小部分属性。
  2. 客户端发回更改后的 DTO
  3. 将 DTO 属性映射回适当的实体,以便 NH 生成和执行 UPDATE 语句。
  4. 保存实体

第 4 点是我遇到问题的地方。目前我可以使用 session.Merge() 方法来实现这个更新,但是在更新之前它必须首先从数据库加载实体(假设没有 2LC)。因此,select 和 update 语句都会被触发。

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

当前使用 session.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 ==

提前致谢, 约翰

【问题讨论】:

    标签: nhibernate dto transient


    【解决方案1】:

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

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

    请注意这可能导致的潜在问题。假设您有一些域逻辑表明 FirstName 或 Nickname 不能为空。 (完全是编造的。)两个人同时更新了 Jon “Jonboy” Jonson。一个人删除了他的名字。因为 dynamic-update 为真,更新语句只是将 Jon 清空,记录现在是“Jonboy” Jonson。另一个同步更新删除了他的昵称。意图是 Jon Jonboy。但只有昵称的空值被发送到数据库。您现在有一条没有名字或昵称的记录。如果 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();
    }
    

    (注意,我的映射中指定了 dynamic-update="true"。)

    这会产生以下 SQL:

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

    【讨论】:

    • 使用它也会对性能产生影响,我相信使用静态更新(总体而言)性能更高,因为 nhibernate 在构建 sessionfactory 时准备更新语句的方式。
    • 嗨詹姆斯,感谢您的回复。我为实体设置了 DynamicUpdate(流利的映射)。也许一个例子可以更清楚地说明这个问题。请参阅上面的编辑部分。
    • 我必须同意 API 在这种情况下并不直观;)我已经在我的“真实世界”应用程序中使用 session.Lock() 进行了测试,它返回的正是我想要的 SQL。非常感谢您对这个 James 的帮助。
    猜你喜欢
    • 1970-01-01
    • 2011-05-20
    • 1970-01-01
    • 1970-01-01
    • 2011-04-04
    • 1970-01-01
    • 1970-01-01
    • 2021-09-16
    • 1970-01-01
    相关资源
    最近更新 更多