KEIS BLOGは株式会社ケイズ・ソフトウェアが運営しています。

KEIS BLOG

Google App Engine 第十一回


Slim3とは

Slim3とはGoogle App Engine/Javaに最適化されたフルスタックMVCフレームワークです。

Slim3を使ったページの作成方法5

今回は前回の修正に加えMemcacheを利用したデータのキャッシュ方法をご紹介いたします。

Abstructクラスの作成

1. File > New > Class を選択して空のクラスファイルを作成します。

Package: {任意のパッケージ名}.model
Package: biz.e_zero.slim3_test.service.dao
Name: DaoBaseEx

2. 下記のソースコードを貼り付けます。

package biz.e_zero.slim3_test.service.dao;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.collections4.CollectionUtils;
import org.slim3.datastore.DaoBase;
import org.slim3.datastore.Datastore;
import org.slim3.memcache.Memcache;

import biz.e_zero.slim3_test.model.Slim3Model;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.memcache.Expiration;

public abstract class DaoBaseEx<T extends Slim3Model> extends DaoBase<T>  {
    private MemcacheEx memcacheEx = new MemcacheEx();
    private static final int EXPIR_SECONDS = 60 * 60 * 24;
    
    // Memcache経由でDatastoreへ<T>オブジェクトを格納するメソッド(P5)
    @Override
    public Key put(T model) {
        Key key = super.put(model);
        Memcache.put(key, model, Expiration.byDeltaSeconds(EXPIR_SECONDS));
        return key;
    }
    
    // Memcache経由でDatastoreへ<T>オブジェクトを格納するメソッド(P5)
    @Override
    public List<Key> put(List<T> models) {
        Map<Key, T> map = new HashMap<Key, T>();
        for (T model : models) {
            map.put(model.getKey(), model);
        }

        memcacheEx.putAll(map);
        return Datastore.put(models);
    }

    // Memcache経由でDatastoreから<T>オブジェクトを取得するメソッド(P5)
    @Override
    public T get(Key key) {
        T model = null;
        if (Memcache.contains(key)) {
            model = Memcache.get(key);
        } else {
            model = super.get(key);
            Memcache.put(model.getKey(), model, Expiration.byDeltaSeconds(EXPIR_SECONDS));
        }
        return model;
    }

    // Memcache経由でDatastoreから<T>オブジェクトを取得するメソッド(P5)
    @Override
    public T getOrNull(Key key) {
        T model = null;
        if (Memcache.contains(key)) {
            model = Memcache.get(key);
        } else {
            model = super.getOrNull(key);
            if (model != null) {
                Memcache.put(model.getKey(), model, Expiration.byDeltaSeconds(EXPIR_SECONDS));
            }
        }
        return model;
    }
    
    // Memcache経由でDatastoreから<T>オブジェクトを取得するメソッド(Bulk get)(P5)
    @Override
    public List<T> get(List<Key> keys) {
        Map<Key, T> memModelMap = memcacheEx.getAll(keys);
        Map<Key, T> dsModelMap = getDSModelMap(keys, memModelMap);
        memcacheEx.putAll(dsModelMap);
        
        List<T> modelList = new ArrayList<T>();
        for (Key key : keys) {
            if (memModelMap.containsKey(key)) {
                modelList.add(memModelMap.get(key));
            } else if (dsModelMap.containsKey(key)) {
                modelList.add(dsModelMap.get(key));
            }
        }
        return modelList;        
    }

    // Memcacheを経由でDatastoreから<T>オブジェクトを削除するメソッド(P5)
    @Override
    public void delete(Key key) {
        Memcache.delete(key);
        super.delete(key);
    }

    // Memcacheを経由でDatastoreから<T>オブジェクトを削除するメソッド(P5)
    private Map<Key, T> getDSModelMap(List<Key> keys, Map<Key, T> memModelMap) {
        Collection<Key> subCollection =
            CollectionUtils.subtract(keys, memModelMap.keySet());
        List<Key> subList = new ArrayList<Key>(subCollection);
        return super.getAsMap(subList);
    }
    
    private class MemcacheEx {
        public void putAll(Map<Key, T> values) {
            Map<Object, Object> objectMap = new HashMap<Object, Object>();
            for (Entry<Key, T> entry : values.entrySet()) {
                Object key = entry.getKey();
                Object model = entry.getValue();
                objectMap.put(key, model);
            }
            Memcache.putAll(objectMap, Expiration.byDeltaSeconds(EXPIR_SECONDS));
        }
        public Map<Key, T> getAll(List<Key> keyList) {
            Map<Key, T> map = new HashMap<Key, T>();
            Map<Object, Object> memcachedMap = Memcache.getAll(keyList);
            for (Entry<Object, Object> entry : memcachedMap.entrySet()) {
                Key key = (Key) entry.getKey();
                @SuppressWarnings("unchecked")
                T model = (T) entry.getValue();
                map.put(key, model);
            }
            return map;
        }
    }
}

Interfaceクラスの作成

1. File > New > Class を選択して空のクラスファイルを作成します。

Package: {任意のパッケージ名}.model
Name: Slim3Model

2. 下記のソースコードを貼り付けます。

package biz.e_zero.slim3_test.model;

import com.google.appengine.api.datastore.Key;

public interface Slim3Model {
    Key getKey();
    Long getVersion();
}



<h3>Serviceクラス<MemoService>の変更</h3>

1. [Google App Engine 第八回で作成]した<MemoService>クラスを下記の内容で書き換える。


package biz.e_zero.slim3_test.service;

import java.util.List;

import org.slim3.datastore.Datastore;

import com.google.appengine.api.datastore.Key;

import biz.e_zero.slim3_test.meta.MemoMeta;
import biz.e_zero.slim3_test.model.Memo;
import biz.e_zero.slim3_test.service.dao.DaoBaseEx;

// <DaoBaseEx>クラスを継承
public class MemoService  extends DaoBaseEx<Memo>{
    /**
     * Get Memo object by key from DS.
     * @param key
     * @return Memo
     */
    public Memo get(Key key) {
        // Datastoreのメソッドからsuper classのメソッドに変更(P5)
        // return Datastore.getOrNull(MemoMeta.get(), key);
        return super.getOrNull(key);
    }
    
    /**
     * Get all Memo object from DS.
     * @return List<Memo>
     */
    public List<Memo> list() {
        MemoMeta m = MemoMeta.get();
        // Datastoreのメソッドからsuper classのメソッドに変更(P5)
        // return Datastore.query(m).sort(m.updateDate.desc).asList();
        List<Key> keys = Datastore.query(m).sort(m.updateDate.desc).asKeyList();
        return super.get(keys);
    }
    
    /**
     * Insert Memo object into DS.
     * @param memo
     */
    public void insert(Memo memo) {
        memo.setKey(Datastore.allocateId(MemoMeta.get()));
        // Datastoreのメソッドからsuper classのメソッドに変更(P5)
        // Datastore.put(memo);
        super.put(memo);
    }

    /**
     * Update Memo object in DS. 
     * @param memo
     */
    public void update(Memo memo) {
        // Datastoreのメソッドからsuper classのメソッドに変更(P5)
        // Datastore.put(memo);
        super.put(memo);
    }
    
    /**
     * Delete Memo object from DS.
     * @param key
     */
    public void delete(Key key) {
        // Datastoreのメソッドからsuper classのメソッドに変更(P5)
        // Datastore.delete(key);
        super.delete(key);
    }
    
    public void upsert(Memo memo) {
        if( memo.getKey() ==  null || get(memo.getKey()) == null ) {
            insert(memo);
        } else {
            update(memo);
        }
    }
}

Google App Engineへのデプロイ

1. appengine-web.xmlの編集
appengin-web.xmlのapplication sectionをデプロイ先のアプリケーション名に変更する。
* デプロイ先のアプリケーションは予めGoogle developer consoleで作成しておく。

	<application>{application name}</application>

2. デプロイ対象プロジェクトを右クリック > Google > Deploy to App Engine を選択

3. Deploy Project to Google App Engineウィンドウが開くので、問題なければDeployボタンを押す。

4. Deploy完了後、対象アプリケーションが表示される。

Google App Engine上での動作確認

1. 下記URLからアプリケーションを起動する

URL: {application name}.appspot.com/memo/index
misawa01

2. 前回同様の操作でMemoデータを登録する。

misawa02
misawa03

Developer consoleでの登録データ確認

1. 下記のURLにアクセスしてDeveloper contoleを起動する。

URL: https://console.developers.google.com

2. Developer consoleからDetastoreの利用状況を確認する。

サイドパネルよりデータストア > Entities を選択 > 種別[Memo] を選択
Memoオブジェクトが登録されていることを確認する。
misawa04

3. Developer consoleからMemcacheの利用状況を確認する。

サイドパネルよりApp Engine > Memcache を選択
ヒット率、キャッシュ内アイテムから利用状況を確認する。
キー検索を実行して、キーの内容を確認する。
misawa05

【関連記事】
Google App Engine 第一回
Google App Engine 第二回
Javaのライブラリを手軽にテストしたい!! Groovy入門 第1回
Google App Engine 第三回
Google App Engine 第四回
Google App Engine 第五回
Google App Engine 第六回
Google App Engine 第七回
Google App Engine 第八回
Google App Engine 第九回
Google App Engine 第十回