CS6320:  SW Engineering of Web Based Systems

 

GAE Datastore using Native Low Level DataStore API (in Java)

Clearing the Datastore on LocalHost ---running on LocalHost

The development web server uses a local version of the Datastore for testing your application, using local files. The data persists as long as the temporary files exist, and the web server does not reset these files unless you ask it to do so.

The file is named local_db.bin, and it is created in your application's WAR directory, in the WEB-INF/appengine-generated/ directory. To clear the Datastore, delete this file.

How to get DataStore and put Entity in it

STEP 1: Create instance of DataStore (ds) for this application      
DatastoreService ds = DatastoreServiceFactory.getDatastoreService();

STEP 2: Create instance of Entity called book     

Entity book = new Entity("Book");

STEP 3: Set various properties of book Entity with name, value pairs   

  book.set*(*);

STEP 4: Add Entity instance book to the DataStore ds

ds.put(book);

 

 

How to get stored data (use Query and datastore.prepare(query)

Query query = new Query("Greeting", guestbookKey).addSort("date", Query.SortDirection.DESCENDING);
List<Entity> greetings = datastore.prepare(query).asList(FetchOptions.Builder.withLimit(5));

  • creates a new query on the Greeting entity
  • sets the guestbookKey as the required parent entity for all entities that will be returned.
  • sort on the date property
  • return the newest Greeting first.
  • query results returned as a list of Entity objects

see the Datastore reference.

 

 

Example cyling through results

// Get the Datastore Service
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

// The Query interface assembles a query
Query q = new Query("Person");
q.addFilter("lastName", Query.FilterOperator.EQUAL, lastNameParam);
q.addFilter("height", Query.FilterOperator.LESS_THAN, maxHeightParam);


// PreparedQuery contains the methods for fetching query results
// from the datastore
PreparedQuery pq = datastore.prepare(q);
//cycle through results
for (Entity result : pq.asIterable()) {
  String firstName = (String) result.getProperty("firstName");
  String lastName = (String) result.getProperty("lastName");
  Long height = (Long) result.getProperty("height");
  System.out.println(firstName + " " + lastName + ", " + height + " inches tall");
}

 

 

GAE Datastore Indexes

 

  • Every query is computed from one or more indexes.

  • Indexes are tables that map ordered property values to entity keys.

  • This is how App Engine is able to serve results quickly regardless of the size of your application's Datastore. Many queries can be computed from the builtin indexes, but the Datastore requires you to specify a custom index for some, more complex, queries. Without a custom index, the Datastore can't execute the query efficiently.

Our guest book example below, which filters results by ancestor and orders by date, uses an ancestor query and a sort order.
indexes file
This query requires a custom index to be specified in your application's
war/WEB-INF/datastore-indexes.xml file
.

 


When you run your application in the SDK, it will automatically add an entry to this file.

When you upload your application, the custom index definition will be automatically uploaded, too.

The entry for this query will look like:

<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes
  autoGenerate="true">
    <datastore-index kind="Greeting" ancestor="true">
        <property name="date" direction="desc" />
    </datastore-index>
</datastore-indexes>

You can read all about Datastore indexes in the Introduction to Indexes section.

 



 

Example 1 -- Book database -- ONLY storage of book Entity

(remember you can also access with higher level API like JPA and JDO)         

package booklowlevel;
import java.io.IOException; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.SimpleTimeZone; import javax.servlet.http.*; import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.DatastoreServiceFactory; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.KeyFactory;


@SuppressWarnings("serial") public class GAE_booklowlevelServlet extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse resp)throws IOException { resp.setContentType("text/html"); PrintWriter out = resp.getWriter(); SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSSSSS"); //STEP 1: Create Datastoreservice
DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
//STEP 2: Create Entity Entity book = new Entity("Book"); //STEP 3: setup properties of Entity
book.setProperty("title", "The Grapes of Wrath"); book.setProperty("author", "John Steinbeck"); book.setProperty("copyrightYear", 1939); Date authorBirthdate = new GregorianCalendar(1902, Calendar.FEBRUARY, 27).getTime(); book.setProperty("authorBirthdate", authorBirthdate); //STEP 4: put Entity in DataStore
ds.put(book);
out.println("<p>Added a Book entity to the datastore via the low-level API, key: " + KeyFactory.keyToString(book.getKey()) + "</p>");
fmt.setTimeZone(new SimpleTimeZone(0, "")); out.println("<p>The time is: " + fmt.format(new Date()) + "</p>"); } }


result of running above code (locally)

run locally book datastore app



 

Example 2 --- Guestbook DataStore storage(user,date,content) AND retrieval

 

package guestbook;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;

import java.io.IOException;
import java.util.Date;
import java.util.logging.Logger;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SignGuestbookServlet extends HttpServlet {
    private static final Logger log =
            Logger.getLogger(SignGuestbookServlet.class.getName());

    public void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        UserService userService = UserServiceFactory.getUserService();
        User user = userService.getCurrentUser();

        // We have one entity group per Guestbook with all Greetings residing
        // in the same entity group as the Guestbook to which they belong.
        // This lets us run an ancestor query to retrieve all Greetings for a
        // given Guestbook. However, the write rate to each Guestbook should be
        // limited to ~1/second.
        String guestbookName = req.getParameter("guestbookName");
        Key guestbookKey = KeyFactory.createKey("Guestbook", guestbookName);
        String content = req.getParameter("content");
        Date date = new Date(); //STEP 2&3: create Entity and set properties
        Entity greeting = new Entity("Greeting", guestbookKey);
        greeting.setProperty("user", user); //storing for Greeting Entity - user,date,content --user passed by authentication,
        greeting.setProperty("date", date); //date is generated above
        greeting.setProperty("content", content); //content is from request data
//STEP 1: get DataStore
        DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); //STEP 4: put Entity in DataStore
        datastore.put(greeting);

        resp.sendRedirect("/guestbook.jsp?guestbookName=" + guestbookName);
    }
}

 

guestbook.jsp contains how to get stored data      ALSO contains form that will call servlet passing date and content info

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.util.List" %>
<%@ page import="com.google.appengine.api.users.User" %>
<%@ page import="com.google.appengine.api.users.UserService" %>
<%@ page import="com.google.appengine.api.users.UserServiceFactory" %>
<%@ page import="com.google.appengine.api.datastore.DatastoreServiceFactory" %>
<%@ page import="com.google.appengine.api.datastore.DatastoreService" %>
<%@ page import="com.google.appengine.api.datastore.Query" %>
<%@ page import="com.google.appengine.api.datastore.Entity" %>
<%@ page import="com.google.appengine.api.datastore.FetchOptions" %>
<%@ page import="com.google.appengine.api.datastore.Key" %>
<%@ page import="com.google.appengine.api.datastore.KeyFactory" %>

<html>
  <head>
    <link type="text/css" rel="stylesheet" href="/stylesheets/main.css" />
  </head>

  <body>

<%
    String guestbookName = request.getParameter("guestbookName");
    if (guestbookName == null) {
        guestbookName = "default";
    }
    UserService userService = UserServiceFactory.getUserService();
    User user = userService.getCurrentUser();
    if (user != null) {
%>
<p>Hello, <%= user.getNickname() %>! (You can
<a href="<%= userService.createLogoutURL(request.getRequestURI()) %>">sign out</a>.)</p>
<%
    } else {
%>
<p>Hello!
<a href="<%= userService.createLoginURL(request.getRequestURI()) %>">Sign in</a>
to include your name with greetings you post.</p>
<%
    }
%>

<%
    DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
    Key guestbookKey = KeyFactory.createKey("Guestbook", guestbookName);
    // Run an ancestor query to ensure we see the most up-to-date
    // view of the Greetings belonging to the selected Guestbook.
    Query query = new Query("Greeting", guestbookKey).addSort("date", Query.SortDirection.DESCENDING);
    List<Entity> greetings = datastore.prepare(query).asList(FetchOptions.Builder.withLimit(5));

    if (greetings.isEmpty()) {
        %>
        <p>Guestbook '<%= guestbookName %>' has no messages.</p>
        <%
    } else {
        %>
        <p>Messages in Guestbook '<%= guestbookName %>'.</p>
        <%
        for (Entity greeting : greetings) {
            if (greeting.getProperty("user") == null) {
                %>
                <p>An anonymous person wrote:</p>
                <%
            } else {
                %>
                <p><b><%= ((User) greeting.getProperty("user")).getNickname() %></b> wrote:</p>
                <%
            }
            %>
            <blockquote><%= greeting.getProperty("content") %></blockquote>
            <%
        }

    }
%>

    <form action="/sign" method="post">
      <div><textarea name="content" rows="3" cols="60"></textarea></div>
      <div><input type="submit" value="Post Greeting" /></div>
      <input type="hidden" name="guestbookName" value="<%= guestbookName %>"/>
    </form>

  </body>
</html>

 

 

© Lynne Grewe