package com.googlecode.objectify; import com.google.appengine.api.datastore.ReadPolicy.Consistency; import com.google.appengine.api.datastore.Transaction; import com.googlecode.objectify.cmd.Deferred; import com.googlecode.objectify.cmd.Deleter; import com.googlecode.objectify.cmd.Loader; import com.googlecode.objectify.cmd.Saver; /** * <p>This is the main "business end" of Objectify. It lets you load, save, and delete your typed POJO entities.</p> * * <p>{@code Objectify} instances are obtained by calling the static method {@code ObjectifyService.ofy()}. This method * will always provide the correct {@code Objectify} instance for a given transactional context. You can run * transactions by calling {@code Objectify.transact()} or {@code Objectify.transactNew()}; calling {@code ObjectifyService.ofy()} * within {@code Work.run()} will produce the correct {@code Objectify} instance associated with the correct transaction.</p> * * <p>Objectify instances are immutable but they are NOT thread-safe. The instance contains * a session cache of entities that have been loaded from the instance. You should never access an Objectify * from more than one thread simultaneously.</p> * * @author Jeff Schnitzer <jeff@infohazard.org> */ public interface Objectify { /** * <p>Start a load command chain. This is where you begin for any request that fetches data from * the datastore: gets and queries.</p> * * <p>A quick example: * {@code Map<Key<Thing>, Thing> things = ofy().load().type(Thing.class).parent(par).ids(123L, 456L);}</p> * * <p><b>All command objects are immutable; this method returns a new object rather than modifying the * current command object.</b></p> * * @return the next step in the immutable command chain. */ Loader load(); /** * <p>Start a save command chain. Allows you to save (or re-save) entity objects. Note that all command * chain objects are immutable.</p> * * <p>Saves do NOT cascade; if you wish to save an object graph, you must save each individual entity.</p> * * <p>A quick example: * {@code ofy().save().entities(e1, e2, e3).now();}</p> * * <p><b>All command objects are immutable; this method returns a new object rather than modifying the * current command object.</b></p> * * @return the next step in the immutable command chain. */ Saver save(); /** * <p>Start a delete command chain. Lets you delete entities or keys.</p> * * <p>Deletes do NOT cascade; if you wish to delete an object graph, you must delete each individual entity.</p> * * <p>A quick example: * {@code ofy().delete().entities(e1, e2, e3).now();}</p> * * <p><b>All command objects are immutable; this method returns a new object rather than modifying the * current command object.</b></p> * * @return the next step in the immutable command chain. */ Deleter delete(); /** * <p>Start a deferred command chain, which lets you make multiple save or delete calls on a single * entity without incurring multiple datastore operations. Deferred operations are executed at the * end of a unit-of-work (transaction, or http request if not in a transaction).</p> * * <p>Deferred operations are reflected in the session cache immediately. However query operations * may not reflect these changes. For example, newly indexed entities may not show up, even with * an otherwise strongly consistent ancestor query. This should not be surprising since the actual * save operation has not occurred yet.</p> * * <p>In the case of deferred save() and delete() operations on the same entity, the last one wins.</p> * * @return the next step in the immutable command chain. */ Deferred defer(); /** * Obtain the ObjectifyFactory from which this Objectify instance was created. * * @return the ObjectifyFactory associated with this Objectify instance. */ ObjectifyFactory factory(); /** * <p>Provides a new Objectify instance with the specified Consistency. Generally speaking, STRONG consistency * provides more consistent results more slowly; EVENTUAL consistency produces results quickly but they * might be out of date. See the * <a href="http://code.google.com/appengine/docs/java/javadoc/com/google/appengine/api/datastore/ReadPolicy.Consistency.html">Appengine Docs</a> * for more explanation.</p> * * <p>The new instance will inherit all other characteristics (transaction, cache policy, session cache contents, etc) * from this instance.</p> * * <p><b>All command objects are immutable; this method returns a new object rather than modifying the * current command object.</b></p> * * @param policy the consistency policy to use. STRONG load()s are more consistent but EVENTUAL load()s * are faster. * @return a new immutable Objectify instance with the consistency policy replaced */ Objectify consistency(Consistency policy); /** * <p>Provides a new Objectify instance with a limit, in seconds, for datastore calls. If datastore calls take longer * than this amount, a timeout exception will be thrown.</p> * * <p>The new instance will inherit all other characteristics (transaction, cache policy, session cache contents, etc) * from this instance.</p> * * <p><b>All command objects are immutable; this method returns a new object rather than modifying the * current command object.</b></p> * * @param value - limit in seconds, or null to indicate no deadline (other than the standard whole request deadline of 30s/10m). * @return a new immutable Objectify instance with the specified deadline */ Objectify deadline(Double value); /** * <p>Provides a new Objectify instance which uses (or doesn't use) a 2nd-level memcache. * If true, Objectify will obey the @Cache annotation on entity classes, * saving entity data to the GAE memcache service. Fetches from the datastore * for @Cache entities will look in the memcache service first. This cache * is shared across all versions of your application across the entire GAE * cluster.</p> * * <p>Objectify instances are cache(true) by default.</p> * * <p><b>All command objects are immutable; this method returns a new object rather than modifying the * current command object.</b></p> * * @return a new immutable Objectify instance which will (or won't) use the global cache */ Objectify cache(boolean value); /** * <p>Provides a new Objectify instance which throws an exception whenever save() or delete() is * called from outside a transaction context. This is a reasonable sanity check for most business * workloads; you may wish to enable it globally by overriding ObjectifyFactory.begin() to * twiddle this flag on the returned object.</p> * * <p>Objectify instances are mandatoryTransactions(false) by default.</p> * * <p><b>All command objects are immutable; this method returns a new object rather than modifying the * current command object.</b></p> * * @return a new immutable Objectify instance which will (or won't) require transactions for save() and delete(). */ Objectify mandatoryTransactions(boolean value); /** * <p>Get the underlying transaction object associated with this Objectify instance. You typically * do not need to use this; use transact() instead.</p> * * <p>Note that this is *not* the same as {@code DatastoreService.getCurrentTransaction()}, * which uses the Low-Level API's implicit transaction management. Every transactional {@code Objectify} * instance is associated with a specific {@code Transaction} object.</p> * * @return the low-level transaction associated with this Objectify instance, * or null if no transaction is associated with this instance. */ Transaction getTransaction(); /** * <p>If you are in a transaction, this provides you an objectify instance which is outside of the * current transaction and works with the session prior to the transaction start. Inherits any * settings (consistency, deadline, etc) from the present Objectify instance.</p> * * <p>If you are not in a transaction, this simply returns "this".</p> * * <p>This allows code to quickly "escape" a transactional context for the purpose of loading * manipulating data without creating or affecting XG transactions.</p> * * <p><b>All command objects are immutable; this method returns a new object instead of modifying the * current command object.</b></p> * * @return an immutable Objectify instance outside of a transaction, with the session as it was before txn start. */ Objectify transactionless(); /** * <p>Executes work in a transaction. If there is already a transaction context, that context will be inherited. * If there is not already a transaction context, a new transaction will be started.</p> * * <p>Within {@code Work.run()}, obtain the correct transactional {@code Objectify} instance by calling * {@code ObjectifyService.ofy()}</p> * * <p>ConcurrentModificationExceptions will cause the transaction to repeat as many times as necessary to * finish the job. Work <b>MUST</b> idempotent.</p> * * @param work defines the work to be done in a transaction. If this method started a new transaction, it * will be committed when work is complete. If transactional context was inherited, no commit is issued * until the full transaction completes normally. * @return the result of the work */ <R> R transact(Work<R> work); /** * <p>Exactly the same behavior as the Work version, but doesn't return anything. Convenient for Java8 * so you don't have to return something from the lambda.</p> */ void transact(Runnable work); /** * <p>Executes work in a new transaction. Note that this is equivalent to {@code transactNew(Integer.MAX_VALUE, work);}</p> * * <p>ConcurrentModificationExceptions will cause the transaction to repeat as many times as necessary to * finish the job. Work <b>MUST</b> idempotent.</p> * * <p>Within {@code Work.run()}, obtain the new transactional {@code Objectify} instance by calling {@code ObjectifyService.ofy()}</p> * * @param work defines the work to be done in a transaction. After the method exits, the transaction will commit. * @return the result of the work */ <R> R transactNew(Work<R> work); /** * <p>Executes the work in a new transaction, repeating up to limitTries times when a ConcurrentModificationException * is thrown. This requires your Work to be idempotent; otherwise limit tries to 1. * * <p>Within {@code Work.run()}, obtain the new transactional {@code Objectify} instance by calling {@code ObjectifyService.ofy()}</p> * * @param limitTries is the max # of tries. Must be > 0. A value of 1 means "try only once". * @param work defines the work to be done in a transaction. After the method exits, the transaction will commit. * @return the result of the work */ <R> R transactNew(int limitTries, Work<R> work); /** * <p>Executes the work with the transactional behavior defined by the parameter txnType. This is very similar * to EJB semantics. The work can inherit a transaction, create a new transaction, prevent transactions, etc.</p> * * <p>This method principally exists to facilitate implementation of AOP interceptors that provide EJB-like behavior. * Usually you will call {@code transact()} or {@code transactNew()} when writing code.</p> * * <p>Note that ConcurrentModificationExceptions will cause the transaction to repeat as many times as necessary to * finish the job. Work <b>MUST</b> idempotent.</p> * * <p>Within {@code Work.run()}, obtain the correct {@code Objectify} instance by calling {@code ObjectifyService.ofy()}</p> * * @param txnType defines what kind of transaction context the work should be executed in. * @param work defines the work to be done; possibly in a transaction, possibly not as defined by txnType * @return the result of the work */ <R> R execute(TxnType txnType, Work<R> work); /** * Synchronously flushes any deferred operations to the datastore. Objectify does this for you at the end * of transactions and requests, but if you need data to be written immediately - say, you're about to perform * a strongly-consistent ancestor query and you need to see the updated indexes immediately - you can call this * method. If there are no deferred operations, this does nothing. */ void flush(); /** * <p>Clears the session; all subsequent requests (or Ref<?>.get() calls) will go to the datastore/memcache * to repopulate the session. This should rarely, if ever be necessary. Note that if you iterate query results * you should only perform this action on chunk boundaries, otherwise performance will suffer. This is a "use * only if you really know what you are doing" feature.</p> */ void clear(); /** * @return true if the key has been loaded into the session; false if loading the key would result in a datastore * (or memcache) fetch. */ boolean isLoaded(Key<?> key); }