- summary Patterns and best practices for using Objectify
- labels Featured
The first question you will have is "when and how should I register my entity classes?" The obvious answer is to do it at application startup in a servlet context listener or an init servlet - wherever your application starts running. However, there is an easier way:
This guarantees that your entities are registered before you use Objectify, but doesn't necessarily impact application startup for requests which do not access the datastore.
Instead of using the static method, use your own.
Now in your code, import your own static :
Another advantage of doing this is that when you want to use [Wrappers], you can easily change the return type of your static to your own custom class.
You might think that you could register an entity as a static initializer for the entity class itself:
This is dangerous! Because Java loads (and initializes) classes on-demand, Objectify cannot guarantee that your class will be registered at the time that it is fetched from the database. For example, suppose you execute a query that might return several different kinds of entities:
When Objectify tries to reconstitute an object of type ThingA, it won't be able to because the ThingA class will not yet have been loaded and the static initializer will not have been called. If your application actually does use a ThingA before this query is executed, it will work - and in fact, it may work 99.99% of the time. But do you really want to hunt down mysterious IllegalStateExceptions 0.01% of the time?
Most J2EE-style frameworks, including Appengine's JDO/JPA system, do classpath scanning and can automatically register classes that have @Entity or other relevant annotations. This is convenient and could easily be added to Objectify without changing a single source file. There are, however, several reasons why this isn't part of the core:
# This feature requires either [http://scannotation.sourceforge.net/ Scannotations] or [http://code.google.com/p/reflections/ Reflections], bringing in 5-6 dependency jars. # Developers would need to add a startup hook to your web.xml (a ServletContextListener) in order to trigger this scanning. # Classpath scanning is *slow* because it opens each .class and .jar file in your project and processes every single class file with a bytecode manipulator. For a moderately sized project this easily adds 3-5 seconds to your application initialization time. That's 3-5 additional seconds that real-world users must sit waiting while your application cold-starts.
Of these issues, the last is the most fatal. If you think "My application gets a lot of traffic! I don't need to worry about cold starts!", you are overlooking the fact that App Engine starts and stops instances to meet demand all the time - at least one user somewhere is going to be affected on every spinup. Plus this happens every time you redeploy your application! There is no escaping cold-start time.
Furthermore, classpath scanning costs accumulate. If you use other tools that perform classpath scanning (Weld, Spring, JAX-RS, etc), they each will also spend 3-5s scanning your jars. It isn't hard to push your cold-start time into the tens of seconds.
That said, 3-5s might be reasonable for your specific project. It should be very easy to add as your own ServletContextListener that calls Reflections and registers the @Entity classes. Spring and other framework users should examine the [Extensions].
In SQL, all data lives in tables and is accessed through queries. It is best not to imagine the Appengine datastore this way - conceptually shift to thinking of the datastore as a key-value store that happens to also let you index and query some values.
The reason this shift is important is because your most effective tool when working with chunks of data is the batch load and save. A batch get-by-key will fetch many entities in parallel; running individual queries for each would take a relative eternity. Asynchronous queries can only provide limited help because GAE limits you to 10 concurrent requests.
Furthermore, a batch get-by-key can be efficiently cached. Use Objectify's annotation and your load() may never need a trip to the datastore.
Of course, batch gets and queries are not necessarily fungible operations - but when they are, use a batch get.
Each index adds cost. There is an up-front cost (two writes per newly-written single-property index; _four_ if you change an index value) plus ongoing storage fees. For most GAE apps, most of your storage costs will be index data - it grows quickly.
Objectify does not index anything by default; you must explicitly request it with the annotation. Use it wisely.
* IBM developerWorks' _Twitter Mining with Objectify-Appengine_, [http://www.ibm.com/developerworks/java/library/j-javadev2-13/index.html part 1] and [http://www.ibm.com/developerworks/java/library/j-javadev2-14/index.html part 2]
* [http://borglin.net/gwt-project/?page_id=604 Review of Objectify/Twig/SimpleDS] * [http://groups.google.com/group/google-appengine-java/browse_thread/thread/4467986eaf01788b/d3a1678a44242c25 Original release announcement on GAE-Java] * [http://sites.google.com/site/io/under-the-covers-of-the-google-app-engine-datastore Google I/O 2008 - Under the Covers of the Google App Engine Datastore] (required watching for anyone that uses App Engine!) * [http://www.youtube.com/watch?v=AgaL6NGpkB8 Google I/O 2009 - Scalable, Complex Apps on App Engine] * [http://groups.google.com/group/google-appengine-java/browse_thread/thread/f20d922ffecb310c Differences between Twig and Objectify plus example of million user fanout] * [http://www.answercow.com/2010/03/google-app-engine-cold-start-guide-for.html Reducing start-up time guide] * [http://turbomanage.wordpress.com/2010/03/26/appengine-cold-starts-considered/ Recucing start-up time blog entry] * [http://turbomanage.wordpress.com/2010/01/28/simplify-with-objectify/ David M. Chandler's blog posting about Objectify] and [http://turbomanage.wordpress.com/2010/02/09/generic-dao-for-objectify-2/ Objectify 2] * [http://iqbalyusuf.wordpress.com/gwt-uibinder-with-jax-rs-jersey/ GWT Example App with Objectify] * [http://groups.google.com/group/objectify-appengine/msg/14a326058a0870be Example of a Cursor based IteratingTask base class] * [http://groups.google.com/group/objectify-appengine/browse_thread/thread/afa0d43de5db483f Controlling user access to data with queries] * [http://iqbalyusuf.wordpress.com/jersey-guice-on-google-app-engine-java/ Jersey + Guice on Google App Engine Java] * [http://groups.google.com/group/objectify-appengine/browse_thread/thread/25a9abfc86f8be51 How to emulate @Unique] and [http://groups.google.com/group/objectify-appengine/browse_thread/thread/5edec724ca69719e/b7536d44d028ef02 a longer discussion] of enforcing uniqueness. * [http://www.fishbonecloud.com/2010/11/use-objectify-to-store-data-in-google.html Simple tutorial for 'putting' data in the datastore using Objectify]