7

NHibernateの行のサブタイプをどのように変更しますか?たとえば、CustomerエンティティとTierOneCustomerのサブクラスがある場合、CustomerをTierOneCustomerに変更する必要がありますが、TierOneCustomerは元のCustomerエンティティと同じID(PK)を持つ必要があります。

マッピングは次のようになります。

<class name="Customer" table="SiteCustomer" discriminator-value="C">
  <id name="Id" column="Id" type="Int64">
    <generator class="identity" />
  </id>
  <discriminator column="CustomerType" />
  ... properties snipped ...

  <subclass name="TierOneCustomer" discriminator-value="P">
    ... more properties ...
  </subclass>
</class>

クラス階層モデルごとに1つのテーブルを使用しているので、plain-sqlを使用すると、識別子(CustomerType)のSQL更新の問題であり、タイプに関連する適切な列を設定します。NHibernateで解決策が見つからないので、ポインタをいただければ幸いです。

このユースケースを考慮してモデルが正しいかどうかも考えていますが、そのルートをたどる前に、そもそも上記のように実際に実行できることを確認したいと思います。そうでなければ、私はほぼ確実にモデルを変更することを考えます。

4

4 に答える 4

11

簡単な答えは「はい」です。ネイティブSQLを使用して、特定の行の識別子の値を変更できます。

ただし、NHibernateはこのように機能することを意図しているとは思いません。なぜなら、ディスクリミネーターは一般にJavaレイヤーからは「見えない」ためです。Javaレイヤーでは、その値は永続化されたオブジェクトのクラスに従って最初に設定され、変更されることはありません。

よりクリーンなアプローチを検討することをお勧めします。オブジェクトモデルの観点からは、永続化されたインスタンスのIDを変更せずに、スーパークラスオブジェクトをそのサブクラスタイプの1つに変換しようとしています。ここで、競合が発生します(変換されたオブジェクトは実際には想定されていません)。同じこと)。2つの代替アプローチは次のとおりです。

  • 元のCustomerオブジェクトの情報に基づいてTierOneCustomerの新しいインスタンスを作成してから、元のオブジェクトを削除します。取得に顧客の主キーを使用していた場合は、新しいPKに注意する必要があります。

また

  • オブジェクトタイプ(ディスクリミネーター)を変更する必要がないように、アプローチを変更します。TierOneCustomerとCustomerを区別するためにサブクラスに依存する代わりに、いつでも自由に変更できるプロパティ、つまりCustomer.Tier=1を使用できます。

興味があるかもしれないHibernateフォーラムに関するいくつかの関連する議論はここにあります:

  1. Hibernateのディスクリミネーター列を更新できますか?
  2. クラスごとのテーブルの問題:弁別子とプロパティ
  3. 永続化されたインスタンスをサブクラスに変換する
于 2009-01-25T23:32:22.750 に答える
6

あなたは何か間違ったことをしています。

あなたがやろうとしているのは、オブジェクトのタイプを変更することです。.NET や Java ではできません。それは単に意味がありません。オブジェクトは厳密に 1 つの具象型であり、その具象型は、オブジェクトが作成された時点からオブジェクトが破棄される時点まで (黒魔術にもかかわらず) 変更することはできません。あなたがやろうとしていることを達成するために、あなたがレイアウトしたクラス階層では、層1の顧客オブジェクトに変えたい顧客オブジェクトを破棄し、新しい層1の顧客オブジェクトを作成する必要があります。関連するすべてのプロパティを顧客オブジェクトから第 1 層の顧客オブジェクトにコピーします。これが、オブジェクト指向言語でクラス階層を使用してオブジェクトを処理する方法です。

明らかに、あなたが持っているクラス階層はあなたのために働いていません。一流の顧客になったとしても、実際の顧客を破壊することはありません。したがって、オブジェクトでも行わないでください。代わりに、実装する必要があるシナリオを考慮して、意味のあるクラス階層を考えてください。使用シナリオは次のとおりです。

  • 以前はティア 1 ステータスではなかったお客様が、ティア 1 ステータスになりました。

つまり、このシナリオを正確に捉えることができるクラス階層が必要です。ヒントとして、継承よりも構成を優先する必要があります。つまり、最適に機能するものに応じて、IsTierOneという名前のプロパティ、または という名前のプロパティなどを使用することをお勧めします。DiscountStrategy

NHibernate (および Java 用の Hibernate) の全体的な目的は、データベースを非表示にすることです。オブジェクトをネイティブに操作できるようにするには、データベースを魔法のように舞台裏で使用して、オブジェクトを永続化します。NHibernate を使用すると、データベースをネイティブに操作できますが、それは NHibernate が構築されたシナリオのタイプではありません。

于 2009-01-25T23:56:09.630 に答える
2

これは本当に遅いですが、似たようなことをしようとしている次の人に役立つかもしれません:

ほとんどの場合、識別子を変更すべきではないという他の回答は正しいですが、マップされたプロパティを巧妙に使用して、純粋に NH (ネイティブ SQL なし) の範囲内で行うことができます。FluentNH を使用した要点は次のとおりです。

public enum CustomerType //not sure it's needed
{
   Customer,
   TierOneCustomer
}

public class Customer
{
   //You should be able to use the Type name instead,
   //but I know this enum-based approach works
   public virtual CustomerType Type 
   { 
      get {return CustomerType.Customer;} 
      set {} //small code smell; setter exists, no error, but it doesn't do anything.
   }
   ...
}

public class TierOneCustomer:Customer
{
   public override CustomerType Type {get {return CustomerType.TierOneCustomer;} set{}}
   ...
}

public class CustomerMap:ClassMap<Customer>
{
   public CustomerMap()
   {
      ...
      DiscriminateSubClassesOnColumn<string>("CustomerType");
      DiscriminatorValue(CustomerType.Customer.ToString());
      //here's the magic; make the discriminator updatable
      //"Not.Insert()" is required to prevent the discriminator column 
      //showing up twice in an insert statement
      Map(x => x.Type).Column("CustomerType").Update().Not.Insert();
   }
}

public class TierOneCustomerMap:SubclassMap<TierOneCustomer>
{
   public CustomerMap()
   {
      //same idea, different discriminator value
      ...
      DiscriminatorValue(CustomerType.TierOneCustomer.ToString());
      ...
   }
}

最終的な結果は、識別子の値が挿入用に指定され、検索時にインスタンス化された型を決定するために使用されますが、同じ Id を持つ別のサブタイプのレコードが保存された場合 (あたかもレコードが複製されたか、バインドされていないかのように) です。 UI を新しいタイプに)、その ID をオブジェクト プロパティとして既存のレコードで識別子の値が更新されるため、そのタイプの今後の取得は新しいオブジェクトとして行われます。AFAIK NHibernate は、プロパティが読み取り専用 (したがって、DB への「書き込み専用」) であると判断できないため、プロパティにはセッターが必要です。NHibernate の世界では、DB に何かを書き込んだ場合、なぜそれを返してほしくないのでしょうか?

私は最近、このパターンを使用して、ユーザーが「ツアー」の基本的なタイプを変更できるようにしました。これは実際には、実際の「ツアー」(クライアントのオンサイト機器への単一のデジタル「訪問」) のスケジュールを管理する一連のルールです。すべてが正しく機能することを確認します)。それらはすべて「ツアースケジュール」であり、リスト/キューなどで収集できる必要がありますが、さまざまなタイプのスケジュールには非常に異なるデータと非常に異なる処理が必要であり、OP と同様のデータ構造が必要です。したがって、データ層での影響を最小限に抑えながら、TierOneCustomer を実質的に異なる方法で扱いたいという OP の要望を完全に理解しています。

于 2012-08-24T23:55:56.440 に答える
1

オフラインで (DB アップグレード スクリプトなどで) 実行している場合は、SQL を使用して一貫性を確保してください。

これがアプリの実行中に発生することを計画している場合は、別のオブジェクトに同じポインターアドレスを保持することが間違っているのと同じように、要件が間違っていると思います.

ID を保存し、それを使用して (URL などで) 再度顧客にアクセスする場合は、ビジネス キーとなるトークンを含む新しいフィールドを作成することを検討してください。これは ID ではないため、新しいエンティティ インスタンスを作成してトークンをコピーするのは簡単です (おそらく古いものからトークンを削除する必要があります)。

于 2009-01-25T23:38:48.883 に答える