1

私は NHibernate プロジェクトに取り組んでおり、一時的なエンティティの更新に関して質問があります。

基本的にワークフローは次のとおりです。

  1. DTO (プロジェクション) を作成し、ワイヤ経由でクライアントに送信します。これには、エンティティからのプロパティの小さなサブセットがあります。
  2. クライアントは変更された DTO を返送します
  3. DTO プロパティを適切なエンティティにマップし直して、NH によって UPDATE ステートメントを生成および実行できるようにします。
  4. エンティティを保存する

ポイント4は私が問題を抱えているところです。現在、session.Merge() メソッドを使用してこの更新を行うことができますが、更新する前に最初にデータベースからエンティティをロードする必要があります (2LC がないと仮定します)。そのため、select ステートメントと update ステートメントの両方が起動されます。

私がやりたいのは、エンティティの一時的なインスタンスを作成し、DTO から新しい値をマップしてから、変更したプロパティのみを使用して NH に SQL ステートメントを生成させることです。エンティティ ID と SET 句に必要な値は既にあるので、追加の選択は不要です。これはNHで可能ですか?

現在 session.Update() を使用すると、すべてのプロパティが update ステートメントに含まれ、DTO の一部ではない初期化されていないプロパティが原因で例外が発生します。

基本的に、どのエンティティ プロパティがダーティかを指定して、これらだけが更新に含まれるようにする方法が必要です。

==編集==

例えば...

public class Person
{
    public virtual int PersonId { get; set; }
    public virtual string Firstname { get; set; }   
    public virtual string Nickname { get; set; }    
    public virtual string Surname { get; set; } 
    public virtual DateTime BirthDate { get; set; }     
}

そしてテストケース。

// Create the transient entity
Person p = new Person()
p.id = 1;

using (ISession session = factory.OpenSession())
{
    session.Update(p);

    // Update the entity – now attached to session    
    p.Firstname = “Bob”;

    session.Flush();
}

「UPDATE Persons SET Firstname = 'Bob' WHERE PersonID = 1」のような SQL ステートメントを生成したいと考えていました。代わりに、BirthDate が初期化されていないため、DateTime が範囲外の例外を受け取ります。BirthDate は SQL ステートメントには必要ないため、必要ありません。多分これは不可能ですか?

== /編集 ==

前もって感謝します、ジョン

4

1 に答える 1

4

動的更新はあなたが探しているものです。マッピング ファイル (hbm.xml) で:

<class name="Foo" dynamic-update="true">
   <!-- remainder of your class map -->

これにより発生する可能性がある潜在的な問題に注意してください。FirstName または Nickname が null であってはならないというドメイン ロジックがあるとします。(完全にでっち上げです。) 2 人が同時に Jon "Jonboy" Jonson を更新します。FirstName を削除します。dynamic-update が true であるため、update ステートメントは Jon を無効にするだけで、レコードは "Jonboy" Jonson になります。もう 1 つの同時更新では、彼のニックネームが削除されます。その意図はジョン・ジョンボーイです。ただし、Nickname のヌルアウトのみがデータベースに送信されます。これで、FirstName または Nickname のないレコードが作成されました。dynamic-update が false の場合、2 回目の更新で Jon Jonboy に設定されます。これはあなたの状況では問題にならないかもしれませんが、dynamic-update="true" を設定すると結果が生じるため、その影響についてよく考える必要があります。

更新: コードをありがとう。それは役に立ちました。基本的な問題は、NHibernate が十分な情報を持っていないことです。session.Update(p) と言うと、NHibernate は切断されたエンティティを現在のセッションに関連付ける必要があります。デフォルト以外の PK があります。したがって、NHibernate はそれが更新であって挿入ではないことを認識しています。session.Update(p) と言うと、NHibernate はエンティティ全体をダーティと見なし、それをデータベースに送信します。(session.Merge(obj) を使用する場合、NHibernate はデータベースからエンティティを選択し、obj をそれとマージします。) これは、あなたが本当に言いたいことではありません。オブジェクトを現在のセッションに関連付けたいが、クリーンとしてマークします。API はやや直感的ではありません。以下のように session.Lock(obj, LockMode.None) を使用します。

using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
    var p = new Person {PersonId = 1};
    session.Lock(p, LockMode.None); // <-- This is the secret sauce!
    p.Firstname = "Bob";
    // No need to call session.Update(p) since p is already associated with the session.
    tx.Commit();
}

(NB dynamic-update="true" は私のマッピングで指定されています。)

これにより、次の SQL が生成されます。

UPDATE Person
SET    Firstname = 'Bob' /* @p0_0 */
WHERE  PersonId = 1 /* @p1_0 */
于 2010-11-19T01:18:11.017 に答える