はじめに: Cloud Datastore

非リレーショナル(NoSQL)データベースである Google Cloud Datastore にデータを保存する App Engine アプリケーションを作成する方法について説明します。

Cloud Datastore は App Engine で利用できるストレージ オプションの 1 つで、簡単にアプリに統合したりテキストデータを保存したりできます。Cloud Datastore、Cloud SQL、Cloud Storage を比較して、アプリの要件を満たすものを選択してください。

このサンプルは、一連のガイドを基に構築されており、ブログの投稿データを Cloud Datastore に保存する方法を示しています。

始める前に

開発環境を設定し、App Engine プロジェクトを作成します

ライブラリのインポート

このサンプルでは、次のライブラリを使用しています。

import com.google.appengine.api.datastore.DatastoreFailureException;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Query.FilterPredicate;

import org.jsoup.Jsoup;
import org.jsoup.safety.Whitelist;

使用しているデータストアの機能に応じて、作業で使用するライブラリが異なる場合があります。すべてのタスクですべてのリストされたライブラリが必要になるわけではありません。必要なライブラリのみをインポートすることをおすすめします。

Cloud Datastore 接続の設定

Cloud Datastore の読み取りまたは書き込みを行う前に、DatastoreService オブジェクトを作成し、それを使用して Cloud Datastore と通信する必要があります。次のコードで接続を作成します。

DatastoreService datastore;
datastore = DatastoreServiceFactory.getDatastoreService();

DatastoreService は処理の負荷が非常に大きいオブジェクトであるため、バッチ オペレーションを使用して呼び出す回数を最小限に抑えてください。

エンティティの作成と保存

オブジェクトは、エンティティ プロパティに保存されるオブジェクト データとともにエンティティとして Cloud Datastore に保存されます。この例では、titlebodyauthortimestamp の 4 つのプロパティを持つ post というエンティティにデータを保存します。

データは、ユーザーが送信したフォームデータの処理で説明されているように、HTML フォームを使用して送信されます。

DatastoreService datastore;

@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException {

  // Create a map of the httpParameters that we want and run it through jSoup
  Map<String, String> blogContent =
      req.getParameterMap()
          .entrySet()
          .stream()
          .filter(a -> a.getKey().startsWith("blogContent_"))
          .collect(
              Collectors.toMap(
                  p -> p.getKey(), p -> Jsoup.clean(p.getValue()[0], Whitelist.basic())));

  Entity post = new Entity("Blogpost"); // create a new entity

  post.setProperty("title", blogContent.get("blogContent_title"));
  post.setProperty("author", blogContent.get("blogContent_author"));
  post.setProperty("body", blogContent.get("blogContent_description"));
  post.setProperty("timestamp", new Date().getTime());

  try {
    datastore.put(post); // store the entity

    // Send the user to the confirmation page with personalised confirmation text
    String confirmation = "Post with title " + blogContent.get("blogContent_title") + " created.";

    req.setAttribute("confirmation", confirmation);
    req.getRequestDispatcher("/confirm.jsp").forward(req, resp);
  } catch (DatastoreFailureException e) {
    throw new ServletException("Datastore error", e);
  }
}

@Override
public void init() throws ServletException {

  // setup datastore service
  datastore = DatastoreServiceFactory.getDatastoreService();
}

サンプルでは、それぞれの post エンティティに Cloud Datastore によって自動的に作成された ID があります。それらの ID の代わりに独自の ID を割り当てる場合は、エンティティを作成する際に ID を指定します。

Entity post = new Entity("Blogpost", "[IDENTIFIER]");

エンティティの更新で説明されているように、既存のエンティティの更新時は ID を指定する機能が重要になります。

post エンティティの作成後、setProperty() を使用して titleauthorbodytimestamp のプロパティを設定し、put を使用してすべてのデータが格納されたエンティティを Cloud Datastore に保存します。

datastore.put(post);

エンティティの読み取り

Cloud Datastore からエンティティを読み取るには、クエリを使用する必要があります。このサンプルでは、トップページに 5 つのブログ投稿を表示するクエリを使用しています。

final Query q =
    new Query("Blogpost").setFilter(new FilterPredicate("title", FilterOperator.NOT_EQUAL, ""));

PreparedQuery pq = datastore.prepare(q);
List<Entity> posts = pq.asList(FetchOptions.Builder.withLimit(5)); // Retrieve up to five posts

posts.forEach(
    (result) -> {
      // Grab the key and convert it into a string in preparation for encoding
      String keyString = KeyFactory.keyToString(result.getKey());

      // Encode the entity's key with Base64
      String encodedID = new String(Base64.getUrlEncoder().encodeToString(String.valueOf(keyString).getBytes()));

      // Build up string with values from the Datastore entity
      String recordOutput =
          String.format(blogPostDisplayFormat, result.getProperty("title"), result.getProperty("timestamp"),
              result.getProperty("author"), encodedID, encodedID, result.getProperty("body"));

      out.println(recordOutput); // Print out HTML
    });

Cloud Datastore の query オブジェクトでは、SQL の WHERE 句に似た setFilter を使用したフィルタリングがサポートされます。この例では、FetchOptions.Builder クラスを使用して、5 つのブログ投稿をフェッチしています。

カーソルを使用してバッチ内の数が決まっていないアイテムを表示する例については、Bookshelf チュートリアルをご覧ください。

エンティティの更新

エンティティを更新するには、元のエンティティと同じキーを持つ新しいエンティティを作成します。この例では、元のキーと同じキーを持つ新しい post エンティティを作成しますが、保存する前に更新されたテキストを入れます。

// Decode the websafe ID
String decodedKey = new String(Base64.getUrlDecoder().decode(blogContent.get("blogContent_id")));

// Create a key from the decoded websafe string
Key originalPostKey = KeyFactory.stringToKey(decodedKey);

// Create a new entity with the same key as the original
Entity post = new Entity("Blogpost", originalPostKey.getId());

// Populate the new entity with the updated blog post contents
post.setProperty("title", blogContent.get("blogContent_title"));
post.setProperty("author", blogContent.get("blogContent_author"));
post.setProperty("body", blogContent.get("blogContent_body"));
post.setProperty("timestamp", new Date());

try {
  datastore.put(post); // Store the post

  // Send the user to the confirmation page with personalised confirmation text
  String confirmation = "Post with title " + blogContent.get("blogContent_title") + " updated.";

  req.setAttribute("confirmation", confirmation);
  req.getRequestDispatcher("/confirm.jsp").forward(req, resp);
} catch (DatastoreFailureException e) {
  throw new ServletException("Datastore error", e);
}

エンティティの削除

Cloud Datastore にあるエンティティを削除するには、削除する投稿の Key を渡す delete メソッドを呼び出します。

// Get the websafe cursor and then convert it into a usable key
// for retrieving a particular post
Map<String, String[]> blogContent = req.getParameterMap();
String[] postId = blogContent.get("id");
String decodedKey = new String(Base64.getUrlDecoder().decode(postId[0])); // Decode the websafe ID

try {
  try {
    Entity deletePost = datastore.get(KeyFactory.stringToKey(decodedKey));
    // Delete the entity based on its key
    datastore.delete(deletePost.getKey());

    // Send the user to the confirmation page with personalised confirmation text
    String confirmation = "Post " + deletePost.getProperty("title") + " has been deleted.";

    req.setAttribute("confirmation", confirmation);
    req.getRequestDispatcher("/confirm.jsp").forward(req, resp);

  } catch (EntityNotFoundException e) {
    throw new ServletException("Datastore error", e);
  }
} catch (DatastoreFailureException e) {
  throw new ServletException("Datastore error", e);
}

この例のエンコードされた元のキー ID は、URL を介してサンプルアプリに渡されます。そこでデコードされ、KeyFactory クラスからの createKey への呼び出しでキー値として使用されます。 createKey は、エンティティ タイプ Blogpost、およびサーブレット リクエスト変数から取得され Long 整数に変換される ID を必要とします。

App Engine へのデプロイ

Maven を使用して App Engine にアプリをデプロイします。

プロジェクトのルート ディレクトリに移動し、次のように入力します。

mvn appengine:deploy

Maven によってアプリがデプロイされた後、次のように入力すると、新しいアプリでウェブブラウザのタブが自動的に開きます。

gcloud app browse

次のステップ

Cloud Datastore は、テキストベースのデータを保存するのに便利です。ただし、画像を保存する場合は、Cloud Storage の使用を検討する必要があります。

Cloud Storage の使用

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Java の App Engine スタンダード環境