@Version
アノテーションはJPAでどのように機能しますか?
次のような抜粋があるさまざまな答えを見つけました。
JPAは、エンティティのバージョンフィールドを使用して、同じデータストアレコードの同時変更を検出します。 JPAランタイムは、同じレコードを同時に変更しようとする試みを検出すると、最後にコミットしようとしているトランザクションに例外をスローします。
しかし、それがどのように機能するのかはまだわかりません。
また、次の行から:
バージョンフィールドは不変と見なす必要があります。フィールド値を変更すると、未定義の結果が生じます。
バージョンフィールドをfinal
として宣言する必要があるということですか?
しかし、それでもそれがどのように機能するのか分かりませんか?
エンティティMyEntity
に注釈付きのversion
プロパティがあるとします。
@Entity
public class MyEntity implements Serializable {
@Id
@GeneratedValue
private Long id;
private String name;
@Version
private Long version;
//...
}
更新時に、@Version
アノテーションが付けられたフィールドがインクリメントされ、次のようなWHERE
句に追加されます。
UPDATE MYENTITY SET ..., VERSION = VERSION + 1 WHERE ((ID = ?) AND (VERSION = ?))
WHERE
句がレコードと一致しない場合(同じエンティティが別のスレッドによって既に更新されているため)、永続化プロバイダーはOptimisticLockException
をスローします。
バージョンフィールドを最終として宣言する必要があるということですか?
いいえ、セッターを呼び出す必要はないので、セッターを保護することを検討できます。
@Pascalの答えは完全に有効ですが、私の経験から、以下のコードは楽観的なロックを達成するのに役立ちます。
@Entity
public class MyEntity implements Serializable {
// ...
@Version
@Column(name = "optlock", columnDefinition = "integer DEFAULT 0", nullable = false)
private long version = 0L;
// ...
}
どうして?なぜなら:
@Version
アノテーションが付けられたフィールドが誤ってnull
に設定されている場合。optlock
ではなくversion
のような名前を付けることをお勧めします。JPAベンダーは作成時に0
フィールドに@version
フィールドを適用するため、アプリケーションがonly JPAを使用してデータベースにデータを挿入する場合、最初のポイントは重要ではありません。ただし、ほとんどの場合、プレーンSQLステートメントも使用されます(少なくともユニット/統合テスト中)。
データベース内のエンティティが更新されるたびに、バージョンフィールドが1ずつ増加します。データベース内のエンティティを更新するすべての操作では、クエリにWHERE version = VERSION_THAT_WAS_LOADED_FROM_DATABASE
が追加されます。
操作の影響を受けた行をチェックする際に、jpaフレームワークは、バージョン番号がロードと持続の間で増加したときにクエリがデータベースでエンティティを見つけられないため、エンティティのロードと持続の間に同時変更がないことを確認できます。
一度に1つの更新のみを保証するために使用されるバージョン。 JPAプロバイダーはバージョンをチェックします。予想されるバージョンが既に増加している場合、他の誰かが既にエンティティを更新しているため、例外がスローされます。
したがって、エンティティ値の更新はより安全で、より楽観的です。
値が頻繁に変更される場合は、バージョンフィールドを使用しないことを検討してください。たとえば、「カウンターフィールドを持つエンティティは、Webページにアクセスするたびに増加します」
もう少し情報を追加するだけです。
JPAは内部でバージョンを管理しますが、JPAUpdateClause
を介してレコードを更新するときはバージョンを管理しません。そのような場合、クエリにバージョンの増分を手動で追加する必要があります。
ペドロ