我正在开发一个NHibernate项目,并且有关于更新瞬态实体的问题。
基本上工作流程如下:
第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 ==
提前致谢, 约翰
答案 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 */