GAE/JにおけるDatastoreServiceアクセスの考察

#Who
Google App Engine (GAE/J)
App Engine SDK 1.9.18
JRE 1.7.0

#What
DatastoreServiceへのアクセス方法を考える。

#When, Where
サーバーサイド
DatastoreServiceへのアクセス部分

#Why
一つ一つのEntityに対してDatastoreへのアクセスを書くのは面倒臭い。
また、見通しが悪くなる。
処理を共通化できるのであれば共通化する。

#How
Person(名字、名前、身長)をDatastoreに保存する。
Personクラスを用意する。

```Java:Person.java
public final class Person implements Storable<Person> {
  static final String FIRST_NAME = "firstName";
  static final String LAST_NAME = "lastName";
  static final String HEIGHT = "height";
  private final String firstName;
  private final String lastName;
  private final int height;

  public Person(final String firstName, final String lastName, final int height) {
    super();
    this.firstName = firstName;
    this.lastName = lastName;
    this.height = height;
  }

  public final String getFirstName() {
    return firstName;
  }

  public final String getLastName() {
    return lastName;
  }

  public final int getHeight() {
    return height;
  }

  @Override
  public Map<String, Object> getPropertyMap() {
    return new HashMap<String, Object>() {
      private static final long serialVersionUID = 1L;
      {
        put(FIRST_NAME, getFirstName());
        put(LAST_NAME, getLastName());
        put(HEIGHT, getHeight());
      }
    };
  }

  @Override
  public Designable<Person> getDesign() {
    return new PersonDesign();
  }
}

```

必要なインターフェースはgetPropertyMapとgetDesign。
getPropertyMapはDatastoreに保存したい値とキー名のMapを返すようにする。

Designは以下のインターフェースを持つクラス。

```java:PersonDesign.java
public class PersonDesign implements Designable<Person> {
  @Override
  public Person create(final Map<String, Object> propertyMap) throws IllegalPropertyMapException {
    for (final String propertyName : getPropertyNames()) {
      if (!propertyMap.containsKey(propertyName)) {
        throw new IllegalPropertyMapException();
      }
    }
    final String firstName = (String) propertyMap.get(Person.FIRST_NAME);
    final String lastName = (String) propertyMap.get(Person.LAST_NAME);
    final long height = (long) propertyMap.get(Person.HEIGHT);
    return new Person(firstName, lastName, (int) height);
  }

  @Override
  public String getName() {
    return "Person";
  }

  @Override
  public List<String> getPropertyNames() {
    return Arrays.asList(Person.FIRST_NAME, Person.LAST_NAME, Person.HEIGHT);
  }
}
```

createはPersonをnewするための関数。
getNameはKindNameを返す。
getPropertyNamesはPersonが保存するプロパティのキー名を返す。

DatastoreServiceへのアクセスは以下のDaoを通して行う。

```java:CoreDao.java
public class CoreDao {
  private static final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

  public final <T extends Storable<T>> void put(final T input) {
    datastore.put(createEntity(input));
  }

  public final <T extends Storable<T>> void put(final Iterable<T> inputCollection) {
    final List<Entity> entityList = new ArrayList<Entity>();
    for (final T input : inputCollection) {
      entityList.add(createEntity(input));
    }
    datastore.put(entityList);
  }

  public final <T> Iterable<T> get(final Designable<T> design) {
    final Query q = new Query(design.getName());
    final PreparedQuery pq = datastore.prepare(q);
    final List<T> list = new ArrayList<T>();
    for (final Entity entity : pq.asIterable()) {
      final Map<String, Object> propertyMap = new HashMap<String, Object>();
      for (final String propertyName : design.getPropertyNames()) {
        propertyMap.put(propertyName, entity.getProperty(propertyName));
      }
      try {
        list.add(design.create(propertyMap));
      } catch (final IllegalPropertyMapException e) {
        e.printStackTrace();
      }
    }
    return list;
  }

  private <T extends Storable<T>> Entity createEntity(final T input) {
    assert input != null;

    final Entity entity = new Entity(input.getDesign().getName());
    for (final Entry<String, Object> entry : input.getPropertyMap().entrySet()) {
      final String propertyName = entry.getKey();
      final Object propertyValue = entry.getValue();
      entity.setProperty(propertyName, propertyValue);
    }
    return entity;
  }
}
```

Datastoreへの保存はputを用いる。
Entityクラスの作成はcreateEntityを使用する。
DesignのgetNameでKind名を取得。
StorableインターフェースのgetPropertyMapで保存するプロパティをセットする。

Datastoreからの取得はgetを用いる。
getの引数にはDesignクラスを渡す。
DesignのcreateでPersonインスタンスを作る。
CoreDaoにはPersonクラスに依存した処理はないので、インターフェースを実装したクラスであれば共通に使うことができる。