Friday, May 10, 2013

Cloud Endpoints + JPA: failure in checking if entity exists

After defining some entity classes to be used by JPA for persisting data in Google App Engine I auto-generated some Cloud Endpoint classes (for Google Cloud Endpoints), via the Google App Engine SDK, to be used in an Android application. However, it turned out to be a sad out-of-the-box experience due to new entity instances having a null ID.

Before proceeding, here's the testing entity code (excluding imports):
@Entity
public class Testo {
 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 private Long ID;
 private String description;
 
 public Long getID() {
  return ID;
 }
 public void setID(Long ID) {
  this.ID = ID;
 }
 public String getDescription() {
  return description;
 }
 public void setDescription(String description) {
  this.description = description;
 }
}

A really simple one.

To test the entity and the endpoint classes, I auto-generated the Cloud Endpoint Client Library and made use of it in an Android testing application (Endpoints Backend enabled). The application would only create a new entity, set its attributes to some value, and then request to insert that entity into the datastore, using Google Cloud Endpoints in the middle.

After testing it up, though, I got a big stack trace from the Google development server due to a NullPointerException that occurred when checking if that entity existed in the datastore.

The exception referred to the fact that no entity ID was set. So, if new entities don't have an ID, how can they be checked for existence requiring a valid ID?

The point of failure
(at least in my specific case)

The endpoint class TestoEndpoint.java, auto-generated method: containsTesto(Testo testo):
private boolean containsTesto(Testo testo) {
 EntityManager mgr = getEntityManager(); 
 boolean contains = true;
 try {
  Testo item = mgr.find(Testo.class, testo.getID());
  if (item == null) {
   contains = false;
  }
 } finally {
  mgr.close();
 }
 return contains;
}

This method makes use of the Entity Manager's find() method, which in this context takes a null ID and crashes, not even being able to tell if the datastore contains or not that entity.

What I changed

To work around this annoying problem I added a null check for the entity argument:
private boolean containsTesto(Testo testo) {
 EntityManager mgr = getEntityManager(); 
 boolean contains = true;
 try {
  // If no ID was set, the entity doesn't exist yet.
  if(testo.getID() == null)
   return false;
  Testo item = mgr.find(Testo.class, testo.getID());
  if (item == null) {
   contains = false;
  }
 } finally {
  mgr.close();
 }
 return contains;
}

Some links: