Replace autoflush with basic entity group transaction support

As an optional feature the database may implement entity group
transaction semantics, where a transaction can be started on a
single root entity (e.g. a Change object in Gerrit Code Review),
reads or updates applied, and later the transaction committed.
This feature better supports a database like Google AppEngine
where the entity group transactions are relatively slow.

Change-Id: I740dfbf6902e403347fd1838aee83271500e0c30
diff --git a/src/main/java/com/google/gwtorm/client/Access.java b/src/main/java/com/google/gwtorm/client/Access.java
index 27a26f7..b6872ff 100644
--- a/src/main/java/com/google/gwtorm/client/Access.java
+++ b/src/main/java/com/google/gwtorm/client/Access.java
@@ -155,6 +155,14 @@
   void delete(Iterable<T> instances) throws OrmException;
 
   /**
+   * Start a transaction on an object.
+   *
+   * @param key key describing the object (and its children).
+   * @throws OrmException a transaction cannot be started.
+   */
+  void beginTransaction(K key) throws OrmException;
+
+  /**
    * Atomically update a single entity.
    * <p>
    * If the entity does not exist, the method returns {@code null} without
diff --git a/src/main/java/com/google/gwtorm/client/Schema.java b/src/main/java/com/google/gwtorm/client/Schema.java
index e18133a..1e8e2a0 100644
--- a/src/main/java/com/google/gwtorm/client/Schema.java
+++ b/src/main/java/com/google/gwtorm/client/Schema.java
@@ -55,30 +55,11 @@
  * </pre>
  */
 public interface Schema {
-  /** @return true if auto flush is enabled (default). */
-  boolean isAutoFlush();
+  /** Commit a pending transaction. */
+  public void commit() throws OrmException;
 
-  /**
-   * Set (or unset) the auto-flush flag for this connection.
-   * <p>
-   * If true writes are sent to the database by the time the method returns. If
-   * false, writes will be sent at any time, or some later point in the future.
-   * Callers should use {@link #flush()} to ensure the writes are visible, or
-   * reset the auto flush flag to true.
-   *
-   * @param autoFlush the new setting.
-   * @throws OrmException previously autoFlush was false, the new setting is
-   *         true, and flushed writes cannot be sent.
-   */
-  void setAutoFlush(boolean autoFlush) throws OrmException;
-
-  /**
-   * Ensures all modifications are now visible to others.
-   *
-   * @throws OrmException one or more modifications cannot be applied. The
-   *         writes are now inconsistent.
-   */
-  void flush() throws OrmException;
+  /** Abort a pending transaction, if one is already in progress, otherwise nop. */
+  public void rollback() throws OrmException;
 
   /**
    * Add any missing columns, create any missing tables or sequences.
diff --git a/src/main/java/com/google/gwtorm/client/impl/AbstractAccess.java b/src/main/java/com/google/gwtorm/client/impl/AbstractAccess.java
index 17ff862..287c816 100644
--- a/src/main/java/com/google/gwtorm/client/impl/AbstractAccess.java
+++ b/src/main/java/com/google/gwtorm/client/impl/AbstractAccess.java
@@ -30,6 +30,11 @@
     implements Access<E, K> {
   private static final int MAX_TRIES = 10;
 
+  @Override
+  public void beginTransaction(K key) throws OrmException {
+    // Do nothing by default.
+  }
+
   public ResultSet<E> get(final Iterable<K> keys) throws OrmException {
     final ArrayList<E> r = new ArrayList<E>();
     for (final K key : keys) {
diff --git a/src/main/java/com/google/gwtorm/jdbc/JdbcSchema.java b/src/main/java/com/google/gwtorm/jdbc/JdbcSchema.java
index a4ecb8f..13ce9f9 100644
--- a/src/main/java/com/google/gwtorm/jdbc/JdbcSchema.java
+++ b/src/main/java/com/google/gwtorm/jdbc/JdbcSchema.java
@@ -40,20 +40,6 @@
     conn = dbDef.newConnection();
   }
 
-  @Override
-  public boolean isAutoFlush() {
-    return true; // We are always flushing.
-  }
-
-  @Override
-  public void setAutoFlush(boolean autoFlush) {
-  }
-
-  @Override
-  public void flush() {
-    // Do nothing, we flush by default during execution.
-  }
-
   public final Connection getConnection() {
     return conn;
   }
diff --git a/src/main/java/com/google/gwtorm/nosql/NoSqlSchema.java b/src/main/java/com/google/gwtorm/nosql/NoSqlSchema.java
index f130ca7..697a6c3 100644
--- a/src/main/java/com/google/gwtorm/nosql/NoSqlSchema.java
+++ b/src/main/java/com/google/gwtorm/nosql/NoSqlSchema.java
@@ -21,26 +21,10 @@
 
 /** Internal base class for implementations of {@link Schema}. */
 public abstract class NoSqlSchema extends AbstractSchema {
-  private boolean autoFlush = true;
-
   protected NoSqlSchema(final NoSqlDatabase<?, ?, ?> d) {
   }
 
   @Override
-  public boolean isAutoFlush() {
-    return autoFlush;
-  }
-
-  @Override
-  public void setAutoFlush(boolean autoFlush) throws OrmException {
-    if (!this.autoFlush && autoFlush) {
-      flush();
-    }
-
-    this.autoFlush = autoFlush;
-  }
-
-  @Override
   public void pruneSchema(StatementExecutor e) throws OrmException {
     // Assume no action is required in a default NoSQL environment.
   }
diff --git a/src/main/java/com/google/gwtorm/nosql/generic/GenericAccess.java b/src/main/java/com/google/gwtorm/nosql/generic/GenericAccess.java
index 8c4df87..7bad7da 100644
--- a/src/main/java/com/google/gwtorm/nosql/generic/GenericAccess.java
+++ b/src/main/java/com/google/gwtorm/nosql/generic/GenericAccess.java
@@ -331,18 +331,12 @@
     return new ListResultSet<T>(res);
   }
 
-  private void maybeFlush() throws OrmException {
-    if (db.isAutoFlush()) {
-      db.flush();
-    }
-  }
-
   @Override
   public void insert(Iterable<T> instances) throws OrmException {
     for (T obj : instances) {
       insertOne(obj);
     }
-    maybeFlush();
+    db.flush();
   }
 
   private void insertOne(T nObj) throws OrmException {
@@ -357,7 +351,7 @@
     for (T obj : instances) {
       upsertOne(obj, true);
     }
-    maybeFlush();
+    db.flush();
   }
 
   @Override
@@ -365,7 +359,7 @@
     for (T obj : instances) {
       upsertOne(obj, false);
     }
-    maybeFlush();
+    db.flush();
   }
 
   private void upsertOne(T newObj, boolean mustExist) throws OrmException {
@@ -447,7 +441,7 @@
       pruneOldIndexes(oldObj, null);
       cache().remove(primaryKey(oldObj));
     }
-    maybeFlush();
+    db.flush();
   }
 
   @Override
diff --git a/src/main/java/com/google/gwtorm/nosql/generic/GenericSchema.java b/src/main/java/com/google/gwtorm/nosql/generic/GenericSchema.java
index 81b62aa..9d7837c 100644
--- a/src/main/java/com/google/gwtorm/nosql/generic/GenericSchema.java
+++ b/src/main/java/com/google/gwtorm/nosql/generic/GenericSchema.java
@@ -46,6 +46,9 @@
     db = d;
   }
 
+  public void flush() throws OrmException {
+  }
+
   /** @return the database that created this schema instance. */
   public GenericDatabase<?, ?, ?> getDatabase() {
     return db;
diff --git a/src/main/java/com/google/gwtorm/server/AbstractSchema.java b/src/main/java/com/google/gwtorm/server/AbstractSchema.java
index 11a934c..1164a22 100644
--- a/src/main/java/com/google/gwtorm/server/AbstractSchema.java
+++ b/src/main/java/com/google/gwtorm/server/AbstractSchema.java
@@ -19,6 +19,16 @@
 
 /** Base implementation any generated schema must implement. */
 public abstract class AbstractSchema implements Schema {
+  @Override
+  public void commit() throws OrmException {
+    // Do nothign by default.
+  }
+
+  @Override
+  public void rollback() throws OrmException {
+    // Do nothign by default.
+  }
+
   /**
    * Obtain the next unique value from a pool of available numbers.
    * <p>