Class DatabaseClientWithChangeSets

  • All Implemented Interfaces:
    com.google.cloud.spanner.DatabaseClient

    public class DatabaseClientWithChangeSets
    extends Object
    implements com.google.cloud.spanner.DatabaseClient
    A wrapper around a DatabaseClient that automatically generates change set id's and adds a mutation for a change set for each read/write transaction. This DatabaseClient can be used in combination with SpannerTableChangeSetPoller or SpannerDatabaseChangeSetPoller to trigger data changed events for tables that do not contain a commit timestamp column.
    • Method Detail

      • of

        public static DatabaseClientWithChangeSets of​(com.google.cloud.spanner.DatabaseClient client)
        Creates a DatabaseClient that will automatically create a change set for each read/write transaction.
      • newChangeSetId

        public String newChangeSetId()
      • write

        public com.google.cloud.Timestamp write​(Iterable<com.google.cloud.spanner.Mutation> mutations)
                                         throws com.google.cloud.spanner.SpannerException
        Writes the given mutations WITHOUT a change set id. Consider using write(String, Iterable).
        Specified by:
        write in interface com.google.cloud.spanner.DatabaseClient
        Throws:
        com.google.cloud.spanner.SpannerException
      • write

        public com.google.cloud.Timestamp write​(String changeSetId,
                                                Iterable<com.google.cloud.spanner.Mutation> mutations)
                                         throws com.google.cloud.spanner.SpannerException
        Writes the given mutations atomically to the database using the given change set id.

        This method uses retries and replay protection internally, which means that the mutations are applied exactly once on success, or not at all if an error is returned, regardless of any failures in the underlying network. Note that if the call is cancelled or reaches deadline, it is not possible to know whether the mutations were applied without performing a subsequent database operation, but the mutations will have been applied at most once.

        Example of blind write.

        
         String changeSetId = dbClient.newChangeSetId();
         long singerId = my_singer_id;
         Mutation mutation = Mutation.newInsertBuilder("Singer")
                 .set("SingerId")
                 .to(singerId)
                 .set("FirstName")
                 .to("Billy")
                 .set("LastName")
                 .to("Joel")
                 .set("ChangeSetId")
                 .to(changeSetId)
                 .build();
         dbClient.write(Collections.singletonList(mutation));
         
        Returns:
        the timestamp at which the write was committed
        Throws:
        com.google.cloud.spanner.SpannerException
      • writeAtLeastOnce

        public com.google.cloud.Timestamp writeAtLeastOnce​(Iterable<com.google.cloud.spanner.Mutation> mutations)
                                                    throws com.google.cloud.spanner.SpannerException
        Writes the given mutations WITHOUT a change set id. Consider using writeAtLeastOnce(String, Iterable).
        Specified by:
        writeAtLeastOnce in interface com.google.cloud.spanner.DatabaseClient
        Throws:
        com.google.cloud.spanner.SpannerException
      • writeAtLeastOnce

        public com.google.cloud.Timestamp writeAtLeastOnce​(String changeSetId,
                                                           Iterable<com.google.cloud.spanner.Mutation> mutations)
                                                    throws com.google.cloud.spanner.SpannerException
        Writes the given mutations atomically to the database without replay protection using the given change set id.

        Since this method does not feature replay protection, it may attempt to apply mutations more than once; if the mutations are not idempotent, this may lead to a failure being reported when the mutation was applied once. For example, an insert may fail with ErrorCode.ALREADY_EXISTS even though the row did not exist before this method was called. For this reason, most users of the library will prefer to use write(Iterable) instead. However, writeAtLeastOnce() requires only a single RPC, whereas write() requires two RPCs (one of which may be performed in advance), and so this method may be appropriate for latency sensitive and/or high throughput blind writing.

        Example of unprotected blind write.

        
         String changeSetId = dbClient.newChangeSetId();
         long singerId = my_singer_id;
         Mutation mutation = Mutation.newInsertBuilder("Singers")
                 .set("SingerId")
                 .to(singerId)
                 .set("FirstName")
                 .to("Billy")
                 .set("LastName")
                 .to("Joel")
                 .set("ChangeSetId")
                 .to(changeSetId)
                 .build();
         dbClient.writeAtLeastOnce(changeSetId, Collections.singletonList(mutation));
         
        Returns:
        the timestamp at which the write was committed
        Throws:
        com.google.cloud.spanner.SpannerException
      • readWriteTransaction

        public DatabaseClientWithChangeSets.TransactionRunnerWithChangeSet readWriteTransaction()
        Creates a TransactionRunner that automatically creates a change set.

        Example usage:

        
         DatabaseClientWithChangeSets dbClientWithChangeSets = DatabaseClientWithChangeSets.of(dbClient);
         TransactionRunnerWithChangeSet runner = dbClientWithChangeSets.readWriteTransaction();
         long singerId = my_singer_id;
         runner.run(
             new TransactionCallable<Void>() {
        
               @Override
               public Void run(TransactionContext transaction) throws Exception {
                 String column = "FirstName";
                 Struct row =
                     transaction.readRow("Singers", Key.of(singerId), Collections.singleton(column));
                 String name = row.getString(column);
                 transaction.buffer(
                     Mutation.newUpdateBuilder("Singers")
                         .set(column).to(name.toUpperCase())
                         .set("ChangeSetId").to(runner.getChangeSetId())
                         .build());
                 return null;
               }
             });
         
        Specified by:
        readWriteTransaction in interface com.google.cloud.spanner.DatabaseClient
      • transactionManager

        public DatabaseClientWithChangeSets.TransactionManagerWithChangeSet transactionManager()
        Returns a transaction manager that automatically creates a change set. This API is meant for advanced users. Most users should instead use the readWriteTransaction() API instead.

        Example usage:

        
         DatabaseClientWithChangeSets dbClientWithChangeSets = DatabaseClientWithChangeSets.of(dbClient);
         long singerId = my_singer_id;
         try (TransactionManagerWithChangeSet manager = dbClientWithChangeSets.transactionManager()) {
           TransactionContext txn = manager.begin();
           while (true) {
             String column = "FirstName";
             Struct row = txn.readRow("Singers", Key.of(singerId), Collections.singleton(column));
             String name = row.getString(column);
             txn.buffer(
                 Mutation.newUpdateBuilder("Singers")
                     .set(column).to(name.toUpperCase())
                     .set("ChangeSetId").to(manager.getChangeSetId())
                     .build());
             try {
               manager.commit();
               break;
             } catch (AbortedException e) {
               Thread.sleep(e.getRetryDelayInMillis() / 1000);
               txn = manager.resetForRetry();
             }
           }
         }
         
        Specified by:
        transactionManager in interface com.google.cloud.spanner.DatabaseClient
      • runAsync

        public DatabaseClientWithChangeSets.AsyncRunnerWithChangeSet runAsync()
        Returns an asynchronous transaction runner that automatically creates a change set for executing a single logical transaction with retries. The returned runner can only be used once.

        Example usage:

         
         Executor executor = Executors.newSingleThreadExecutor();
         DatabaseClientWithChangeSets dbClientWithChangeSets = DatabaseClientWithChangeSets.of(dbClient);
         final long singerId = my_singer_id;
         AsyncRunnerWithChangeSet runner = dbClientWithChangeSets.runAsync();
         ApiFuture rowCount =
             runner.runAsync(
                 new AsyncWork() {
        
        Specified by:
        runAsync in interface com.google.cloud.spanner.DatabaseClient
      • transactionManagerAsync

        public DatabaseClientWithChangeSets.AsyncTransactionManagerWithChangeSet transactionManagerAsync()
        Returns an asynchronous transaction manager that automatically creates a change set, and which allows manual management of transaction lifecycle. This API is meant for advanced users. Most users should instead use the runAsync() API instead.

        Example of using DatabaseClientWithChangeSets.AsyncTransactionManagerWithChangeSet with lambda expressions (Java 8 and higher).

        
         DatabaseClientWithChangeSets dbClientWithChangeSets = DatabaseClientWithChangeSets.of(dbClient);
         long singerId = 1L;
         try (AsyncTransactionManagerWithChangeSet manager = dbClientWithChangeSets.transactionManagerAsync()) {
           TransactionContextFuture txnFut = manager.beginAsync();
           while (true) {
             String column = "FirstName";
             CommitTimestampFuture commitTimestamp =
                 txnFut
                     .then(
                         (txn, __) ->
                             txn.readRowAsync(
                                 "Singers", Key.of(singerId), Collections.singleton(column)))
                     .then(
                         (txn, row) -> {
                           String name = row.getString(column);
                           txn.buffer(
                               Mutation.newUpdateBuilder("Singers")
                                   .set(column)
                                   .to(name.toUpperCase())
                                   .set("ChangeSetId")
                                   .to(manager.getChangeSetId())
                                   .build());
                           return ApiFutures.immediateFuture(null);
                         })
                     .commitAsync();
             try {
               commitTimestamp.get();
               break;
             } catch (AbortedException e) {
               Thread.sleep(e.getRetryDelayInMillis() / 1000);
               txnFut = manager.resetForRetryAsync();
             }
           }
         }
         
        Specified by:
        transactionManagerAsync in interface com.google.cloud.spanner.DatabaseClient
      • singleUse

        public com.google.cloud.spanner.ReadContext singleUse()
        Specified by:
        singleUse in interface com.google.cloud.spanner.DatabaseClient
      • singleUse

        public com.google.cloud.spanner.ReadContext singleUse​(com.google.cloud.spanner.TimestampBound bound)
        Specified by:
        singleUse in interface com.google.cloud.spanner.DatabaseClient
      • singleUseReadOnlyTransaction

        public com.google.cloud.spanner.ReadOnlyTransaction singleUseReadOnlyTransaction()
        Specified by:
        singleUseReadOnlyTransaction in interface com.google.cloud.spanner.DatabaseClient
      • singleUseReadOnlyTransaction

        public com.google.cloud.spanner.ReadOnlyTransaction singleUseReadOnlyTransaction​(com.google.cloud.spanner.TimestampBound bound)
        Specified by:
        singleUseReadOnlyTransaction in interface com.google.cloud.spanner.DatabaseClient
      • readOnlyTransaction

        public com.google.cloud.spanner.ReadOnlyTransaction readOnlyTransaction()
        Specified by:
        readOnlyTransaction in interface com.google.cloud.spanner.DatabaseClient
      • readOnlyTransaction

        public com.google.cloud.spanner.ReadOnlyTransaction readOnlyTransaction​(com.google.cloud.spanner.TimestampBound bound)
        Specified by:
        readOnlyTransaction in interface com.google.cloud.spanner.DatabaseClient
      • executePartitionedUpdate

        public long executePartitionedUpdate​(com.google.cloud.spanner.Statement stmt)
        Specified by:
        executePartitionedUpdate in interface com.google.cloud.spanner.DatabaseClient