Monday, July 28, 2014

Data Persistence With AppEngine

When you are using AppEngine, there are a handful of choices to store your data.  Once Google introduced Cloud SQL the choices just increased.  How can you figure out which framework to use?

I'm a pretty big fan of the Cloud Storage so that limited my choices just a bit - but I still had a choice between JDO or JPA.  How and why should you decide one of these frameworks over the other?  I'm sure a true J2EE aficionado can tell you which is the better choice and why under which circumstances depending on which app server and database you're using.  I do know that I've tried to shoe horn both of these technologies into AppEngine and found it to be very cumbersome.  To be honest I wasn't looking forward to fighting through it once again.  As I was researching which one to chose I ran across a very amusing quote at the Objectify website: https://code.google.com/p/objectify-appengine/
The quote from the page is:
"After you've banged your head against JDO and screamed "Why, Google, why??" enough times..."

I decided to play around with Objectify to see how hard or easy it was, and turns out it was super easy.  I started with the latest version Objectify 5.  It is pretty new, most of the info you'll find on the internet is for the older versions.  The latest is a tad more straightforward and arguably easier to understand and use.

Here are the basics:
When you create a new domain object you must annotate two things:

  1. The @Entity annotation on the class itself
  2. The Id annotation on a variable to be used as the @Id
  3. An Optional annotation is there if you want to search by a particular field @Index - this actually took me a little while to figure out
Once you have your domain object properly annotated (the annotations are all in the 

com.googlecode.objectify.annotation package) you need to access them somehow.  Objectify supports my favorite pattern really well - I'm not sure if it has an official name but I really like to have all of my database access in a single class.  It may sound a tad horrible but if you keep the business logic out of it and you are just creating, reading, updating and deleting stuff around it doesn't usually get too unmanageable.  It makes maintenance and testing very easy - and God forbid you ever have to change your persistence framework you can keep your changes all in one place.

To use objectify there are two things you need to have: a Factory and a Service.  This is how it is recommended to get access to them:

private static Objectify service()
{
   return ObjectifyService.ofy();
}
private static ObjectifyFactory factory()
{
   return ObjectifyService.factory();
}

Once you have those in place the next thing you need to do is register your annotated domain objects:
static 
{
   factory().register(User.class);
   factory().register(Group.class);
}

Saving is as easy as this:
public static void saveUser(User user)
{
   Result> result = service().save().entity(user);
   result.now();
}

If the Object you are saving has an Id populated the save becomes an update.

The now method on the result is a flag tells the system we want to wait for the save to finish - if you leave that off it will still save but whenever the system gets around to it.


Searching looks like this (remember if you're searching a field it has to have the index annotation):
public static MobileUser findMobileUserByEmail(String email)
{
     List< User > users = service().load().type(User.class).filter("email", email).list();
     User mu = null;
     if (users.size() > 0)
     {
          mu = users.get(0);
     }
     return mu;
}

And lastly Deleting looks like this:
public static void deleteUser(User ) 
{
   service().delete().entity(u);
}

These are the imports for my DataHelper class:
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyFactory;
import com.googlecode.objectify.ObjectifyService;
import com.googlecode.objectify.Result;

Pretty simple stuff.  Objectify does support transactions but for my little test project I wasn't worried about that too much.  Once I had this all coded I ran into a couple of issues and to be honest I had a hard time figuring it out and really started wishing I had a test case to just hit my class that accessed the data store but how can you test a class that access cloud storage persistence that is pretty much buried behind rest calls.  Guess what,  Google has a spectacular test class called: LocalServiceTestHelper that does just that.

Basically you create your standard JUnit class and create an instance of the DataStoreServiceTestConfig class like this:
private final LocalServiceTestHelper helper = new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());

Once that guy is created you can access the datastore in your test case normally (and so can the Objectify framework).
So I could write a test that looked like this;
@Test
public void testRetrieve()
{
     User user = DataService.findUserByEmail("somebody@somewhere.com");
     Assert.assertNull(user);

}

and it just magically works.

If you're writing a web service for AppEngine I would highly recommend using Google Cloud Endpoints and Objectify.  The level of effort is small but the capability and value returned is very high.

Happy Coding!

-Aaron


No comments: