Storing Data with Objectify and Datastore

This walkthrough describes how user supplied messages are stored in Google Cloud Datastore using Objectify.

It also shows how these messages are retrieved and displayed to the user.

In the following code walkthrough, you'll learn about the following items:

  • The Objectify dependency in your project pom.xml file.
  • The data model classes needed for the Guestbook app.
  • The entries in web.xml that route requests to SignGuestbookServlet.
  • The JSP code allowing users to post greetings to the data store, and display all stored greetings.
  • The SignGuestbookServlet code that handles the form submission and saves the messages to the datastore.

This page is part of a multi-page tutorial. To start from the beginning and see instructions for setting up, go to Creating a Guestbook.

The Objectify dependency in pom.xml

To use the Objectify library, you must declare the dependency in the final/pom.xml file, so Maven can install it, as follows:

<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>${guava.version}</version>
</dependency>
<dependency>
  <groupId>com.googlecode.objectify</groupId>
  <artifactId>objectify</artifactId>
  <version>${objectify.version}</version>
</dependency>

Maven installs the library the next time you run a target that needs it, such as the development server.

The data model classes

With Objectify, you create classes whose instances will represent data store entities in your code. Objectify does the work of translating these Java objects to the entities stored in Cloud Datastore.

The following code shows the Greeting class representing a message posted by the user:

package com.example.guestbook;

import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.Parent;

import java.lang.String;
import java.util.Date;
import java.util.List;

/**
 * The @Entity tells Objectify about our entity.  We also register it in {@link OfyHelper}
 * Our primary key @Id is set automatically by the Google Datastore for us.
 *
 * We add a @Parent to tell the object about its ancestor. We are doing this to support many
 * guestbooks.  Objectify, unlike the AppEngine library requires that you specify the fields you
 * want to index using @Index.  Only indexing the fields you need can lead to substantial gains in
 * performance -- though if not indexing your data from the start will require indexing it later.
 *
 * NOTE - all the properties are PUBLIC so that we can keep the code simple.
 **/
@Entity
public class Greeting {
  @Parent Key<Guestbook> theBook;
  @Id public Long id;

  public String author_email;
  public String author_id;
  public String content;
  @Index public Date date;

  /**
   * Simple constructor just sets the date
   **/
  public Greeting() {
    date = new Date();
  }

  /**
   * A convenience constructor
   **/
  public Greeting(String book, String content) {
    this();
    if( book != null ) {
      theBook = Key.create(Guestbook.class, book);  // Creating the Ancestor key
    } else {
      theBook = Key.create(Guestbook.class, "default");
    }
    this.content = content;
  }

  /**
   * Takes all important fields
   **/
  public Greeting(String book, String content, String id, String email) {
    this(book, content);
    author_email = email;
    author_id = id;
  }

}

The sample also uses the model class Guestbook to represent an entire guestbook:

package com.example.guestbook;

import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;

/**
 * The @Entity tells Objectify about our entity.  We also register it in
 * OfyHelper.java -- very important.
 *
 * This is never actually created, but gives a hint to Objectify about our Ancestor key.
 */
@Entity
public class Guestbook {
  @Id public String book;
}

While our sample doesn't explicitly create a Guestbook object in the datastore, the sample uses the Guestbook class as part of the datastore key for the Greeting objects. This demonstrates how you can define keys so that all of the greetings in a guestbook could be updated in a single datastore transaction.

The helper file OfyHelper.java

In order to use Objectify in a JSP, we need a helper class that registers each model class in the JSP servlet context. The following code in OfyHelper.java shows how to do this:

package com.example.guestbook;

import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyFactory;
import com.googlecode.objectify.ObjectifyService;

import javax.servlet.ServletContextListener;
import javax.servlet.ServletContextEvent;

/**
 * OfyHelper, a ServletContextListener, is setup in web.xml to run before a JSP is run.  This is
 * required to let JSP's access Ofy.
 **/
public class OfyHelper implements ServletContextListener {
  public void contextInitialized(ServletContextEvent event) {
    // This will be invoked as part of a warmup request, or the first user request if no warmup
    // request.
    ObjectifyService.register(Guestbook.class);
    ObjectifyService.register(Greeting.class);
  }

  public void contextDestroyed(ServletContextEvent event) {
    // App Engine does not currently invoke this method.
  }
}

This file refers to the Greeting and Guestbook model classes used in the sample.

Mapping the OfyHelper helper in the servlet

Objectify requires a filter to clean up any thread-local transaction contexts and pending asynchronous operations that remain at the end of a request. You add this filter in web.xml as follows:

<filter>
  <filter-name>ObjectifyFilter</filter-name>
  <filter-class>com.googlecode.objectify.ObjectifyFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>ObjectifyFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
  <listener-class>com.example.guestbook.OfyHelper</listener-class>
</listener>

Handling user input in the JSP template

The following imports in the JSP file show the Cloud Datastore model classes and the required Objectify classes:

<%@ page import="com.example.guestbook.Greeting" %>
<%@ page import="com.example.guestbook.Guestbook" %>
<%@ page import="com.googlecode.objectify.Key" %>
<%@ page import="com.googlecode.objectify.ObjectifyService" %>

Just above the line <form action="/guestbook.jsp" method="get">, notice the following code: