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; }
}

CustomerPerson(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ことがわかります (バッチ処理されていることを意味します)が、テーブルに対して生成されている個々のステートメントがあります (これらのステートメントはバッチ処理されていないと推測されます)。 )。 INSERTPersonINSERTCustomer

奇妙なことに、派生型 (つまりCustomer) に独自のコレクション ( と呼びましょう) があり、OrdersそのInsertコレクション内の各項目を (実際のコレクションではなく) 同じステートレス セッションに直接入れると、それらもバッチ処理されることがわかりました。問題なく関係を解決できます。この動作は、ポリモーフィック エンティティの派生クラスに完全に限定されているようです。

これは予想される動作ですか?もしそうなら、上記の挿入コードを書き直して、すべてのサブクラス テーブルもバッチ処理されるようにする方法はありますか?

(注: 私はSequenceHiLoGeneratorすべての ID にを使用しており、AdoNetBatchSizeそれに応じて を構成しているため、私が知る限り、これはバッチ処理の一般的な問題ではありませんHiLo。バッチ操作が発生しているときにテーブルがヒットしていることがわかります。 .)

4

1 に答える 1

3

数日間掘り下げて実験した結果、答えは「はい」のようです。NHibernate がサブクラスのバッチ処理に問題があることを言及しているさまざまなブログ投稿が散在しています

これは、クラス階層ごとのテーブル戦略の問題ではありません。残念ながら、パーシスタンス モデルを変更するには遅すぎたので (さらに、リレーショナル DB 設計の観点から TPH が本当に嫌いです)、他のオプションを検討する必要がありました。

挿入については、次の方法で問題を回避できました。

  1. 基本クラスを具象化します (以前は抽象的でした)。
  2. 挿入する各オブジェクトの浅いコピーを作成します (基本クラスのみを構築します)。
  3. 通常どおりコピーを挿入します。
  4. 参照整合性の制約を満たすためにセッションをフラッシュします。
  5. SqlBulkCopy派生クラスを独自のテーブルに挿入するために使用 します。すべての列/テーブル マッピングを取得するために to をIClassMetadataキャストすることに基づく恐ろしいブードゥー教を使用します。AbstractEntityPersister
  6. メソッドを通常どおり使用して、サブクラスが所有するマップされたコレクションを挿入しますIStatelessSession.Insert(独自のクラス階層を持つコレクションに対してこのプロセスを繰り返します)。

更新ははるかに複雑で、ハードコーディングされたテーブル/列名に依存しないと処理がおそらく非常に難しいと思います。これを行う必要がある場合は、代わりにストアド プロシージャ (テーブル値パラメーターまたはステージング テーブルを使用) に頼るでしょう。

結果として得られるコードは見栄えがよくありませんが、パフォーマンスが大幅に向上します。約 20 ~ 30k の通常サイズのバッチで少なくとも 25% 向上します。

于 2012-05-02T22:52:57.443 に答える