public interface DatabaseClient
Modifier and Type | Method and Description |
---|---|
long |
executePartitionedUpdate(Statement stmt,
Options.UpdateOption... options)
Returns the lower bound of rows modified by this DML statement.
|
default Dialect |
getDialect()
Returns the SQL dialect that is used by the database.
|
ReadOnlyTransaction |
readOnlyTransaction()
Returns a read-only transaction context in which a multiple reads and/or queries can be
performed using
TimestampBound.strong() concurrency. |
ReadOnlyTransaction |
readOnlyTransaction(TimestampBound bound)
Returns a read-only transaction context in which a multiple reads and/or queries can be
performed at the given timestamp bound.
|
TransactionRunner |
readWriteTransaction(Options.TransactionOption... options)
Returns a transaction runner for executing a single logical transaction with retries.
|
AsyncRunner |
runAsync(Options.TransactionOption... options)
Returns an asynchronous transaction runner for executing a single logical transaction with
retries.
|
ReadContext |
singleUse()
Returns a context in which a single read can be performed using
TimestampBound.strong()
concurrency. |
ReadContext |
singleUse(TimestampBound bound)
Returns a context in which a single read can be performed at the given timestamp bound.
|
ReadOnlyTransaction |
singleUseReadOnlyTransaction()
Returns a read-only transaction context in which a single read or query can be performed using
TimestampBound.strong() concurrency. |
ReadOnlyTransaction |
singleUseReadOnlyTransaction(TimestampBound bound)
Returns a read-only transaction context in which a single read or query can be performed at
given timestamp bound.
|
TransactionManager |
transactionManager(Options.TransactionOption... options)
Returns a transaction manager which allows manual management of transaction lifecycle.
|
AsyncTransactionManager |
transactionManagerAsync(Options.TransactionOption... options)
Returns an asynchronous transaction manager which allows manual management of transaction
lifecycle.
|
com.google.cloud.Timestamp |
write(Iterable<Mutation> mutations)
Writes the given mutations atomically to the database.
|
com.google.cloud.Timestamp |
writeAtLeastOnce(Iterable<Mutation> mutations)
Writes the given mutations atomically to the database without replay protection.
|
CommitResponse |
writeAtLeastOnceWithOptions(Iterable<Mutation> mutations,
Options.TransactionOption... options)
Writes the given mutations atomically to the database without replay protection.
|
CommitResponse |
writeWithOptions(Iterable<Mutation> mutations,
Options.TransactionOption... options)
Writes the given mutations atomically to the database with the given options.
|
default Dialect getDialect()
com.google.cloud.Timestamp write(Iterable<Mutation> mutations) throws SpannerException
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.
long singerId = my_singer_id;
Mutation mutation = Mutation.newInsertBuilder("Singer")
.set("SingerId")
.to(singerId)
.set("FirstName")
.to("Billy")
.set("LastName")
.to("Joel")
.build();
dbClient.write(Collections.singletonList(mutation));
SpannerException
CommitResponse writeWithOptions(Iterable<Mutation> mutations, Options.TransactionOption... options) throws SpannerException
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.
long singerId = my_singer_id;
Mutation mutation = Mutation.newInsertBuilder("Singer")
.set("SingerId")
.to(singerId)
.set("FirstName")
.to("Billy")
.set("LastName")
.to("Joel")
.build();
dbClient.writeWithOptions(
Collections.singletonList(mutation),
Options.priority(RpcPriority.HIGH));
Options for a transaction can include:
Options.priority(com.google.cloud.spanner.Options.RpcPriority)
: The Options.RpcPriority
to use for the commit request of the transaction. The priority will not be
applied to any other requests on the transaction.
Options.commitStats()
: Request that the server includes commit statistics in the
CommitResponse
.
SpannerException
com.google.cloud.Timestamp writeAtLeastOnce(Iterable<Mutation> mutations) throws SpannerException
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.
long singerId = my_singer_id;
Mutation mutation = Mutation.newInsertBuilder("Singers")
.set("SingerId")
.to(singerId)
.set("FirstName")
.to("Billy")
.set("LastName")
.to("Joel")
.build();
dbClient.writeAtLeastOnce(Collections.singletonList(mutation));
SpannerException
CommitResponse writeAtLeastOnceWithOptions(Iterable<Mutation> mutations, Options.TransactionOption... options) throws SpannerException
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.
long singerId = my_singer_id;
Mutation mutation = Mutation.newInsertBuilder("Singers")
.set("SingerId")
.to(singerId)
.set("FirstName")
.to("Billy")
.set("LastName")
.to("Joel")
.build();
dbClient.writeAtLeastOnceWithOptions(
Collections.singletonList(mutation),
Options.priority(RpcPriority.LOW));
Options for a transaction can include:
Options.priority(com.google.cloud.spanner.Options.RpcPriority)
: The Options.RpcPriority
to use for the commit request of the transaction. The priority will not be
applied to any other requests on the transaction.
Options.commitStats()
: Request that the server includes commit statistics in the
CommitResponse
.
SpannerException
ReadContext singleUse()
TimestampBound.strong()
concurrency. This method will return a ReadContext
that will not return the read
timestamp that was used by Cloud Spanner. If you want to be able to access the read timestamp,
you should use the method singleUseReadOnlyTransaction()
.
Example of single use.
long singerId = my_singer_id;
String column = "FirstName";
Struct row =
dbClient.singleUse().readRow("Singers", Key.of(singerId), Collections.singleton(column));
String firstName = row.getString(column);
ReadContext singleUse(TimestampBound bound)
ReadContext
that will not return the read timestamp that was used
by Cloud Spanner. If you want to be able to access the read timestamp, you should use the
method singleUseReadOnlyTransaction()
.
Example of single use with timestamp bound.
long singerId = my_singer_id;
String column = "FirstName";
Struct row = dbClient.singleUse(TimestampBound.ofMaxStaleness(10, TimeUnit.SECONDS))
.readRow("Singers", Key.of(singerId), Collections.singleton(column));
String firstName = row.getString(column);
bound
- the timestamp bound at which to perform the readReadOnlyTransaction singleUseReadOnlyTransaction()
TimestampBound.strong()
concurrency. This method differs from singleUse()
in
that the read timestamp used may be inspected after the read has returned data or finished
successfully.
Example of single use read only transaction.
long singerId = my_singer_id;
String column = "FirstName";
ReadOnlyTransaction txn = dbClient.singleUseReadOnlyTransaction();
Struct row = txn.readRow("Singers", Key.of(singerId), Collections.singleton(column));
row.getString(column);
Timestamp timestamp = txn.getReadTimestamp();
ReadOnlyTransaction singleUseReadOnlyTransaction(TimestampBound bound)
singleUse(TimestampBound)
in that the
read timestamp used may be inspected after the read has returned data or finished successfully.
Example of single use read only transaction with timestamp bound.
long singerId = my_singer_id;
String column = "FirstName";
ReadOnlyTransaction txn =
dbClient.singleUseReadOnlyTransaction(TimestampBound.ofMaxStaleness(10, TimeUnit.SECONDS));
Struct row = txn.readRow("Singers", Key.of(singerId), Collections.singleton(column));
row.getString(column);
Timestamp timestamp = txn.getReadTimestamp();
bound
- the timestamp bound at which to perform the readReadOnlyTransaction readOnlyTransaction()
TimestampBound.strong()
concurrency. All reads/queries will use the
same timestamp, and the timestamp can be inspected after any read/query has returned data or
finished successfully.
Example of read only transaction.
long singerId = my_singer_id;
long albumId = my_album_id;
String singerColumn = "FirstName";
String albumColumn = "AlbumTitle";
String albumTitle = null;
// ReadOnlyTransaction should be closed to prevent resource leak.
try (ReadOnlyTransaction txn = dbClient.readOnlyTransaction()) {
Struct singerRow =
txn.readRow("Singers", Key.of(singerId), Collections.singleton(singerColumn));
Struct albumRow =
txn.readRow("Albums", Key.of(singerId, albumId), Collections.singleton(albumColumn));
singerRow.getString(singerColumn);
albumTitle = albumRow.getString(albumColumn);
}
ReadOnlyTransaction readOnlyTransaction(TimestampBound bound)
Note that the bounded staleness modes, TimestampBound.Mode.MIN_READ_TIMESTAMP
and
TimestampBound.Mode.MAX_STALENESS
, are not supported for multi-use read-only
transactions.
Example of read only transaction with timestamp bound.
long singerId = my_singer_id;
long albumId = my_album_id;
String singerColumn = "FirstName";
String albumColumn = "AlbumTitle";
String albumTitle = null;
// ReadOnlyTransaction should be closed to prevent resource leak.
try (ReadOnlyTransaction txn =
dbClient.readOnlyTransaction(TimestampBound.ofExactStaleness(10, TimeUnit.SECONDS))) {
Struct singerRow =
txn.readRow("Singers", Key.of(singerId), Collections.singleton(singerColumn));
Struct albumRow =
txn.readRow("Albums", Key.of(singerId, albumId), Collections.singleton(albumColumn));
singerRow.getString(singerColumn);
albumTitle = albumRow.getString(albumColumn);
}
bound
- the timestamp bound at which to perform the readTransactionRunner readWriteTransaction(Options.TransactionOption... options)
Example of a read write transaction.
long singerId = my_singer_id;
TransactionRunner runner = dbClient.readWriteTransaction();
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()).build());
return null;
}
});
Options for a transaction can include:
Options.priority(com.google.cloud.spanner.Options.RpcPriority)
: The Options.RpcPriority
to use for the commit request of the transaction. The priority will not be
applied to any other requests on the transaction.
Options.commitStats()
: Request that the server includes commit statistics in the
CommitResponse
.
TransactionManager transactionManager(Options.TransactionOption... options)
#readWriteTransaction()
API instead.
Example of using TransactionManager
.
long singerId = my_singer_id;
try (TransactionManager manager = dbClient.transactionManager()) {
TransactionContext transaction = manager.begin();
while (true) {
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()).build());
try {
manager.commit();
break;
} catch (AbortedException e) {
Thread.sleep(e.getRetryDelayInMillis());
transaction = manager.resetForRetry();
}
}
}
Options for a transaction can include:
Options.priority(com.google.cloud.spanner.Options.RpcPriority)
: The Options.RpcPriority
to use for the commit request of the transaction. The priority will not be
applied to any other requests on the transaction.
Options.commitStats()
: Request that the server includes commit statistics in the
CommitResponse
.
AsyncRunner runAsync(Options.TransactionOption... options)
Example of a read write transaction.
Executor executor = Executors.newSingleThreadExecutor();
final long singerId = my_singer_id;
AsyncRunner runner = client.runAsync();
ApiFuture rowCount =
runner.runAsync(
() -> {
String column = "FirstName";
Struct row =
txn.readRow("Singers", Key.of(singerId), Collections.singleton("Name"));
String name = row.getString("Name");
return txn.executeUpdateAsync(
Statement.newBuilder("UPDATE Singers SET Name=@name WHERE SingerId=@id")
.bind("id")
.to(singerId)
.bind("name")
.to(name.toUpperCase())
.build());
},
executor);
Options for a transaction can include:
Options.priority(com.google.cloud.spanner.Options.RpcPriority)
: The Options.RpcPriority
to use for the commit request of the transaction. The priority will not be
applied to any other requests on the transaction.
Options.commitStats()
: Request that the server includes commit statistics in the
CommitResponse
.
AsyncTransactionManager transactionManagerAsync(Options.TransactionOption... options)
#runAsync()
API instead.
Example of using AsyncTransactionManager
.
long singerId = 1L;
try (AsyncTransactionManager manager = client.transactionManagerAsync()) {
TransactionContextFuture transactionFuture = manager.beginAsync();
while (true) {
String column = "FirstName";
CommitTimestampFuture commitTimestamp =
transactionFuture
.then(
(transaction, __) ->
transaction.readRowAsync(
"Singers", Key.of(singerId), Collections.singleton(column)))
.then(
(transaction, row) -> {
String name = row.getString(column);
return transaction.bufferAsync(
Mutation.newUpdateBuilder("Singers")
.set(column)
.to(name.toUpperCase())
.build());
})
.commitAsync();
try {
commitTimestamp.get();
break;
} catch (AbortedException e) {
Thread.sleep(e.getRetryDelayInMillis());
transactionFuture = manager.resetForRetryAsync();
}
}
}
Options for a transaction can include:
Options for a transaction can include:
Options.priority(com.google.cloud.spanner.Options.RpcPriority)
: The Options.RpcPriority
to use for the commit request of the transaction. The priority will not be
applied to any other requests on the transaction.
Options.commitStats()
: Request that the server includes commit statistics in the
CommitResponse
.
long executePartitionedUpdate(Statement stmt, Options.UpdateOption... options)
The method will block until the update is complete. Running a DML statement with this method does not offer exactly once semantics, and therefore the DML statement should be idempotent. The DML statement must be fully-partitionable. Specifically, the statement must be expressible as the union of many statements which each access only a single row of the table. This is a Partitioned DML transaction in which a single Partitioned DML statement is executed. Partitioned DML partitions the key space and runs the DML statement over each partition in parallel using separate, internal transactions that commit independently. Partitioned DML transactions do not need to be committed.
Partitioned DML updates are used to execute a single DML statement with a different
execution strategy that provides different, and often better, scalability properties for large,
table-wide operations than DML in a #readWriteTransaction()
transaction. Smaller scoped
statements, such as an OLTP workload, should prefer using TransactionContext#executeUpdate(Statement)
with #readWriteTransaction()
.
That said, Partitioned DML is not a drop-in replacement for standard DML used in #readWriteTransaction()
.
Given the above, Partitioned DML is good fit for large, database-wide, operations that are idempotent, such as deleting old rows from a very large table.
Copyright © 2022 Google LLC. All rights reserved.