【问题标题】:How to tell NHibernate that a trigger updates another table?如何告诉 NHibernate 触发器更新了另一个表?
【发布时间】:2011-07-05 13:41:21
【问题描述】:

我刚刚在使用 NHibernate 时遇到了一个 TooManyRowsAffectedException 并且我已经看到了解决方法,方法是注入一个不同的批处理器,比如这里 TooManyRowsAffectedException with encrypted triggers,或者修改数据库中的触发器以使用 SET NOCOUNT ON(我不能使用这是因为我不想修改数据库——它非常复杂,有一百多个表都相关联,我不想弄乱它,因为其他应用程序使用它)。我不明白为什么会发生这种异常。我所做的只是我有一个 Sample 对象,该对象有几个我检查的值,如果这些值符合给定的标准,我将 Sample.IsDone 行设置为“Y”(在我们的数据库中,所有布尔值都由字符 Y 或 N)。代码很简单:

IQueryable<Sample> samples = session.Query<Sample>().Where(s =­­> s.Value == desiredValue);
foreach (Sample sample in samples)
{
  sample.IsDone = 'Y';
  session.Flush(); // Throws TooManyRowsAffectedException
}
session.Flush(); // Throws TooManyRowsAffectedException

无论我把它放在循环内部还是外部,Flush 调用都会抛出。我做错了什么还是仅与数据库的制作方式有关?我尝试在 Flush() 之前对样本调用 SaveOrUpdate(),但它没有改变任何东西。我知道我可以解决这个异常,但我更愿意了解问题的根源。

注意:在异常中,它告诉我实际行数为 2,预期为 1。为什么它更新 2 行,因为我只更改 1 行?

感谢大家的帮助!

编辑:

我能够找出原因是因为数据库中有一个触发器在更新样本时更新了容器表中的一行(容器包含样本)。有没有办法配置 NHibernate 以便它知道这个触发器并期望更新正确的行数?

【问题讨论】:

    标签: c# nhibernate exception triggers


    【解决方案1】:

    我发现的唯一解决方法显示在下面使用 Fluent NHibernate 的代码 sn-p 中。这很丑陋,因为它将 SQL 硬编码到您的映射中,但它确实有效。 Check 属性设置为 None 以便忽略计数。我不知道您是否可以使用直接的 HBM 文件来完成此操作,但可能有一种方法。如果 NHibernate(或 Fluent NH)中有一个配置选项来设置预期的更新行数或在需要时忽略它,那就太好了。

    public class OrderMap : ClassMap<Order>
    {
        public OrderMap()
        {
            Id(c => c.Id, "order_id").GeneratedBy.Native();
    
            Table("order");
    
            Map(c => c.GroupId, "group_id");
            Map(c => c.Status, "status");
            Map(c => c.LocationNumber, "location");
    
            SqlInsert("insert into order (group_id, status, location) values (?, ?, ?)").Check.None();
            SqlUpdate("update order set group_id = ?, status = ?, location = ? where order_id = ?")).Check.None();
            SqlDelete("delete order where order_id = ?").Check.None();
        }
    }
    

    编辑: 对于那些不了解 Fluent NHibernate 或喜欢手工痛苦地生成 HBM 文件的不幸的人,这里是此示例映射的 HBM 文件:

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
      <class xmlns="urn:nhibernate-mapping-2.2" mutable="true" name="Your.DomainModel.Entities.Order, Your.DomainModel, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="order">
        <id name="Id" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" unsaved-value="0">
          <column name="order_id" />
          <generator class="identity" />
        </id>
        <property name="GroupId" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
          <column name="group_id" />
        </property>
        <property name="Status" type="System.Int16, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
          <column name="status" />
        </property>
        <property name="LocationNumber" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
          <column name="loc_num" />
        </property>
        <sql-insert check="none">insert into order (group_id, status, location) values (?, ?, ?)</sql-insert>
        <sql-update check="none">update order set group_id = ?, status = ?, location = ? where order_id = ?</sql-update>
        <sql-delete check="none">delete order where order_id = ?</sql-delete>
      </class>
    </hibernate-mapping>
    

    【讨论】:

    • 我没有使用 Fluent NHibernate,你知道这适用于 NHibernate 设置的什么地方吗?我不知道应该在哪里写(我没有使用任何 ClassMap,也不知道它是什么,也没有见过 SqlInsert 之类的函数)。
    • 请查看我的答案的编辑。希望您不是手动创建 HBM。但如果是的话,一定要看看 Fluent NHibernate 或 NHibernate 3.0 中新的 fluent 映射功能能为您做什么。
    • 谢谢。是的,我正在手动创建 HBM,但目前这不是问题(我不觉得这特别困难)。
    【解决方案2】:

    使用分析器检查被执行的 sql。 anjlab sql profiler 总是为我解决问题。

    然后检查该表上是否有触发器 - 也许它们会造成一些麻烦。

    编辑

    你应该像这里一样改变你的触发器:http://www.codewrecks.com/blog/index.php/2009/03/25/nhibernate-and-toomanyrowsaffectedexception/

    【讨论】:

    • 感谢您的链接。你是对的,我看到更新样本时有一个触发器更新 Container 的 last_updated_date 列(包含样本)。你知道是否有办法告诉 NHibernate 期待吗?
    • 我认为这就是将要发生的事情,因为我还没有找到任何其他方法让它工作,但由于有 100 多个客户端使用具有 10 多个不同应用程序的数据库,我不不知道修改触发器是否会使某些东西在另一个应用程序中停止工作。
    【解决方案3】:

    除了像 Sixto Saez 在他的回答中显示的那样忽略它之外,您无法告诉 NHibernate 触发器更新了数据库中的其他实体。但是,您可以使用 EventListeners 在代码中执行触发操作。或者,如果更新对事务的其余部分无关紧要,则在触发器开始时设置 SET NOCOUNT ON 并在结束时设置 SET NOCOUNT OFF。

    【讨论】:

      猜你喜欢
      • 2011-07-12
      • 1970-01-01
      • 2021-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-28
      • 1970-01-01
      相关资源
      最近更新 更多