I am going to use the AppEngine Datastore, see:
This is because JDO and JPA are massive and have a steep learning curve They suggest Objectify, TWiG and Slim3. With my first Google I found this cool article by Andreas Borglin, and then lots of links to his article.
Now for some implementation.
First I have to manually register the entities with ObjectifyService. The Best Practices guide advised me to use a DAO object with a static initialiser. so I created a new class in package com.yrdomain.yrproject.server;
import com.googlecode.objectify.ObjectifyService; import com.googlecode.objectify.util.DAOBase; /** * By accessing Objectify through your own DAO class, you can register your * entities in a static initializer and also add domain-specific helper methods. * */ public class DAO extends DAOBase { static { ObjectifyService.register(Gift.class); } /* Your DAO can have your own useful methods public MyThing getOrCreateMyThing(long id) { MyThing found = ofy().find(clazz, id); if (found == null) return new MyThing(id); else return found; }*/ }
I then didn't realise what I was doing and tried to get a reference to the Objectify twice with:
Objectify ofy = ObjectifyService.begin();
in my YrprojectService.java class. However, by extending DAOBase, it already gets this and is accessible by just typing:
dao.ofy().some_method()
If you do it twice it comes up with a NoClassDefFoundError for DAOBase - well it did the first time, when trying to re-create the error to put in the stack-trace for you guys, it worked fine!
This is a first go at the implementation:
public class YrprojectService { DAO dao = new DAO(); @ServiceMethod public Gift createGift() { UserService userService = UserServiceFactory.getUserService(); User user = userService.getCurrentUser(); String message; if (user == null) { message = "No one is logged in!\nSent from App Engine at " + new Date(); } else { message = "Howdie, " + user.getEmail() +
"!\nSent from createGift on the App Engine at " +
new Date(); } // Simple create Gift porsche = new Gift(); porsche.setGiftName("sports car"); porsche.setGiftLocation(message); dao.ofy().put(porsche); assert porsche.getId() != null; // id was autogenerated return porsche; } //...other CRUD methods... }
It all works fine, as in there are no errors... but can I get my porche back?
Another Error
Objectify FAQs suggest that we turn off the datanucleus byte-code enhancer in Eclipse (go to the Project Properties -> Builders and disable the Enhancer) If you are using the default AppEngine Connected Android Project that is created when you use the Wizard in Eclipse, it uses JDO in the registration of the device. If you subsequently decide to use Objectify, turning off the datanucleus byte-code enhancer will give you the following error:
Failure: Got exception javax.jdo.JDOUserException: Persistent class "Class com.yrdomain.yrproject.server.DeviceInfo 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. NestedThrowables: org.datanucleus.store.exceptions.NoTableManagedException: Persistent class "Class comyrdomain.yrproject.server.DeviceInfo 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.
Just turn on the enhancer again, Clean and re-build, the error will go away. You have to remove all the default behaviour and use the Objectify ways. People like to turn off the enhancer because it can take too much time, its easier to just be a bit more specific in what files you want enhanced, remember it's looking for files that will be persisted. Change these options in:
Project Settings > Google > App Engine > ORM
Change the src/ and shared/ folders
Over on our old blog (http://how2code.wordpress.com/2011/11/29/appengine-datastore-implementation-with-objectify-put/) I had a great comment, so I thought I'd add it here:
ReplyDeleteJust a quick question – I am playing around with building a REST API and am using GAE for hosting it and Objectify for managing the ORM. Following the best practices, I am using the DAO class as you are. My one question – doesn’t this give me lots of places where I am creating a new DAO object? Or is that being invisibly managed for me so that the same object is being reused? Or am I missing something fairly obvious.
Thanks!
RB
This was a bit odd to me too, but here is why it is necessary:
Hi Richard,
Yes that’s right, you are creating many DAO objects, but that’s fine. Let’s look at what you’re creating here by following Best Practice:
When you’re coding:
public class DAO extends DAOBase {
static {
ObjectifyService.register(Gift.class);
}
ObjectifyService is just a wrapper for ObjectifyFactory; it holds onto a single ObjectifyFactory as a singleton.
See: http://code.google.com/p/objectify-appengine/source/browse/trunk/src/com/googlecode/objectify/ObjectifyService.java?r=423
ObjectifyFactory is a heavyweight Factory object so you only want one. It is also threadsafe and is reused by all requests in your app.
The first time the DAO class is loaded the static block is run and your entities are registered. Everytime you subsequently create a new DAO object, this bit of code is not run, so don’t worry about it re-registering your entities all the time.
In the meat of your application, everytime you call ofy() you are calling ObjectifyService.factory().begin() to get a new Objectify object.
See: http://code.google.com/p/objectify-appengine/source/browse/trunk/src/com/googlecode/objectify/helper/DAOBase.java?r=423
Objectify objects are NOT threadsafe (and neither is the underlying DatastoreService), so you NEED a new Objectify object each time. Your DAO class that is extending DAOBase is just a wrapper for an Objectify object. They’re light-weight objects that are designed to be created per request.
So don’t worry about creating them, you HAVE to :)
What about update? How to do update on Googledatastore for objectify 3.1?
ReplyDelete