Nhibernate 如何持久化对象的子集而不是整个对象?

Nhibernate 如何持久化对象的子集而不是整个对象?,nhibernate,nhibernate-mapping,Nhibernate,Nhibernate Mapping,我正在努力解决一个与NHibernate相关的问题,我需要一些输入 简介: 我有一个遗留数据库,其中关系概念尚未真正应用 在数据库中,我有一个OrderLine表,其中包含订单行的数据 除此之外,该表还包含带有顺序特定信息的所有列。例如,这可能是客户的订单号 例如,如果我有10个订单行,那么我的订单行表中有10行,每行都有所有订单特定数据,例如订单号或客户信息 我不想在我的代码中包含上述结构,因此为订单创建了一个视图,这样我就可以在NHibernate中映射我的订单,而NHibernate有一组

我正在努力解决一个与NHibernate相关的问题,我需要一些输入

简介:

我有一个遗留数据库,其中关系概念尚未真正应用

在数据库中,我有一个
OrderLine
表,其中包含订单行的数据

除此之外,该表还包含带有
顺序
特定信息的所有列。例如,这可能是客户的订单号

例如,如果我有10个订单行,那么我的
订单行
表中有10行,每行都有所有
订单
特定数据,例如订单号或客户信息

我不想在我的代码中包含上述结构,因此为
订单创建了一个视图
,这样我就可以在NHibernate中映射我的
订单
,而NHibernate有一组/包的
订单行
,这更有意义

映射:(简化)


问题:

视图的复杂性使得无法保存到视图中。尝试NHibernates时引发此异常:

NHibernate.Exceptions.GenericADOException:无法插入:XXX-->System.Data.SqlClient.SqlException:视图或函数“查看顺序”不可更新,因为修改会影响多个基表

我的NHibernate映射被构造为一个
Order
对象,它有一组
OrderLine
对象。理想情况下,我希望NHibernate只保留
OrderLine
对象集,而不是整个对象


有没有办法做到这一点?我曾尝试使用不同的锁定模式锁定对象,但对我没有帮助。

如果我确实理解您的问题,解决方案非常简单。我们只需要用
dynamic update=“true”
标记根对象

<class name="Order" table="[view_Orders]" dynamic-update="true">
    ...
</class>
但我们的收藏将需要标准的,甚至是级联映射:

<class name="Order" table="[view_Orders]" dynamic-update="true">
    <bag name="OrderLines" 
         lazy="true" 
         inverse="true" 
         batch-size="25" 
         cascade="all-delete-orphan" >
      ...
     </bag>
     ... // other stuff is update="false"
</class>
就这样。根对象上的级联正在执行我们需要的操作,而update=“false”没有更新它

注:只是有趣的说明-还有类和集合 设置
mutable=“false”
,但在这里不起作用。。。作为 上面提到的解决方案(很遗憾,因为那样会更糟 优雅,但未按预期工作。见:

如果应用程序需要读取但从不修改持久类的实例,则可以使用只读缓存。这是最简单、最有效的策略。在集群中使用它甚至是非常安全的

您可以使用
mutable=“false”
来避免更新和删除,如下所示:

不可变类,mutable=“false”可能不会被应用程序更新或删除。这允许NHibernate进行一些较小的性能优化

要避免插入,可以使用以下语句(使用proyection代替insert命令,不要忘记使用
check=“none”
):

选择1
下面是一个经过测试的示例:

<class name="Order" table="[view_Orders]" mutable="false">
  <id name="OrderId" type="System.Guid">
    <generator class="guid.comb"/> <!-- Change as you need -->
  </id>

  <!-- Other properties -->
  <!-- <property name="GrandTotal"/> -->

  <set name="OrderLines" lazy="true" inverse="true" cascade="all-delete-orphan">
    <key column="OrderId"/>
    <one-to-many class="OrderLine"/>
  </set>

  <sql-insert check="none">SELECT 1</sql-insert>
</class>

<class name="OrderLine" table="OrderLine">
  <id name="OrderLineId" type="System.Guid">
    <generator class="guid.comb"/> <!-- Change as you need -->
  </id>

  <!-- Other properties -->
  <!-- <property name="OrderId"/>
  <property name="GrandTotal"/>/> --> 

</class>

选择1

看来您正在尝试的应该是可行的。异常表示NHibernate正试图对映射到不可更新视图的订单实体执行更新。NHibernate只有在认为实体是脏的、在OrderLines集合之外时才会这样做。您应该检查其他映射属性。也许可以尝试使用NHProf这样的分析器来查看到底发生了什么变化。我不想更新我的订单,因为它映射到不可更新的视图。相反,我只想更新子对象,即订单行包。我一直在使用NHProfiler,上面的异常基本上是通过NHibernate传递的SQL异常。嗨,Radim,非常感谢您的帮助。我已经尝试设置mutable=“false”,但没有成功。我尝试使用动态更新,但仍然会遇到相同的错误。我上面描述的错误与数据插入有关,而不是更新,我相信您的示例针对的是im是否正确?是的,正如我所说的
mutable=“false”
不是这里的解决方案。但将所有视图相关属性标记为
update=“false”
将起作用。只有
将被持久化。。。因为。。。如果你说你已经映射了视图,那么我们就只能谈论更新了。所以,正如您所描述的(如果我读得正确的话),我们需要在更新过程中只保留集合。在这种情况下,甚至可以插入该集合的项。但在父视图中插入全新记录。。。基于您的场景,没有意义。这是我检查过的,正在工作。。或者?我是指你问题的一部分“…理想情况下,我希望NHibernate只保留一组OrderLine对象,而不是整个对象…”因此,如果我们确实有现有记录(由视图表示的记录,映射为父对象),我们将只插入OrderLine。。。上述答案将支持该解决方案。订单已经存在(未由NHibernate更新)订单行中的所有项目都可以完全管理。。。
<class name="Order" table="[view_Orders]" dynamic-update="true">
    <bag name="OrderLines" 
         lazy="true" 
         inverse="true" 
         batch-size="25" 
         cascade="all-delete-orphan" >
      ...
     </bag>
     ... // other stuff is update="false"
</class>
var session = ... // get ISession 
// load root
var root = session.Get<Order>(123);

// if needed change existing line (pretend there is one)
root.OrderLines[0].Amount = 100;

// add new
var newOrder = ... // new order
root.OrderLines.Add(newOrder);

session.Save(root);
session.Flush();
<sql-insert check="none">SELECT 1</sql-insert>
<class name="Order" table="[view_Orders]" mutable="false">
  <id name="OrderId" type="System.Guid">
    <generator class="guid.comb"/> <!-- Change as you need -->
  </id>

  <!-- Other properties -->
  <!-- <property name="GrandTotal"/> -->

  <set name="OrderLines" lazy="true" inverse="true" cascade="all-delete-orphan">
    <key column="OrderId"/>
    <one-to-many class="OrderLine"/>
  </set>

  <sql-insert check="none">SELECT 1</sql-insert>
</class>

<class name="OrderLine" table="OrderLine">
  <id name="OrderLineId" type="System.Guid">
    <generator class="guid.comb"/> <!-- Change as you need -->
  </id>

  <!-- Other properties -->
  <!-- <property name="OrderId"/>
  <property name="GrandTotal"/>/> --> 

</class>