update(entity)
概要
基本概念
Entityをもとに 排他制御あり一件更新 をします(但し、排他制御対象でないテーブルなら、排他制御なしの普通の一件更新)。主に、排他制御が必要なオンライン系の更新処理で利用されます。 アプリケーションでは、すれ違いが発生した場合に発生する例外を明示的にハンドリングし、業務的なアクションを取ることが想定されます。
会話上では、あっぷでーと もしくは あっぷでーとえんてぃてぃ と表現します。文脈上、曖昧で伝わりにくい場合は Behavior のあっぷでーと(メソッド) や 排他制御ありのあっぷでーと などと言うとわかりやすいでしょう。
実装方法
実装の流れ
Behaviorの update() を呼び出し、Entityを指定します。
e.g. update()の実装手順 (Eclipseでコード補完) {MEMBER} @Java
memberBhv.up // .up と打って enter
--
memberBhv.update(member);
排他制御対象テーブルの場合、排他制御処理のために、PKに加えて排他制御カラムの値を設定します。
e.g. 事前に検索されたPKと排他制御カラムの値を設定して更新 {MEMBER} @Java
Member member = new Member();
member.setMemberId(memberId); // PK
member.setMemberName("Stojkovic"); // 業務的な更新値の設定
member.setVersionNo(versionNo); // 事前に検索されたバージョン番号
memberBhv.update(member);
Setterが呼び出されたカラムに対してのみ、更新値を設定します。
ユニークキーで更新 @since 1.0.5g
主キーではなく、ユニーク制約のキーで更新することもできます。
プログラム上でPKは持ってなくて代わりにユニークキーだけを持っているような場合に、PK取得のためだけの無駄な事前検索を省くことができます。 (こういったケースはわりと多いと想定されます)
e.g. ユニークキーである会員アカウントで更新 {MEMBER} @Java
// PK取得のためだけの無駄な事前検索は不要
//Member member = memberBhv.selectEntityWithDeletedCheck(cb);
Member member = new Member();
member.uniqueBy(memberAccount); // ユニークキー (複合キーなら引数が増える)
member.setMemberName("Stojkovic"); // 業務的な更新値の設定
member.setVersionNo(versionNo); // 事前に検索されたバージョン番号
memberBhv.update(member);
そのテーブルにユニーク制約が一つだけの場合は uniqueBy() というメソッド名、二つ以上ある場合は uniqueBy[カラム名の列挙]() というメソッド名になります。
このとき、Entityの中の PK は、値が設定されていても更新処理に何も影響しません。PKの値自体を更新することもできません。 (そもそもPKの値は更新すべきではなく、更新できない方がよいという考えから)
バッチ更新では利用できません。(uniqueByしても無視されます)
insertOrUpdate()など、他の一件更新系のメソッドでも利用できます。
メソッド仕様
引数
該当のBehaviorに対応するテーブルのEntityとなります。
nullを指定した場合は例外発生します。 また、PKカラムの値と(排他制御が有効の場合に)排他制御カラムの値の指定が必須です(指定なしは例外)。
戻り値
void です。
例外
- 更新対象が存在しなかった場合
- org.seasar.dbflute.exception.EntityAlreadyDeletedException
- この例外は、排他制御対象でない場合のみ発生します。業務的に存在することが前提の更新であれば、開発時はバグ・データ不備、結合テスト・運用時はすれ違いと捉えることができます。
- 排他制御対象の場合は、"更新対象が存在しないという状況" は、"すれ違いがあったのと同じ状況" として扱われます(仕組み上区別が付かないため)。
- 更新結果が複数件の場合
- org.seasar.dbflute.exception.EntityDuplicatedException
- 予期せぬレコードを更新してしまっている可能性があるため、ロールバックが求められます。
- すれ違いがあった場合
- org.seasar.dbflute.exception.EntityAlreadyUpdatedException
- この例外は、排他制御対象の場合のみ発生します。
- 一意制約違反があった場合
- org.seasar.dbflute.exception.EntityAlreadyExistsException
- DBFluteで正式サポートしていない DBMS では、別の例外(SQLFailureException)の可能性があります。
設定された値だけ更新
設定された(setterが呼ばれた)カラム と内部で自動的に設定されるカラム(バージョン番号など)だけが更新対象になります。ゆえに、何も設定しなかった(Entity上は) null のカラムがあっても、そのカラムが null で更新されることはありません(明示的に null で設定されれば、null で更新されます)。
これを ModifedOnly と呼び、DBFluteでは基本的には 更新都合の事前検索は不要 です。
e.g. update()は設定された値だけ更新される {MEMBER,memberId=3} @Java
Member member = new Member();
member.setMemberId(3);
member.setMemberName("Stojkovic"); // 明示的に設定されたカラムだけが更新される
member.setBirthdate(null); // 明示的に null を設定すれば null で更新される
memberBhv.update(member);
// update MEMBER
// set MEMBER_NAME = 'Stojkovic'
// , BIRTHDATE = null
// , VERSION_NO = ...
// where ...
設定された値がなければ
排他制御対象でないテーブル、かつ、共通カラムの自動設定対象になっていないテーブル(かつ、Entity で自動設定を無効にしていない)で、設定された値(更新値)が何もない(呼び出された Setter がない)場合は更新処理自体が発生しません。その場合、DB上で仕掛けているトリガがあったとして発動しません。
更新系、列挙されるカラムのまとめ
まとめていますので、ぜひご覧を。
排他制御の自動化
対応テーブルが排他制御カラムを持っている場合は、このメソッドの処理に排他制御が追加されます(排他制御が有効になる)。 排他制御が不要な状況がある場合は、このメソッドではなく、updateNonstrict() を利用します。
その場合、排他制御カラムは自動で更新されます。例えば、バージョン番号は Entity に設定されている値にプログラム上でインクリメント(+1)された値で更新 されます。更新後の Entity には、(インクリメントされた)その値が格納されています。
また、検索条件に排他制御カラムによる等値条件が追加され、すれ違いが発生した場合(更新対象が存在しなかった場合)は、 EntityAlreadyUpdatedException が発生します。(ゆえに、排他制御カラムの値の設定は必須です)
e.g. 排他制御カラムの値が色々なところで活躍 {MEMBER,memberId=3,versionNo=10} @Java
Member member = memberBhv.selectByPKValue(3);
member.setMemberName("Stojkovic");
memberBhv.update(member);
// update MEMBER
// set MEMBER_NAME = 'Stojkovic'
// , VERSION_NO = 11
// where MEMBER_ID = 3 and VERSION_NO = 10
共通カラムの自動設定
対応テーブルが共通カラムの自動設定対象になっている場合は、実行時に設定された通りに共通カラムの自動設定が行われます。 ゆえに、その場合は、プログラム上で共通カラムの値の設定をする必要はありません(設定しても自動設定処理にて上書きされます)。 自動設定された値は、更新処理後に Entity に格納されています。
もし、共通カラムの自動設定を無視した更新を行いたい場合は、Entity の disableCommonColumnAutoSetup() を更新前に呼び出すと、共通カラムの自動設定がされなくなります。 主に、システム移行のプログラム(移行プログラム)などで、共通カラムの値を保持したままデータを更新したいような場合に有効です。
カスケードはしません
例えば、会員 Entity が購入 Entity のリストを保持していたとしても、更新するのは会員、つまり、基点テーブルだけです。 自動で購入のリストも一緒に更新しにいくことはありません。これは DBFlute のポリシーですが、明示的に更新処理を指定していないものがディベロッパーの気付かないところで更新される、というような状態を避けるためです。 できた方が便利と思う場面も確かにありますが、別の機能でも同様ですが 明示的に が重視されています。
Entity更新のスコープ
Entity を利用した更新系のメソッドでは、更新値はプログラム上で指定されたスカラ値である が基本です。例えば、SQL上で値を導出(set句で足し算したり別のカラムの値を設定したり)して更新をする、というような場合は、 varying 系の更新メソッドを利用します。但し、やれることに限界もありますので、複雑な計算をするような場合は、 一度検索してからプログラム上で値を導出して更新するか、外だしSQLで実装します。