Note
This tutorial is more on practice guide, please refer to this official Using JDO in datastore for detail explanation.
See following code snippets to perform CRUD on GAE datastore, using Java Data Objects(JDO). Just annotate the Customer with JDO annotation and perform the CRUD via PersistenceManager.

Add

        Customer c = new Customer();
 c.setName(name);
 
 PersistenceManager pm = PMF.get().getPersistenceManager();
 try {
  pm.makePersistent(c);
 } finally {
  pm.close();
 }

Search

Search “Customer” where name == “mkyong”.
 PersistenceManager pm = PMF.get().getPersistenceManager();
 
 Query q = pm.newQuery(Customer.class);
 q.setFilter("name == nameParameter");
 q.setOrdering("date desc");
 q.declareParameters("String nameParameter");
 
 try {
  List<Customer> results = (List<Customer>) q.execute("mkyong");
  //...
 } finally {
  q.closeAll();
  pm.close();
 }
Search “Customer” where name == “mkyong” and email ==”test@gmail.com”.
 Query q = pm.newQuery(Customer.class);
 q.setOrdering("date desc");
 q.setFilter("name == nameParameter && email == emailParameter");
 q.declareParameters("String nameParameter, String emailParameter");
 
 try {
  List<Customer> results = (List<Customer>) q.execute("mkyong", "test@gmail.com");
  //...
 } finally {
  q.closeAll();
  pm.close();
 }
Return list of customer records.
 PersistenceManager pm = PMF.get().getPersistenceManager();
 Query q = pm.newQuery(Customer.class);
 q.setOrdering("date desc");
 List<Customer> results = null;
 
 try {
  results = (List<Customer>) q.execute();
  if (!results.isEmpty()) {
   // good for listing
  } 
 } finally {
  q.closeAll();
  pm.close();
 }

Update

To update, get existing object and modify it.
 PersistenceManager pm = PMF.get().getPersistenceManager();
 try {
  Customer c = pm.getObjectById(Customer.class, key);
  c.setName(name);
  c.setEmail(email);
  c.setDate(new Date());
 } finally {
  pm.close();
 }

Delete

        PersistenceManager pm = PMF.get().getPersistenceManager();
 try {
  Customer c = pm.getObjectById(Customer.class, key);
  pm.deletePersistent(c);
 } finally {
  pm.close();
 }

GAE + Spring MVC + CRUD example

Ok, now we will show you a simple web application developed using Spring MVC in REST style, using JDO to store data in datastore.
  1. Google App Engine Java SDK 1.6.3.1, JDO 2.3
  2. Spring 3.1.1
  3. JDK 1.6
  4. Eclipse 3.7 + Google Plugin for Eclipse
P.S Use Google Plugin for Eclipse to create a web application project template, it will create and configure jdoconfig.xmlfor you automatically.
Note
This example is keep into as simple as possible, to show you how to use JDO to perform CRUD only, no layers like DAO or BO, no validation or message notification of the success or failed action.

1. Customer

Annotate Customer object with JDO annotation.
package com.mkyong.model;
 
import java.util.Date;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import com.google.appengine.api.datastore.Key;
 
@PersistenceCapable
public class Customer {
 
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;
 
    @Persistent
    private String name;
 
    @Persistent
    private String email;
 
    @Persistent
    private Date date;
 
    //getter and setter methods
}

2. PersistenceManager

Create a singleton PersistenceManager class.
package com.mkyong;
 
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;
 
public final class PMF {
 private static final PersistenceManagerFactory pmfInstance = JDOHelper
  .getPersistenceManagerFactory("transactions-optional");
 
 private PMF() {
 }
 
 public static PersistenceManagerFactory get() {
  return pmfInstance;
 }
}

3. Spring Controller

Spring controller, REST style, perform the CRUD operation. The code should be self-explanatory.
File : CustomerController.java
package com.mkyong.controller;
 
import java.util.Date;
import java.util.List;
 
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.servlet.http.HttpServletRequest;
 
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import com.mkyong.PMF;
import com.mkyong.model.Customer;
 
@Controller
@RequestMapping("/customer")
public class CustomerController {
 
 @RequestMapping(value = "/add", method = RequestMethod.GET)
 public String getAddCustomerPage(ModelMap model) {
 
  return "add";
 
 }
 
 @RequestMapping(value = "/add", method = RequestMethod.POST)
 public ModelAndView add(HttpServletRequest request, ModelMap model) {
 
  String name = request.getParameter("name");
  String email = request.getParameter("email");
 
  Customer c = new Customer();
  c.setName(name);
  c.setEmail(email);
  c.setDate(new Date());
 
  PersistenceManager pm = PMF.get().getPersistenceManager();
  try {
   pm.makePersistent(c);
  } finally {
   pm.close();
  }
 
  return new ModelAndView("redirect:list");
 
 }
 
 @RequestMapping(value = "/update/{name}", method = RequestMethod.GET)
 public String getUpdateCustomerPage(@PathVariable String name,
  HttpServletRequest request, ModelMap model) {
 
  PersistenceManager pm = PMF.get().getPersistenceManager();
 
  Query q = pm.newQuery(Customer.class);
  q.setFilter("name == nameParameter");
  q.setOrdering("date desc");
  q.declareParameters("String nameParameter");
 
  try {
   List<Customer> results = (List<Customer>) q.execute(name);
 
   if (results.isEmpty()) {
    model.addAttribute("customer", null);
   } else {
    model.addAttribute("customer", results.get(0));
   }
  } finally {
   q.closeAll();
   pm.close();
  }
 
  return "update";
 
 }
 
 @RequestMapping(value = "/update", method = RequestMethod.POST)
 public ModelAndView update(HttpServletRequest request, ModelMap model) {
 
  String name = request.getParameter("name");
  String email = request.getParameter("email");
  String key = request.getParameter("key");
 
  PersistenceManager pm = PMF.get().getPersistenceManager();
 
  try {
 
   Customer c = pm.getObjectById(Customer.class, key);
 
   c.setName(name);
   c.setEmail(email);
   c.setDate(new Date());
 
  } finally {
 
   pm.close();
  }
 
  // return to list
  return new ModelAndView("redirect:list");
 
 }
 
 @RequestMapping(value = "/delete/{key}", method = RequestMethod.GET)
 public ModelAndView delete(@PathVariable String key,
   HttpServletRequest request, ModelMap model) {
 
  PersistenceManager pm = PMF.get().getPersistenceManager();
 
  try {
 
   Customer c = pm.getObjectById(Customer.class, key);
   pm.deletePersistent(c);
 
  } finally {
   pm.close();
  }
 
  // return to list
  return new ModelAndView("redirect:../list");
 
 }
 
 // get all customers
 @RequestMapping(value = "/list", method = RequestMethod.GET)
 public String listCustomer(ModelMap model) {
 
  PersistenceManager pm = PMF.get().getPersistenceManager();
  Query q = pm.newQuery(Customer.class);
  q.setOrdering("date desc");
 
  List<Customer> results = null;
 
  try {
   results = (List<Customer>) q.execute();
 
   if (results.isEmpty()) {
    model.addAttribute("customerList", null);
   } else {
    model.addAttribute("customerList", results);
   }
 
  } finally {
   q.closeAll();
   pm.close();
  }
 
  return "list";
 
 }
 
}

4. JSP Pages

JSP pages to display customer and perform add, update and delete.
File : list.jsp
<%@ page import="java.util.List" %>
<%@ page import="com.mkyong.model.Customer" %>
<%@ page import="com.google.appengine.api.datastore.KeyFactory" %>
<html>
<body>
 <h1>GAE + Spring 3 MVC REST + CRUD Example with JDO</h1>
 
 Function : <a href="add">Add Customer</a>
 <hr />
 
 <h2>All Customers</h2>
 <table border="1">
  <thead>
   <tr>
    <td>Name</td>
    <td>Email</td>
    <td>Created Date</td>
    <td>Action</td>
   </tr>
  </thead>
 
  <%
 
  if(request.getAttribute("customerList")!=null){
 
   List customers = 
                           (List)request.getAttribute("customerList");
 
   if(!customers.isEmpty()){
     for(Customer c : customers){
 
  %>
    <tr>
      <td><%=c.getName() %></td>
      <td><%=c.getEmail() %></td>
      <td><%=c.getDate() %></td>
      <td><a href="update/<%=c.getName()%>">Update</a> | 
                                      <a href="delete/<%=KeyFactory.keyToString(c.getKey()) %>">
                                       Delete</a>
                                  </td>
    </tr>
  <% 
 
    }
 
   }
 
     }
  %>
 
        </tr>
 
 </table>
 
</body>
</html>
File : add.jsp
<html>
<body>
 <h1>Add Customer</h1>
 
 <form method="post" action="add">
  <table>
   <tr>
    <td>UserName :</td>
    <td><input type="text" style="width: 185px;" maxlength="30"
     name="name" id="name" /></span></td>
   </tr>
   <tr>
    <td>Email :</td>
    <td><input type="text" style="width: 185px;" maxlength="30"
     name="email" id="email" /></span></td>
   </tr>
  </table>
  <input type="submit" class="save" title="Save" value="Save" />
 </form>
 
</body>
</html>
File : update.jsp
<%@ page import="com.mkyong.model.Customer" %>
<%@ page import="com.google.appengine.api.datastore.KeyFactory" %>
<html>
<body>
 <h1>Update Customer</h1>
 
 <%
  Customer customer = new Customer();
 
  if(request.getAttribute("customer")!=null){
 
   customer = (Customer)request.getAttribute("customer");
 
  }
 
 %>
 
 <form method="post" action="../update" >
  <input type="hidden" name="key" id="key" 
   value="<%=KeyFactory.keyToString(customer.getKey()) %>" /> 
 
  <table>
   <tr>
    <td>
     UserName :
    </td>
    <td>
     <input type="text" style="width: 185px;" 
                                             maxlength="30" name="name" id="name" 
      value="<%=customer.getName() %>" />
    </td>
   </tr>
   <tr>
    <td>
     Email :
    </td>
    <td>
     <input type="text" style="width: 185px;" 
                                             maxlength="30" name="email" id="email" 
      value="<%=customer.getEmail() %>" />
    </td>
   </tr>
  </table>
  <input type="submit" class="update" title="Update" value="Update" />
 </form>
 
</body>
</html>

5. Demo

Done, see demo to show you the workflow of the web application.
1. A listing page, to display list of existing customers.
URL : http://localhost:8888/customer/list
gar jdo example
2. In listing page, click on the “Add Customer” link to display the add customer page, fill in a new customer, name = “mkyong“, email = “test@yahoo.com” and click on the “Add” button.
URL : http://localhost:8888/customer/add
gar jdo example
3. Saved the customer, it will return back to the listing page.
URL : http://localhost:8888/customer/list
gar jdo example
4. Try update link, it will display the selected customer’s data, update email to “mkyong@yahoo.com“, and click on the update button.
URL : http://localhost:8888/customer/update/mkyong
gar jdo example
5. The email is updated, and redirect back to listing page.
URL : http://localhost:8888/customer/list
gar jdo example
6. To delete the customer, just click on the “delete” link.

Download Source Code

Due to large file size, all Spring MVC and GAE jars are excluded.
Download – GAE-SpringMVC-JDO-example.zip (22 KB)

References

  1. Java Data Objects(JDO)
  2. GAE : Using JDO
  3. GAE : datastore low-level api
  4. GAE : CRUD Operations with JDO and Spring MVC