FrequentlyAskedQuestions - objectify/objectify GitHub Wiki

Frequently Asked Questions

What happened between Objectify v5 and v6?

For most of the history of Google App Engine there were only Standard Runtimes. These launch one JVM per instance and connect to GAE services through a special proprietary interface called an ApiProxy. Objectify v5 uses this ApiProxy. This is the "Google App Engine SDK" and has maven dependencies like appengine-api-1.0-sdk.jar.

Google is in the process of replacing this proprietary interface with standards-based (REST and GRPC) interfaces to cloud services. This is the new "Google Cloud SDK" which can be used from both outside and inside GAE Standard. This required a rewrite of Objectify, which is v6.

Can I use Objectify from a GAE Flexible Runtime?

Yes. Use Objectify v6.

I am using GAE Standard. Should I use Objectify v5 or v6?

There are still some limitations to the Google Cloud SDK (and consequently, Objectify v6) that you may wish to consider:

  • Not yet any way to enqueue tasks as part of a datastore transaction
  • Memcached integration requires your own memecached server (no integrated GAE offering yet)
  • Performance may suffer; in particular, per-request latency

However, Google has made it clear that the Cloud SDK is the path forward and is actively developing it. If you can live with these limitations, use Objectify v6. This route is the most future-proof.

On the other hand, we tried to make the transition from Objectify v5 to v6 as easy as possible. See the Upgrade Guide and decide if you want to tackle it now or later. It's your call.

Why aren't my queries working properly?

All of Objectify's intermediate command objects are immutable. This will not work:

Query<Foo> q = ofy().load().type(Foo.class);
q.filter("bar", bar);
List<Foo> foos = q.list();

The filter command did nothing because you did not reassign q. You need this:

q = q.filter("bar", bar);

Alternatively, chain the whole sequence in a single statement. Read more here

NoSuchMethodError?!?

This is almost always a result of having the wrong (or a duplicate) jar in your classpath. Ensure that you have one and only one objectify.jar on your classpath, that it is the correct version, and that you are running against the correct minimum version of the Google App Engine SDK. Check the Objectify ReleaseNotes to see what minimum version is required. Google is rapidly developing Appengine; as they add new features, we add support to Objectify. This tends to require up-to-date versions of the SDK.

Also be sure that you have the guava.jar dependency on your classpath. If you are using Maven, the dependency will be included automatically.

Sometimes Maven gets dumb. Try a maven clean.

Strange things are showing up in my session cache! (or missing from it!)

This is almost always caused by one of two problems:

  • You did not enable the ObjectifyFilter in your web.xml. You must install this filter.
  • You are sharing an Objectify instance across threads. Objectify instances represent a single session of work and are not thread-safe.

Make sure the ObjectifyFilter is installed, and always use the static ofy() method whenever you perform Objectify operations. If you never hold on to an Objectify reference, you will not get into trouble.

ClassCastException: com.google.appengine.api.datastore.Entity cannot be cast to XXX

If you load an entity kind that has not been registered, Objectify leaves it untranslated as the raw datastore Entity. This is a convenience feature.

This means that if you have any kind of race condition that allows load() operations to proceed before registration, you may receive ClassCastExceptions when you try to cast the value returned by ofy().load() to your expected entity type.

See BestPractices for more about how to register entities.

Will GAE complete my asynchronous operations if I don't call now()?

Yes. GAE completes all asynchronous operations at the end of the request. This means your request will block until they finish; async operations are not a way of getting around the 60s request deadline.

Also, asynchronous operations are completed at the time of transaction commit.

Can I use Dependency Injection (Spring, Guice, Weld) with Objectify?

Yes! Objectify is designed to integrate with DI systems and can even use injectors to create entities and internal classes. See the Motomapia Example.

However, we do not recommend using DI to inject Objectify instances. As you enter and exit transaction contexts, your code will use different Objectify instances; a single injected instance creates a significant risk that you will mix up transaction contexts. Always use the static ofy() method.

How do I shut off the datanucleus byte code enhancer?

  • In Eclipse, go to the Project Properties -> Google -> App Engine and uncheck "Use Datanucleus JDO/JPA to access the datastore"

Can I change data after it is loaded or before it is stored?

Absolutely, see the LifecycleCallbacks

Should I Use a String (Name) Id?

If it is a natural key, sure; Objectify allows this no problem. Here is a little more of a discussion about it.

How do I do a like query (LIKE "foo%")

You can do something like a startWith (or endWith if you reverse the order when stored and searched). You do a range query with the starting value you want, and a value just above the one you want.

String start = "foo";
... = ofy().load().type(MyEntity.class).filter("field >=", start).filter("field <", start + "\uFFFD"); 

See this discussion for more background and details

How do I migrate from JDO to Objectify?

You should have no trouble using Objectify side-by-side with JDO.

For the most part, migrating JDO classes to Objectify classes should be pretty straightforward. The only trick will be mapping the relationships. Look in the datastore viewer to see what entity structure JDO created - relationships are just a Key on one end or both. If you make this field a Key>, Ref>, or concrete entity reference (or collection thereof if appropriate) in your Objectify entities, it will map just fine.

Does Objectify support JDO Fetch Groups?

There is no such thing as a fetch group in the GAE datastore; entities are fetched in their entirety or not at all. Objectify reflects the underlying nature of the datastore so it does not require this JDO-ism.

Does Objectify support JDO detach copy?

There is no need for this JDO-ism either. Objectify entities are exactly as you define them. They are not bytecode-enhanced, proxied, or manipulated in any way. Want to serialize your entity? Just make sure it is Serializable.

Can I mix Low Level and Objectify writes in a single Objectify transaction?

Example...

ofy().save().entity(makeSomeObject()).now();
ofy().factory().datastore().put(makeSomeLowLevelEntity());

Yes you can! With a few caveats...

  • The direct-datastore put() will bypass the session cache. If the session cache already held a value for that key, subsequent a load() from the Objectify instance will produce the (now incorrect) cached value.
  • The global cache will remain consistent as long as you obtain the Datastore from ObjectifyFactory.datastore(); if you get it directly from the google SDK it will bypass Objectify's global (memcache) cache.

You're free to interleave Objectify and Low-Level API calls.

⚠️ **GitHub.com Fallback** ⚠️