Skip to content

Commit 0fd4098

Browse files
authored
docs: Add snippet for ReadLockMode configuration at client and transaction (#4305)
* docs: Add snippet for ReadLockMode configuration at client and transaction * read write IT using txn options * updated README with the sample link
1 parent aaae90e commit 0fd4098

File tree

4 files changed

+169
-48
lines changed

4 files changed

+169
-48
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ Samples are in the [`samples/`](https://github.com/googleapis/java-spanner/tree/
374374
| Get Database Ddl Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/GetDatabaseDdlSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/GetDatabaseDdlSample.java) |
375375
| Get Instance Config Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/GetInstanceConfigSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/GetInstanceConfigSample.java) |
376376
| Insert Using Dml Returning Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/InsertUsingDmlReturningSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/InsertUsingDmlReturningSample.java) |
377+
| Isolation Level and Read Lock Mode Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/IsolationLevelAndReadLockModeSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/IsolationLevelAndReadLockModeSample.java) |
377378
| Last Statement Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/LastStatementSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/LastStatementSample.java) |
378379
| List Backup Schedules Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/ListBackupSchedulesSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/ListBackupSchedulesSample.java) |
379380
| List Database Roles | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/ListDatabaseRoles.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/ListDatabaseRoles.java) |

google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@
4141
import com.google.cloud.spanner.ReadContext;
4242
import com.google.cloud.spanner.ReadOnlyTransaction;
4343
import com.google.cloud.spanner.ResultSet;
44+
import com.google.cloud.spanner.Spanner;
4445
import com.google.cloud.spanner.SpannerException;
46+
import com.google.cloud.spanner.SpannerOptions;
47+
import com.google.cloud.spanner.SpannerOptions.Builder.DefaultReadWriteTransactionOptions;
4548
import com.google.cloud.spanner.Statement;
4649
import com.google.cloud.spanner.Struct;
4750
import com.google.cloud.spanner.TimestampBound;
@@ -52,6 +55,8 @@
5255
import com.google.common.collect.Sets;
5356
import com.google.common.util.concurrent.SettableFuture;
5457
import com.google.common.util.concurrent.Uninterruptibles;
58+
import com.google.spanner.v1.TransactionOptions.IsolationLevel;
59+
import com.google.spanner.v1.TransactionOptions.ReadWrite.ReadLockMode;
5560
import java.util.ArrayList;
5661
import java.util.Arrays;
5762
import java.util.Collections;
@@ -213,6 +218,54 @@ public void basicsUsingQuery() throws InterruptedException {
213218
});
214219
}
215220

221+
@Test
222+
public void isolationLevelAndReadLockModeSetAtClientLevelTest() {
223+
SpannerOptions options =
224+
env.getTestHelper().getOptions().toBuilder()
225+
.setDefaultTransactionOptions(
226+
DefaultReadWriteTransactionOptions.newBuilder()
227+
.setIsolationLevel(IsolationLevel.REPEATABLE_READ)
228+
.setReadLockMode(ReadLockMode.OPTIMISTIC)
229+
.build())
230+
.build();
231+
try (Spanner spanner = options.getService()) {
232+
DatabaseClient client = spanner.getDatabaseClient(db.getId());
233+
Long updatedRows =
234+
client
235+
.readWriteTransaction()
236+
.run(
237+
transaction ->
238+
transaction.executeUpdate(
239+
Statement.of("INSERT INTO T (K, V) VALUES ('test1', 2)")));
240+
assertThat(updatedRows).isEqualTo(1L);
241+
}
242+
}
243+
244+
@Test
245+
public void isolationLevelAndReadLockModeSetAtClientAndTxnLevelTest() {
246+
SpannerOptions options =
247+
env.getTestHelper().getOptions().toBuilder()
248+
.setDefaultTransactionOptions(
249+
DefaultReadWriteTransactionOptions.newBuilder()
250+
.setIsolationLevel(IsolationLevel.REPEATABLE_READ)
251+
.setReadLockMode(ReadLockMode.OPTIMISTIC)
252+
.build())
253+
.build();
254+
try (Spanner spanner = options.getService()) {
255+
DatabaseClient client = spanner.getDatabaseClient(db.getId());
256+
Long updatedRows =
257+
client
258+
.readWriteTransaction(
259+
Options.isolationLevel(IsolationLevel.SERIALIZABLE),
260+
Options.readLockMode(ReadLockMode.PESSIMISTIC))
261+
.run(
262+
transaction ->
263+
transaction.executeUpdate(
264+
Statement.of("INSERT INTO T (K, V) VALUES ('test1', 2)")));
265+
assertThat(updatedRows).isEqualTo(1L);
266+
}
267+
}
268+
216269
@Test
217270
public void userExceptionPreventsCommit() {
218271
class UserException extends Exception {
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.spanner;
18+
19+
import com.google.cloud.spanner.DatabaseClient;
20+
import com.google.cloud.spanner.DatabaseId;
21+
import com.google.cloud.spanner.Options;
22+
import com.google.cloud.spanner.ResultSet;
23+
import com.google.cloud.spanner.Spanner;
24+
import com.google.cloud.spanner.SpannerOptions;
25+
import com.google.cloud.spanner.SpannerOptions.Builder.DefaultReadWriteTransactionOptions;
26+
import com.google.cloud.spanner.Statement;
27+
import com.google.spanner.v1.TransactionOptions.IsolationLevel;
28+
import com.google.spanner.v1.TransactionOptions.ReadWrite.ReadLockMode;
29+
30+
public class IsolationLevelAndReadLockModeSample {
31+
32+
// [START spanner_isolation_level]
33+
static void isolationLevelSetting(DatabaseId db) {
34+
// The isolation level specified at the client-level will be applied to all
35+
// RW transactions.
36+
DefaultReadWriteTransactionOptions transactionOptions =
37+
DefaultReadWriteTransactionOptions.newBuilder()
38+
.setIsolationLevel(IsolationLevel.SERIALIZABLE)
39+
.build();
40+
SpannerOptions options =
41+
SpannerOptions.newBuilder()
42+
.setDefaultTransactionOptions(transactionOptions)
43+
.build();
44+
Spanner spanner = options.getService();
45+
DatabaseClient dbClient = spanner.getDatabaseClient(db);
46+
dbClient
47+
// The isolation level specified at the transaction-level takes precedence
48+
// over the isolation level configured at the client-level.
49+
.readWriteTransaction(Options.isolationLevel(IsolationLevel.REPEATABLE_READ))
50+
.run(transaction -> {
51+
// Read an AlbumTitle.
52+
String selectSql =
53+
"SELECT AlbumTitle from Albums WHERE SingerId = 1 and AlbumId = 1";
54+
String title = null;
55+
try (ResultSet resultSet = transaction.executeQuery(Statement.of(selectSql))) {
56+
if (resultSet.next()) {
57+
title = resultSet.getString("AlbumTitle");
58+
}
59+
}
60+
System.out.printf("Current album title: %s\n", title);
61+
62+
// Update the title.
63+
String updateSql =
64+
"UPDATE Albums "
65+
+ "SET AlbumTitle = 'New Album Title' "
66+
+ "WHERE SingerId = 1 and AlbumId = 1";
67+
long rowCount = transaction.executeUpdate(Statement.of(updateSql));
68+
System.out.printf("%d record updated.\n", rowCount);
69+
return null;
70+
});
71+
}
72+
// [END spanner_isolation_level]
73+
74+
// [START spanner_read_lock_mode]
75+
static void readLockModeSetting(DatabaseId db) {
76+
// The read lock mode specified at the client-level will be applied to all
77+
// RW transactions.
78+
DefaultReadWriteTransactionOptions transactionOptions =
79+
DefaultReadWriteTransactionOptions.newBuilder()
80+
.setReadLockMode(ReadLockMode.OPTIMISTIC)
81+
.build();
82+
SpannerOptions options =
83+
SpannerOptions.newBuilder()
84+
.setDefaultTransactionOptions(transactionOptions)
85+
.build();
86+
Spanner spanner = options.getService();
87+
DatabaseClient dbClient = spanner.getDatabaseClient(db);
88+
dbClient
89+
// The read lock mode specified at the transaction-level takes precedence
90+
// over the read lock mode configured at the client-level.
91+
.readWriteTransaction(Options.readLockMode(ReadLockMode.PESSIMISTIC))
92+
.run(transaction -> {
93+
// Read an AlbumTitle.
94+
String selectSql =
95+
"SELECT AlbumTitle from Albums WHERE SingerId = 1 and AlbumId = 1";
96+
String title = null;
97+
try (ResultSet resultSet = transaction.executeQuery(Statement.of(selectSql))) {
98+
if (resultSet.next()) {
99+
title = resultSet.getString("AlbumTitle");
100+
}
101+
}
102+
System.out.printf("Current album title: %s\n", title);
103+
104+
// Update the title.
105+
String updateSql =
106+
"UPDATE Albums "
107+
+ "SET AlbumTitle = 'New Album Title' "
108+
+ "WHERE SingerId = 1 and AlbumId = 1";
109+
long rowCount = transaction.executeUpdate(Statement.of(updateSql));
110+
System.out.printf("%d record updated.\n", rowCount);
111+
return null;
112+
});
113+
}
114+
// [END spanner_read_lock_mode]
115+
}

samples/snippets/src/main/java/com/example/spanner/SpannerSample.java

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,13 @@
3232
import com.google.cloud.spanner.KeyRange;
3333
import com.google.cloud.spanner.KeySet;
3434
import com.google.cloud.spanner.Mutation;
35-
import com.google.cloud.spanner.Options;
3635
import com.google.cloud.spanner.ReadOnlyTransaction;
3736
import com.google.cloud.spanner.ResultSet;
3837
import com.google.cloud.spanner.Spanner;
3938
import com.google.cloud.spanner.SpannerBatchUpdateException;
4039
import com.google.cloud.spanner.SpannerException;
4140
import com.google.cloud.spanner.SpannerExceptionFactory;
4241
import com.google.cloud.spanner.SpannerOptions;
43-
import com.google.cloud.spanner.SpannerOptions.Builder.DefaultReadWriteTransactionOptions;
4442
import com.google.cloud.spanner.Statement;
4543
import com.google.cloud.spanner.Struct;
4644
import com.google.cloud.spanner.TimestampBound;
@@ -73,7 +71,6 @@
7371
import com.google.spanner.admin.database.v1.RestoreDatabaseRequest;
7472
import com.google.spanner.admin.database.v1.RestoreInfo;
7573
import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions;
76-
import com.google.spanner.v1.TransactionOptions.IsolationLevel;
7774
import java.math.BigDecimal;
7875
import java.time.Instant;
7976
import java.time.ZoneId;
@@ -1555,47 +1552,6 @@ static void queryWithQueryOptions(DatabaseClient dbClient) {
15551552
}
15561553
// [END spanner_query_with_query_options]
15571554

1558-
// [START spanner_isolation_level]
1559-
static void isolationLevelSetting(DatabaseId db) {
1560-
// The isolation level specified at the client-level will be applied to all
1561-
// RW transactions.
1562-
DefaultReadWriteTransactionOptions transactionOptions =
1563-
DefaultReadWriteTransactionOptions.newBuilder()
1564-
.setIsolationLevel(IsolationLevel.SERIALIZABLE)
1565-
.build();
1566-
SpannerOptions options =
1567-
SpannerOptions.newBuilder()
1568-
.setDefaultTransactionOptions(transactionOptions)
1569-
.build();
1570-
Spanner spanner = options.getService();
1571-
DatabaseClient dbClient = spanner.getDatabaseClient(db);
1572-
dbClient
1573-
// The isolation level specified at the transaction-level takes precedence
1574-
// over the isolation level configured at the client-level.
1575-
.readWriteTransaction(Options.isolationLevel(IsolationLevel.REPEATABLE_READ))
1576-
.run(transaction -> {
1577-
// Read an AlbumTitle.
1578-
String selectSql =
1579-
"SELECT AlbumTitle from Albums WHERE SingerId = 1 and AlbumId = 1";
1580-
ResultSet resultSet = transaction.executeQuery(Statement.of(selectSql));
1581-
String title = null;
1582-
while (resultSet.next()) {
1583-
title = resultSet.getString("AlbumTitle");
1584-
}
1585-
System.out.printf("Current album title: %s\n", title);
1586-
1587-
// Update the title.
1588-
String updateSql =
1589-
"UPDATE Albums "
1590-
+ "SET AlbumTitle = 'New Album Title' "
1591-
+ "WHERE SingerId = 1 and AlbumId = 1";
1592-
long rowCount = transaction.executeUpdate(Statement.of(updateSql));
1593-
System.out.printf("%d record updated.\n", rowCount);
1594-
return null;
1595-
});
1596-
}
1597-
// [END spanner_isolation_level]
1598-
15991555
// [START spanner_create_backup]
16001556
static void createBackup(DatabaseAdminClient dbAdminClient, String projectId, String instanceId,
16011557
String databaseId, String backupId, Timestamp versionTime) {
@@ -2152,9 +2108,6 @@ static void run(
21522108
case "querywithqueryoptions":
21532109
queryWithQueryOptions(dbClient);
21542110
break;
2155-
case "isolationlevelsettings":
2156-
isolationLevelSetting(database);
2157-
break;
21582111
case "createbackup":
21592112
createBackup(dbAdminClient, database.getInstanceId().getProject(),
21602113
database.getInstanceId().getInstance(), database.getDatabase(),
@@ -2265,7 +2218,6 @@ static void printUsageAndExit() {
22652218
System.err.println(" SpannerExample querywithtimestampparameter my-instance example-db");
22662219
System.err.println(" SpannerExample clientwithqueryoptions my-instance example-db");
22672220
System.err.println(" SpannerExample querywithqueryoptions my-instance example-db");
2268-
System.err.println(" SpannerExample isolationlevelsettings my-instance example-db");
22692221
System.err.println(" SpannerExample createbackup my-instance example-db");
22702222
System.err.println(" SpannerExample listbackups my-instance example-db");
22712223
System.err.println(" SpannerExample listbackupoperations my-instance example-db backup-id");

0 commit comments

Comments
 (0)