1

我正在开发一个 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 语句不需要它。也许这是不可能的?

== /编辑 ==

在此先感谢,约翰

4

1 回答 1

4

动态更新是您正在寻找的。在您的映射文件 (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();
}

(NB dynamic-update="true" 在我的映射中指定。)

这将产生以下 SQL:

UPDATE Person
SET    Firstname = 'Bob' /* @p0_0 */
WHERE  PersonId = 1 /* @p1_0 */
于 2010-11-19T01:18:11.017 回答