2015年5月18日月曜日

【GAE】初級実装編3 一意制約(新規登録パターン)

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

現在はクラウド基盤「Google App Engine(以下、GAE)」の連載中です。

今回は一意制約について検証してみます。

GAEに一意制約は無い


一意制約とは何か」は、割愛させて頂きます
(読者ならみんな知っているでしょう)

とにかく、結論から言わせて頂ければ、GAEに一意制約はありません。

GAEのBigTableは、要するにJavaの動きで言う所のMapだからなんでしょうね。

Mapは「key」を基準にvalueを登録しますよね?
だから「keyが重複しているか?」は主キー検索一発でチェック出来ますし、重複も物理的にありえません。

しかし、valueの重複が無いかはmap総当たりでチェックするしか無いのです。
GAEも同じような状況にあるのです。

BigTableで一意制約はありません。不可能です。

しかし、BigTableはデータベースですから、本当に厳密な一意性を確保しなければならないシチュエーションも存在します。

そこで活躍するのが、「一意制約専用テーブルを作って主キーを一意制約みたいに使う」というテクニックです。

GAEライブラリであるslim3にはこのテクニックを支援する機能があります。
今回はそれのご紹介です。


Datastore.putUniqueValue


この機能を実現してくれるメソッドは「Datastore.putUniqueValue」、これです。

まずはソースをご覧下さい。

public Key insert(Shohin model) throws Exception {

  /*
   * トランザクション開始
   */
  Transaction tx = Datastore.beginTransaction();

  try {

   Key key = null;

   /*
    * 一意制約専用テーブルに登録する。
    */
   if (Datastore.putUniqueValue(Shohin.UNIQUE_SHOHIN_NAME, model.getShohinName())) {

    /*
     * 一意制約専用テーブルに登録が成功した場合
     */
    key = super.put(model);

   } else {

    /*
     * 一意制約エラー
     */
    throw new BusinessCheckException("対象の商品名は既に登録されています。");

   }

   /*
    * 正常な場合はコミットする。
    */
   tx.commit();

   return key;

  } catch (Exception e) {

   /*
    * エラーになったらロールバックする。
    */
   tx.rollback();
   throw e;

  }

 }

これは「商品名の一意制約チェック」を想定したものです。

いくつかの新登場の情報が出てきていますね。

トランザクション


まず、GAEのトランザクションはコレです。
Transaction tx = Datastore.beginTransaction();

  • Transaction tx = Datastore.beginTransaction();
  • tx.commit();
  • tx.rollback();

読んで字のごとく。説明不要。

疑似一意制約


そして、今回の主題となるのはココです。

if (Datastore.putUniqueValue(Shohin.UNIQUE_SHOHIN_NAME, model.getShohinName())) {

「Datastore.putUniqueValue」

これで「そのキーだけが主キーのテーブル」が作成され、データが投入されます。
ちなみに、Shohin.UNIQUE_SHOHIN_NAMEは単なる固定値。制約のイメージとして一意に定義しています。

/** 商品名一意性約 */
 public static final String UNIQUE_SHOHIN_NAME = "UniqueShohin_ShohinName";


この状況で商品を登録すると、結果はこちら。



「UniqueShohin_ShohinName」というテーブルが作られて、その値は一意制約指定したキー1コだけですね?

こうすることで、主キー制約を一意制約として使い回すことが出来るわけです。


考察

以上のことから分かりますように、「一意制約一個作るのも面倒臭い!!」というのが結論です。
一意制約の数だけテーブル数も増えていってしまいますので、そう気軽に搭載するわけにはいきません。

よって、「何があっても厳密に一意を維持しなければならない!!」という場合を除いて、基本的に一意制約は付与しない方向が良いと思います。

例:メールアドレス

世の中には「メールアドレスをログインIDとして使う」というシステムがあります。
ログインIDが重複しては大変なので、メールアドレスの一意制約は必須。
こういう場合は手間を惜しまず一意制約を搭載しなければなりません。

例:商品名

一方で、この例で出した商品名はどうか?

「同じ商品のレコードが2つ以上あっては業務に混乱を来すから防ぎたい」

という需要があるとしましょう。
しかしですね、商品名なんて重複してたら更新すれば良いのですよ。

この一意制約チェックは「複数ユーザが同時に同じタイミングにレコードを登録した場合」に威力を発揮するもの。

「別々のユーザが同じタイミングで同じ商品名を入れるなんて、まず滅多に考えられない。商品登録前に普通にDB検索チェックするだけで99.999%は防げる。それでも重複した場合は手動修正して貰えばいいや」

という楽観的対処で十分です。

一意制約に厳密性を求めず、ロジックで制御した楽観的一意制約で済ませる。

手を抜ける所は手を抜くのも、GAE開発の一つです。

終わりに

今回は「新規登録」の一意制約をご紹介しました。
しかし、この一意制約はロジックで作り出している擬似的なものですので、「更新」の一意制約はまた違ったロジックを組まなければなりません。

次回は「更新一意制約」についてご紹介します。

0 件のコメント:

コメントを投稿