Tutorial: JPA on Google App Engine

This project will create and delete an entity record. I have chosen JPA for datastore in app engine. You can also look JDO for this purpose. But my choice to go for JPA is due to Sun standardization on it.

I am using App Engine SDK version 1.2.18

Project name: engineplay
URL: http://engineplay1.appspot.com

Project structure:

jpa_gae_project_structureUserPrefs.java: The entity class

package com.engineplay.datastore.pojos;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.Id;
import com.engineplay.datastore.EMFService;
import com.google.appengine.api.users.User;

@Entity(name = "UserPrefs")
public class UserPrefs {
 @Id
 private String userId;
 private int donuts;
 @Basic
 private User user;

 public UserPrefs(String userId) {
 this.userId = userId;
 }

 /* TODO create getter and setters for above properties yourself */

 public static UserPrefs getPrefsForUser(User user) {
 UserPrefs userPrefs = null;
 EntityManager em = EMFService.get().createEntityManager();
 try {
 if (em.find(UserPrefs.class, user.getEmail()) == null) {

 userPrefs = new UserPrefs(user.getEmail());
 userPrefs.setUser(user);

 } else {
 userPrefs = em.find(UserPrefs.class, user.getEmail());
 }
 } finally {
 em.close();
 }
 return userPrefs;
 }

 public void save() {
 EntityManager em = EMFService.get().createEntityManager();
 try {
 em.persist(this);
 } finally {
 em.close();
 }
 }

 public void remove() {
 EntityManager em = EMFService.get().createEntityManager();
 try {
 UserPrefs userPrefs = em.find(UserPrefs.class, user.getEmail());
 em.remove(userPrefs);
 } finally {
 em.close();
 }
 }
}

EMFService.java: The class to create EntityManagerFactory instance.

package com.engineplay.datastore;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class EMFService {
 private static final EntityManagerFactory emfInstance = Persistence
 .createEntityManagerFactory("transactions-optional");

 private EMFService() {
 }

 public static EntityManagerFactory get() {
 return emfInstance;
 }
}

LoginServlet.java

package com.engineplay.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.engineplay.datastore.pojos.UserPrefs;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;

public class LoginServlet extends HttpServlet {

 public void doGet(HttpServletRequest req, HttpServletResponse resp)
 throws IOException {

 UserService userService = UserServiceFactory.getUserService();
 User user = userService.getCurrentUser();
 String navBar, form = null;
 int donuts = 0;
 if (user == null) {
 navBar = "<p>Welcome! <a href=\"" + userService.createLoginURL("/") +
 "\">Sign in or register</a> to customize.</p>";
 form = "";
 } else {
 UserPrefs userPrefs = UserPrefs.getPrefsForUser(user);

 if (userPrefs != null) {
 donuts = userPrefs.getDonuts();
 }
 navBar = "<p>Welcome, " + user.getEmail() + "! You can <a href=\"" +
 userService.createLogoutURL("/") +
 "\">sign out</a>.</p>";
 form = "<form action=\"/donuts\" method=\"post\">" +
 "<label for=\"donuts\">" +
 "Need more donuts?:" +
 "</label>" +
 "<input name=\"donuts\" id=\"donuts\" type=\"text\" size=\"4\" />" +
 " <input name=\"olddonuts\"  type=\"hidden\" value=\"" + donuts + "\" /> " +
 " <input name=\"userId\"  type=\"hidden\" value=\"" +user.getEmail()+ "\" /> " +
 "   <input type=\"submit\" value=\"  ADD  \" /><br><input type=\"submit\"  name =\"deleteBtn\" value=\"  DELETE ME!  \" />" +
 "</form>";

 }
 resp.setContentType("text/html");
 PrintWriter out = resp.getWriter();
 out.print("<html><head><title>Engine Play - Donuts</title</head><body>");
 out.println(navBar);
 if (donuts != 0)
 out.println("<p>Donuts you have: " + donuts + "</p>");
 else
 out.println("<p> No donuts you have :( </p>");
 out.println(form);
 out.print("</body></html>");
 }
}

DonutsServlet.java

package com.engineplay.servlet;

import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.engineplay.datastore.pojos.UserPrefs;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;

public class DonutsServlet extends HttpServlet {

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

 String deleteOpt = req.getParameter("deleteBtn");
 if (deleteOpt == null){
 int donuts = new Integer(req.getParameter("donuts")).intValue();
 int oldDonuts = new Integer(req.getParameter("olddonuts")).intValue();

 userPrefs.setDonuts(donuts + oldDonuts);
 userPrefs.save();
 }else{
 String userId = req.getParameter("userId");
 userPrefs.setUserId(userId);
 userPrefs.remove();
 }
 } catch (Exception e) {
 e.printStackTrace();
 }
 resp.sendRedirect("/Login");
 }
}

persistence.xml: Configuration file required for JPA

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
 version="1.0">
 <persistence-unit name="transactions-optional">
 <provider>
 org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider
 </provider>
 <properties>
 <property name="datanucleus.NontransactionalRead"
 value="true" />
 <property name="datanucleus.NontransactionalWrite"
 value="true" />
 <property name="datanucleus.ConnectionURL"
 value="appengine" />
 </properties>
 </persistence-unit>
</persistence>

web.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">

 <servlet>
 <servlet-name>LoginServlet</servlet-name>
 <servlet-class>com.engineplay.servlet.LoginServlet</servlet-class>
 </servlet>
 <servlet>
 <servlet-name>DonutsServlet</servlet-name>
 <servlet-class>com.engineplay.servlet.DonutsServlet</servlet-class>
 </servlet>

 <servlet-mapping>
 <servlet-name>LoginServlet</servlet-name>
 <url-pattern>/Login</url-pattern>
 </servlet-mapping>
 <servlet-mapping>
 <servlet-name>DonutsServlet</servlet-name>
 <url-pattern>/donuts</url-pattern>
 </servlet-mapping>

 <welcome-file-list>
 <welcome-file>Login</welcome-file>
 </welcome-file-list>
</web-app>

Access the project here: http://engineplay1.appspot.com

 

You can access the code from Github as well.

You May Also Like

12 Comments

  1. Tahir,

    Nice tutorial. I started out with app engine using the JPA/JDO but then found the low level API to be just as easy. I then disabled the JDO/JPA which allows the project to deploy faster. Have you found a significant advantage of using the JDO/JPA over the low level API?

  2. Coloma;

    I just take a look on the page. Sounds interesting if its easy to use over JPA and JDO. As JPA and SimpleDS both are new for me so learning core JPA will be better investment for me.

    Thanks for your comments.

  3. @Robert Lancer:

    There is a significat adventage of using JPA over the low level api – you can move your application from Google and deploy it somewhere else, and to do that you only have mappings (probably) – you don’t have to change the code. Using low level api you depends on google – to move your application somewhere else you have to change a bunch of code.

    Best regards
    Marek

Leave a Reply to Ramya Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.