2009-04-22 18 views
14

NHibernateでは、インスタンスを取得し、取得したエンティティを表すレコードを排他ロックする必要があります。あなたが見ることができるようにNHibernate:排他ロック

With.Transaction (session, IsolationLevel.Serializable, delegate 
{ 
    ICriteria crit = session.CreateCriteria (typeof (TarificationProfile)); 

    crit.SetLockMode (LockMode.Upgrade); 

    crit.Add (Expression.Eq ("Id", tarificationProfileId)); 

    TarificationProfile profile = crit.UniqueResult<TarificationProfile>(); 

    nextNumber = profile.AttestCounter; 

    profile.AttestCounter++; 

    session.SaveOrUpdate (profile); 
}); 

は、私が「アップグレード」するには、この基準のLOCKMODEを設定します。

は今のところ、私はこのコードを持っています。 これはupdlockrowlockロックヒントを使用するSQL Server用のSQL文を発行します。

しかし、私は本当の排他ロックを使用することができるようにしたいです。つまり、私がロックを解除するまで、他の人がこの非常に同じレコードを読むことを防ぐことができます。 つまり、updlockではなく、xlockのロックヒントを使用したいと考えています。

私は(または場合でも)、私はそれを達成することができますかわからない....たぶん誰かが私にこの:)それは本当に必要な場合

、私はのSQLQuery機能を使用することができますについていくつかのヒントを与えることができますNHibernateの独自のSQLクエリを書くことができますが、できるだけ避けたいと思います。

+0

あなたが排他ロックを達成したいのはなぜ?おそらく他の解決策があります。 –

+0

なぜですか?それは必要なので...私のエンティティには使用すべきカウンタが含まれており、このカウンタが正しく使用されていることを絶対に確かめる必要があります... –

+0

読み込み時にロックしないでください。 –

答えて

7

HQL DMLクエリは、ロックを必要とせずに更新を実行します。

これはNHibernate 2.1で利用可能ですが、まだリファレンスドキュメントにはありません。 Java hibernate documentationはNHibernateの実装に非常に近いです。

ReadCommitted Isolationを使用していると仮定すると、トランザクション内で値を安全に読み取ることができます。

With.Transaction (session, IsolationLevel.Serializable, delegate 
{ 
    session.CreateQuery("update TarificationProfile t set t.AttestCounter = 1 + t.AttestCounter where t.id=:id") 
     .SetInt32("id", tarificationProfileId) 
     .ExecuteUpdate(); 

    nextNumber = session.CreateQuery("select AttestCounter from TarificationProfile where Id=:id") 
     .SetInt32("id", id) 
     .UniqueResult<int>(); 
} 

あなたのテーブル名や列名によって、生成されたSQLは次のようになります。

update TarificationProfile 
set AttestCounter = 1 + AttestCounter 
where Id = 1 /* @p0 */ 

select tarificati0_.AttestCounter as col_0_0_ 
from TarificationProfile tarificati0_ 
where tarificati0_.Id = 1 /* @p0 */ 
+0

ニース、次のAttestCounterも知っておく必要があります。したがって、新しい値を取得する必要があります。 –

+0

@Frederik新しい値を取得するための例を更新しました –

+0

正しいAttestCounterを取得してもよろしいですか? ここで起こるレースの条件はありませんか、シリアライズ可能な分離レベルで解決されていますか? –

0

トランザクション中にデータベースから読み取った値が変更されないようにする場合は、分離レベル "反復可能読み取り"を使用できます。しかし、すべての重要なトランザクションでこれを行う必要があります。または、アップグレードロックを使用して重要な読み取りトランザクションでロックします。

+0

thxです。もう少し詳しいことを教えてください。 私は1つのトランザクションを使用します。ここでは、エンティティ(今すぐupdlockを使用)を読み取り、同じトランザクションで更新されたエンティティを保存します。 –

+0

したがって、この取引中にエンティティは変更できません。他のトランザクションは値を読み取ることができ、トランザクション中に変更を確認できます。彼らがこれを気に入らなければ、彼らは自分のロックを作らなければならない。または、「繰り返し読み取り」を使用します。 –

1

IsolationLevelをSerializableで実行し、すべての書き込みがSerializableのIsolationLevelで行われている場合は、なぜ自分自身のデータベース行をロックする必要があるのか​​わかりません。

だから、シリアル化を安全にデータを保持し、今はまだ可能死者のロックの問題を持っている....

デッドロックはちょうど[、トランザクションを開始し、読み取り、更新、保存]入れて、共通していない場合あなたがデッドロックを取得したときに再試行ループで十分に良いかもしれません。

それ以外の場合は、変更前の行を読み取っている別のトランザクションを停止するために、直接生成された単純な「select for update」文を使用できます(たとえばnhibernateでは使用できません)。

しかし、私は、更新速度が十分速く、多くのデッドロックが発生する場合は、ORMが正しいツールではない可能性があります。そうでないと、データベーススキーマは再読み込みする必要があります/書かれている(例えば、データを読むときにそれを計算する)

+1

Serializableであっても、読み取りは共有ロックのみを取り出します。このテーブルにアクセスする複数の同様のトランザクションがある場合(同じコードが別々のスレッドで同時に実行されているとします)、どちらのスレッドも排他ロックにアップグレードして書き込みを実行できないため、デッドロックになります。 – Sam

+0

@Sam、デッドロックを含めるように私の答えを更新してくれてありがとう –

3

私はそれがNHibernateからすることができますか疑いがあります。個人的には、ストアドプロシージャを使用して、達成しようとしていることを実行します。

更新:続きを読むと、私はこれをさらに詳しく説明します。 FrederickはORMレイヤーから基礎となるデータベースエンジンの構文や実装固有の詳細なロックヒントを使用する方法を尋ねています。このような操作を実行しようとすると、間違ったレベルになります。可能であったとしても、NHibernateでサポートされているすべてのデータベースで一貫して動作する可能性は非常に低いです。

フレデリックの最終的な解決策は、先行排他的なロック(パフォーマンスを殺し、あなたが何をやっているか分からない限り一般的には悪い考えです)を必要としませんでしたが、私の答えは有効です。この質問に遭遇し、NHibernateから排他ロックオン・リードを行いたいと思っている人は、まず第一に:第二に:ストアド・プロシージャまたはSQLQueryを使用する必要がある場合。

関連する問題