2015年5月28日木曜日

【GAE】初級実装編4 一意制約(更新パターン)

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

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

今回も前回に引き続き一意制約についてです。

やっぱりGAEに一意制約は無い


前回にもテーマにしましたが、GAEに一意制約はありません。
しかし主キー制約はあります。
よって、主キーのみのテーブルを作って主キー制約を実現するしか無いです。

まずは前回にも記載した新規登録の一意制約ソースを以下に張ります。

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にあるのは「put」と「delete」ですから、「新規登録/更新」などという区分けは技術的にはありません。
しかし人間の操作感では「新規登録/更新」では別物でしょう。
「put」を「新規登録/更新」で使い分けるには、以下ロジックが必要になります。


・対象データを検索する。
⇒データが存在していなければ新規登録。
⇒データが存在していれば更新。


一回find処理が必要になります。普通のSQLみたいにupdateは存在しません。

その上で、delete&insertで一意制約ロジックを実現することになります。

そのフローはこちら。

①一意制約対象のカラムが更新前と更新後で違うかどうかを判定する。
⇒同じである場合は普通にputして終わり。
⇒違う場合胃は次に進む。
②一意制約テーブルに更新後のカラムを新規登録する。
⇒失敗したら処理終了。
⇒成功したら次に進む。
③putする。
④一意制約テーブルに更新前のカラムを削除する。

一意制約カラム削除のメソッドは「Datastore.deleteUniqueValue」です。

これを使用したソースはこちら。

public Shohin update(Shohin updateModel) throws Exception {

   Transaction tx = Datastore.beginTransaction();

   try {

       /*
        * 既存を取得
        */
       Shohin model = super.get(updateModel.getKey());
       model.setEntityUpdate(updateModel);
       super.put(model);

       /*
        * 一意制約項目が既存と同じであれば、そのまま更新する。
        */
       if (StringUtils.equals(model.getShohinName(), updateModel.getShohinName())) {

           model.setEntityUpdate(updateModel);
           super.put(model);

       } else {

           /*
            * 一意制約項目と異なる場合は、更新を行ってから過去の一意制約照合用レコードを消す。
            */
           if (Datastore.putUniqueValue(Shohin.UNIQUE_SHOHIN_NAME, model.getShohinName())) {

               model.setEntityUpdate(updateModel);
               super.put(model);
               Datastore.deleteUniqueValue(Shohin.UNIQUE_SHOHIN_NAME, updateModel.getShohinName());

           }

       }

       tx.commit();

       return model;

   } catch (Exception e) {

       tx.rollback();
       throw e;

   }

}

普通のSQLであればUPDATE一発で済むのに、GAEではこんなにロジックを作らなければなりません。
超面倒ですね!!

GAEの特性を認識せよ


これにより、GAEには個性があるということがお分かり頂けたかと思います。
要点は二点です。

  • GAEで一意制約を実現するのは大変である。
  • GAEはputであって、insert/updateではない。新規登録と更新を分けるのは大変である。

要件定義する時に、エンジニアがこの辺りの事情に配慮して定義しなければいけないのです。

やっぱり、基本的にGAEの一意制約は、本当に本当に必要な場合以外は適応しないというのが大原則と考えて良さそうです。

終わりに

なかなかGAEの個性に悩まされた要件でした。

次はちょっと変わった実装、「メール送信」についてご紹介します。

0 件のコメント:

コメントを投稿