4

我有一个像这样的类结构:

public class Person
{
    public virtual int Id { get; protected internal set; }
    public virtual string Name { get; set; }
}

public class Customer : Person
{
    public virtual string AccountNumber { get; set; }
}

Customer被映射为Person(使用 FluentNHibernate 的SubclassMap<T>)的子类,并且表结构是每个子类的表(单独的Person并且Customer表共享一Id列)。

在我的测试中,我打开一个无状态会话并尝试插入一系列Person实体:

using (var stateless = sessionFactory.OpenStatelessSession())
using (var transaction = stateless.BeginTransaction())
{
    var persons = new[]
    {
        new Person { Name = "Alice" },
        new Person { Name = "Bob" },
        new Customer { Name = "Cathy", AccountNumber = "001" },
        new Customer { Name = "Dave", AccountNumber = "002" }
    };
    foreach (var person in persons)
        stateless.Insert(person);
    transaction.Commit();
}

如果我在ShowSql打开开关的情况下运行它,我可以看到表上没有生成任何 INSERT语句Person(这意味着它们是批处理的),但是INSERT表生成了单独的语句Customer(我从中推断这些语句没有被批处理)。

奇怪的是,我发现如果派生类型(即)Customer有它自己的集合(我们称它们为解决关系没有问题。这种行为似乎完全限于多态实体的派生类。OrdersInsert

这是预期的行为吗?如果是这样,有什么方法可以重写上面的插入代码以确保所有子类表也被批处理?

(注意:我使用的SequenceHiLoGenerator是所有 ID,并且我已经进行了相应的配置AdoNetBatchSize,所以据我所知,这不是HiLo批处理的一般问题。我可以看到当批处理操作发生时该表被命中.)

4

1 回答 1

3

经过几天的挖掘和实验,答案似乎是“是”。有各种分散的博客文章顺便提到 NHibernate 在批处理子类时遇到问题 - 特别是在使用每个子类策略的表时(我一直在使用)。

这不是 table-per-class-hierarchy 策略的问题。不幸的是,改变持久性模型对我来说已经太晚了(另外,从关系数据库设计的角度来看,我真的不喜欢 TPH),所以我不得不考虑其他选项。

对于插入,我能够通过以下方式解决该问题:

  1. 使基类具体化(它曾经是抽象的);
  2. 制作每个要插入的对象的浅拷贝(仅构建基类);
  3. 照常插入副本;
  4. 刷新会话以满足参照完整性约束;
  5. 用于 SqlBulkCopy将派生类插入到它们自己的表中,使用一些基于强制转换的可怕巫术IClassMetadataAbstractEntityPersister获取所有列/表映射;
  6. 像往常一样使用该方法插入由子类拥有的任何映射集合IStatelessSession.Insert(对具有自己的类层次结构的集合重复此过程)。

我想,如果不依赖硬编码的表/列名,更新会复杂得多,而且可能很难处理;如果我必须这样做,我可能会改用存储过程(使用表值参数或临时表)。

生成的代码看起来并不令人愉快,但它带来了显着的性能提升——对于大约 20-30k 的正常大小的批次,至少提升了 25%。

于 2012-05-02T22:52:57.443 回答