MultipleDatastores - objectify/objectify GitHub Wiki

A Little History

Originally, "the datastore" was a service available to Google App Engine applications using a proprietary API. The design of this API allowed an application to connect only to a single datastore. The first five versions of Objectify were built against this API.

In 2013, Google launched "Google Cloud Datastore" as a standalone offering. This made datastores available outside of Google App Engine via REST/gRPC and a new SDK library. The new API allows an application to connect to multiple datastores. Objectify v6+ uses this new SDK.

Objectify's v6+ API tries to make life easy for people working with a single datastore (the vast majority of users) while still accommodating folks that want to use multiple datastores. This document should explain what you need to know.

ObjectifyFactory

The ObjectifyFactory represents a connection to a single datastore. Some useful things to know:

  • It wraps the com.google.cloud.datastore.Datastore object from the Google SDK
  • It maintains your registered entity classes and knows how to persist and restore your POJOs.
  • It maintains thread-local transaction state so that factory.ofy() always gives you the right context.

For every datastore you want to interact with, you need a properly configured ObjectifyFactory.

ObjectifyService for single datastores

To make the simple "just one datastore" use case easy (and to preserve backwards compatibility with old versions), Objectify provides the ObjectifyService class. ObjectifyService is a simple holder of a singleton ObjectifyFactory with static methods that mimic the methods on ObjectifyFactory. Take a look at the code, it looks like this:

    private static ObjectifyFactory factory;

    public static void init(final ObjectifyFactory fact) {
        factory = fact;
    }

    public static void register(Class<?> clazz) {
        factory().register(clazz);
    }

    public static Objectify ofy() {
        return factory().ofy();
    }

    public static <T> Key<T> key(final Class<? extends T> kindClass, final long id) {
        return factory().key(kindClass, id);
    }

    ...etc

With a static import, Objectify is easy to use in your code:

import static com.googlecode.objectify.ObjectifyService.ofy;

Thing thing = ofy().load().key(thingKey).now();

The ObjectifyService class is totally optional! If you don't like statics, you can use the ObjectifyFactory directly.

Multiple datastores

If you have multiple datastores, use multiple ObjectifyFactory instances. Skip the ObjectifyService - you don't need it. Just be aware that you need to call the ofy() method on the appropriate factory when you want to access each datastore.

Here is a complete (though simplistic) bit of code that accesses two separate datastores:

public class DoSomeDatastoreStuff {
    private static final ObjectifyFactory LEFT;
    static {
        Datastore left = DatastoreOptions.newBuilder().setDatabaseId("left").build().getService();
        LEFT = new ObjectifyFactory(left);
        LEFT.register(LeftThing.class);
    }

    private static final ObjectifyFactory RIGHT;
    static {
        Datastore right = DatastoreOptions.newBuilder().setDatabaseId("right").build().getService();
        RIGHT = new ObjectifyFactory(right);
        RIGHT.register(RightThing.class);
    }

    public void doStuff() {
        LEFT.run(() -> {
            RIGHT.run(() -> {
                LeftThing leftThing = LEFT.ofy().load().type(LeftThing.class).id(123L).now();
                RightThing rightThing = RIGHT.ofy().load().type(RightThing.class).id(456L).now();
            });
        }); 
    }
}

You could also have a "main" datastore that you access through static ObjectifyService methods and accessory datastores that you access through explicit ObjectifyFactory instances. It's up to you.

There's another example here.

What about ObjectifyService.Filter?

You don't need it, you just need to obey the contract. Before you can use ObjectifyFactory.ofy(), you must call ObjectifyFactory.begin() (or ObjectifyFactory.run()) to start a session. That's all the filter does!

Create your own servlet filter and start a session across each of your databases. Or you can start sessions on demand; that's up to you. Sessions are cheap to create, so there's no particular value in creating them lazily.

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