Main Tutorials

Google App Engine + JDO + Spring MVC, CRUD example

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 ==”[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.

  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.xml for 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<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

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 = “[email protected]” 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 “[email protected]“, 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

About Author

author image
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter. If you like my tutorials, consider make a donation to these charities.

Comments

Subscribe
Notify of
13 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
Abhishek
7 years ago

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

Nguy?n V?n Nam
9 years ago

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);
}

Peter
10 years ago

“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.

mustapha
11 years ago

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 !

JavaCoder
11 years ago

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.

yowan
11 years ago

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

tan nguyen
11 years ago

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??

Ankur Jain
11 years ago

Hello Sir. You are a Java Genius.

Arun Pandian
10 years ago
Reply to  Ankur Jain

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.

msikwal
11 years ago

Great work by you… sir

Jim
11 years ago

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.

Rene
11 years ago

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

viny
11 years ago
Reply to  Rene

Rene did you resolver this problem? I’m getting the same error….