NHibernate - 将特定属性标记为“脏”;
我正在开发一个 NHibernate 项目,并且有一个关于更新瞬态实体的问题。
基本上,工作流程如下:
- 创建 DTO(投影)并通过线路发送给客户端。它具有实体属性的一小部分。
- 客户端发送回更改的 DTO
- 将 DTO 属性映射回适当的实体,以便 NH 可以生成并执行 UPDATE 语句。
- 保存实体
第 4 点是我遇到问题的地方。目前我可以使用 session.Merge() 方法实现此更新,但是在更新之前它必须首先从数据库加载实体(假设没有 2LC)。因此,选择语句和更新语句都会被触发。
我想要做的是创建实体的瞬态实例,映射来自 DTO 的新值,然后让 NH 仅使用我已更改的属性生成 SQL 语句。额外的选择应该是不必要的,因为我已经有了实体 ID 和 SET 子句所需的值。这在新罕布什尔州可能吗?
当前使用 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 out of range 异常。它不需要 BirthDate,因为 SQL 语句不需要它。也许这不可能?
== /编辑==
提前致谢, 约翰
I am working on an NHibernate project and have a question regarding updating transient entities.
Basically the workflow is as follows:
- Create a DTO (projection) and send over the wire to client. This has a small subset of properties from the entity.
- Client sends back the changed DTO
- Map the DTO properties back onto the appropriate enitity so an UPDATE statement can be generated and executed by NH.
- Save the entity
Point 4 is where I have the issue. Currently I can achieve this update using the session.Merge() method, however it must first load the entity from the db (assume no 2LC) before updating. So, both a select and an update statement are fired.
What I would like to do is create a transient instance of the entity, map the new values from the DTO, then have NH generate a SQL statement using only the properties I have changed. The additional select should be unnecessary as I already have the entity ID and the values required for the SET clause. Is this possible in NH?
Currently using session.Update(), all properties will be included in the update statement and an exception is raised due to the uninitialized properties that are not part of the DTO.
Essentially I need a way to specify which entity properties are dirty so only these are included in the update.
== EDIT ==
For example...
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; }
}
And the test case.
// 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();
}
I was hoping to generate a SQL statement similar to ‘UPDATE Persons SET Firstname = ‘Bob’ WHERE PersonID = 1’. Instead I get a DateTime out of range exception due to BirthDate not being initialised. It shouldn’t need BirthDate as it is not required for the SQL statement. Maybe this isn’t possible?
== /EDIT ==
Thanks in advance,
John
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
动态更新正是您所寻找的。在映射文件 (hbm.xml) 中:
请注意这可能导致的潜在问题。假设您有一些域逻辑,规定 FirstName 或 Nickname 不得为空。 (完全是编造的。)两个人同时更新乔恩·“Jonboy”·乔森。其中一个删除了他的名字。因为动态更新为 true,所以更新语句只是将 Jon 清空,记录现在为“Jonboy”Jonson。另一个同时更新删除了他的昵称。意图是乔恩·乔恩博伊。但只有昵称的空值才会发送到数据库。您现在有一条没有名字或昵称的记录。如果动态更新为 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) ,如下所示。
(注意,dynamic-update="true" 在我的映射中指定。)
这会产生以下 SQL:
Dynamic-update is what you're looking for. In your mapping file (hbm.xml):
Be aware of the potential problems that this may cause. Let's say you have some domain logic that says either FirstName or Nickname must not be null. (Completely making this up.) Two people update Jon "Jonboy" Jonson at the same time. One removes his FirstName. Because dynamic-update is true, the update statement just nulls out Jon and the record is now "Jonboy" Jonson. The other simultaneous update removes his Nickname. The intent is Jon Jonboy. But only the null-out of the Nickname gets sent to the database. You now have a record with no FirstName or Nickname. If dynamic-update had been false, the second update would have set it to Jon Jonboy. Maybe this isn't an issue in your situation, but setting dynamic-update="true" has consequences and you should think through the implications.
UPDATE: Thanks for the code. That helped. The basic problem is NHibernate not having enough information. When you say session.Update(p), NHibernate has to associated a disconnected entity with the current session. It has a non-default PK. So NHibernate knows that it's an update and not an insert. When you say session.Update(p), NHibernate sees the whole entity as dirty and sends it to the database. (If you use session.Merge(obj), NHibernate selects the entity from the database and merges obj with it.) This is not what you really mean. You want to associate your object with the current session, but mark it as clean. The API is somewhat non-intuitive. You use session.Lock(obj, LockMode.None) as below.
(N.B. dynamic-update="true" is specified in my mapping.)
This results in the following SQL: