Google App Engine + JDO + Spring MVC, CRUD example
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 ==”[email protected]”.
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", "[email protected]");
//...
} 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.
- Google App Engine Java SDK 1.6.3.1, JDO 2.3
- Spring 3.1.1
- JDK 1.6
- 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.xml
for you automatically.
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<Customer> customers =
(List<Customer>)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
2. In listing page, click on the “Add Customer” link to display the add customer page, fill in a new customer, name = “mkyong“, email = “[email protected]” and click on the “Add” button.
URL : http://localhost:8888/customer/add
3. Saved the customer, it will return back to the listing page.
URL : http://localhost:8888/customer/list
4. Try update link, it will display the selected customer’s data, update email to “[email protected]“, and click on the update button.
URL : http://localhost:8888/customer/update/mkyong
5. The email is updated, and redirect back to listing page.
URL : http://localhost:8888/customer/list
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.
Hi am new to GAE, I have tried this example, Its working without giving any exceptions. But am not getting Customer list which i am adding. Can any one tell me how to test the weather data is persisted or not and where can we find the Table which get created
Hello, I want write method getById() but error, help me!
@Override
public MonHoc getById(String tenMonHoc) {
MonHoc mh = new MonHoc();
PersistenceManager pm = PMF.get().getPersistenceManager();
javax.jdo.Query q = pm.newQuery(MonHoc.class);
q.setFilter(“tenMonHoc == tenMonHocParameter”);
q.setOrdering(“tenMonHoc desc”);
q.declareParameters(“String tenMonHocParameter”);
List results = null;
try {
results = (List) q.execute(tenMonHoc);
if (!results.isEmpty()) {
for (MonHoc p : results) {
p.getTenMonHoc();
p.getHeSo();
p.getSoTiet();
}
}
} finally {
q.closeAll();
pm.close();
}
return (MonHoc) results.get(0);
}
“com.xxx.model.customer Persistent class does not seem to have been enhanced.You may want to rerun the enhancer and check for errors in the output”.
has no table in the database, but the operation requires it. Please check the specification of the MetaData for this class.
javax.jdo.JDOUserException: Persistent class “Class com.pandian.model.Customer does not seem to have been enhanced. You may want to rerun the enhancer and check for errors in the output.” has no table in the database, but the operation requires it. Please check the specification of the MetaData for this class.
your suggestion and discussion are warm welcome.
Kindly input your text as reply.
i get the error 503 service SERVICE_UNAVAILABLE when when I accede to the url
someone has an idea ??
GAE 1.7.6 spring 3.2.0
thanks !
Hi,
I am a frequent visitor to your website and have learnt many interesting things from here.
I guess, it would be very useful if you create the same tutorial on Spring + App Engine + CRUD using Spring’s awesome feature of Form Backing Bean. This will help us get rid of all the code that processes the Request object in Controller to fetch form’s input values as well as those boring Scriptlets and Expressions.
Thanks.
Hi ,
I followed your tutorial , ADD , UPDATE and LISTING are fine but whhen i click delete i get the below error . please help me how if i have to cofigure something for this
/customer/delete/ahJzfmtpdGUtc3J0c2VydmljZXNyDgsSCEN1c3RvbWVyGAEM
javax.jdo.JDOObjectNotFoundException: Could not retrieve entity of kind Customer with key Customer(1)
at org.datanucleus.jdo.NucleusJDOHelper.getJDOExceptionForNucleusException(NucleusJDOHelper.java:331)
at org.datanucleus.jdo.JDOPersistenceManager.getObjectById(JDOPersistenceManager.java:1676)
at org.datanucleus.jdo.JDOPersistenceManager.getObjectById(JDOPersistenceManager.java:1767)
at org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManager.getObjectById(DatastoreJDOPersistenceManager.java:73)
at com.mkyong.controller.CustomerController.delete(CustomerController.java:115)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:43)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
at
Hi,
I have added one customer,but then when redirecting to list page,I can’t see it,It happend when I pressed f5 ,sometime have sometime not ,must to refesh the page,please help me clear it.Could I not refesh pages??
Hello Sir. You are a Java Genius.
Hi,
when i am running these application.Its show’s me an error.
http://localhost:8888/customer/list
http://pastebin.com/h7aRu77u
I just cant figure out…one days has been past.
Great work by you… sir
Hey Mr MKYONG, can you make a tutorial about using the org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy??
I the Spring Documentations they said this way is better over your way because only 1 thread is used but i just can’t get it to work properly.
Great tutorial. Please helpme I’m working woth RPC (google services), and JPA and I have an entity
Country City
———— ————
Long id Long id
String name String name
@ManyToOne
Country country
I’m persisting a city and I have this implementation
em.getTransaction().begin();
Country c = em.fin(Country.class, id)
myCity.setCountry(c);
em.persist(miCity);
em.getTransaction().commit();
and I have the error
22/06/2012 04:34:49 PM com.google.apphosting.utils.servlet.TransactionCleanupFilter handleAbandonedTxns
WARNING: Request completed without committing or rolling back transaction with id 3. Transaction will be rolled back.
ec.edu.udla.shared.AppException: Ocurrio un error en el sistema, por favor intente mas tarde
at ec.edu.udla.shared.AppException_FieldSerializer.instantiate(AppException_FieldSerializer.java:16)
at ec.edu.udla.shared.AppException_FieldSerializer.create(AppException_FieldSerializer.java:25)
at com.google.gwt.user.client.rpc.impl.SerializerBase.instantiate(SerializerBase.java:115)
at com.google.gwt.user.client.rpc.impl.ClientSerializationStreamReader.deserialize(ClientSerializationStreamReader.java:111)
at com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamReader.readObject(AbstractSerializationStreamReader.java:119)
at com.google.gwt.user.client.rpc.impl.RequestCallbackAdapter.onResponseReceived(RequestCallbackAdapter.java:216)
at com.google.gwt.http.client.Request.fireOnResponseReceived(Request.java:287)
at com.google.gwt.http.client.RequestBuilder$1.onReadyStateChange(RequestBuilder.java:395)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:172)
at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessagesWhileWaitingForReturn(BrowserChannelServer.java:337)
at com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:218)
at com.google.gwt.dev.shell.ModuleSpaceOOPHM.doInvoke(ModuleSpaceOOPHM.java:136)
at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:561)
at com.google.gwt.dev.shell.ModuleSpace.invokeNativeObject(ModuleSpace.java:269)
at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeObject(JavaScriptHost.java:91)
at com.google.gwt.core.client.impl.Impl.apply(Impl.java)
at com.google.gwt.core.client.impl.Impl.entry0(Impl.java:213)
at sun.reflect.GeneratedMethodAccessor29.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:172)
at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessages(BrowserChannelServer.java:292)
at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:546)
at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:363)
at java.lang.Thread.run(Thread.java:662)
Please help me…advance thanks
Rene did you resolver this problem? I’m getting the same error….