22
22
import com .google .spanner .v1 .ReadRequest .OrderBy ;
23
23
import com .google .spanner .v1 .RequestOptions .Priority ;
24
24
import com .google .spanner .v1 .TransactionOptions .IsolationLevel ;
25
+ import com .google .spanner .v1 .TransactionOptions .ReadWrite .ReadLockMode ;
25
26
import java .io .Serializable ;
26
27
import java .time .Duration ;
27
28
import java .util .Objects ;
@@ -155,9 +156,44 @@ public static TransactionOption commitStats() {
155
156
* process in the commit phase (when any needed locks are acquired). The validation process
156
157
* succeeds only if there are no conflicting committed transactions (that committed mutations to
157
158
* the read data at a commit timestamp after the read timestamp).
159
+ *
160
+ * @deprecated Use {@link Options#readLockMode(ReadLockMode)} instead.
158
161
*/
162
+ @ Deprecated
159
163
public static TransactionOption optimisticLock () {
160
- return OPTIMISTIC_LOCK_OPTION ;
164
+ return Options .readLockMode (ReadLockMode .OPTIMISTIC );
165
+ }
166
+
167
+ /**
168
+ * Returns a {@link TransactionOption} to set the desired {@link ReadLockMode} for a read-write
169
+ * transaction.
170
+ *
171
+ * <p>This option controls the locking behavior for read operations and queries within a
172
+ * read-write transaction. It works in conjunction with the transaction's {@link IsolationLevel}.
173
+ *
174
+ * <ul>
175
+ * <li>{@link ReadLockMode#PESSIMISTIC}: Read locks are acquired immediately on read. This mode
176
+ * primarily applies to transactions with {@code SERIALIZABLE} isolation.
177
+ * <li>{@link ReadLockMode#OPTIMISTIC}: Locks for reads within the transaction are not acquired
178
+ * on read. Instead the locks are acquired on a commit to validate that read/queried data
179
+ * has not changed since the transaction started. If a conflict is detected, the transaction
180
+ * will fail. This mode applies to transactions with {@code SERIALIZABLE} isolation.
181
+ * <li>{@link ReadLockMode#READ_LOCK_MODE_UNSPECIFIED}: This is the default if no mode is set.
182
+ * The locking behavior depends on the isolation level:
183
+ * <ul>
184
+ * <li>For {@code REPEATABLE_READ} isolation: Locking semantics default to {@code
185
+ * OPTIMISTIC}. However, validation checks at commit are only performed for reads
186
+ * within queries using {@code SELECT FOR UPDATE}, statements with {@code
187
+ * LOCK_SCANNED_RANGES} hints, or DML statements. <br>
188
+ * Note: It is an error to explicitly set {@code ReadLockMode} when the isolation
189
+ * level is {@code REPEATABLE_READ}.
190
+ * <li>For all other isolation levels: If the read lock mode is not set, it defaults to
191
+ * {@code PESSIMISTIC} locking.
192
+ * </ul>
193
+ * </ul>
194
+ */
195
+ public static TransactionOption readLockMode (ReadLockMode readLockMode ) {
196
+ return new ReadLockModeOption (readLockMode );
161
197
}
162
198
163
199
/**
@@ -367,16 +403,6 @@ void appendToOptions(Options options) {
367
403
}
368
404
}
369
405
370
- /** Option to request Optimistic Concurrency Control for read/write transactions. */
371
- static final class OptimisticLockOption extends InternalOption implements TransactionOption {
372
- @ Override
373
- void appendToOptions (Options options ) {
374
- options .withOptimisticLock = true ;
375
- }
376
- }
377
-
378
- static final OptimisticLockOption OPTIMISTIC_LOCK_OPTION = new OptimisticLockOption ();
379
-
380
406
/** Option to request the transaction to be excluded from change streams. */
381
407
static final class ExcludeTxnFromChangeStreamsOption extends InternalOption
382
408
implements UpdateTransactionOption {
@@ -516,6 +542,20 @@ void appendToOptions(Options options) {
516
542
}
517
543
}
518
544
545
+ /** Option to set read lock mode for read/write transactions. */
546
+ static final class ReadLockModeOption extends InternalOption implements TransactionOption {
547
+ private final ReadLockMode readLockMode ;
548
+
549
+ public ReadLockModeOption (ReadLockMode readLockMode ) {
550
+ this .readLockMode = readLockMode ;
551
+ }
552
+
553
+ @ Override
554
+ void appendToOptions (Options options ) {
555
+ options .readLockMode = readLockMode ;
556
+ }
557
+ }
558
+
519
559
private boolean withCommitStats ;
520
560
521
561
private Duration maxCommitDelay ;
@@ -530,7 +570,6 @@ void appendToOptions(Options options) {
530
570
private String tag ;
531
571
private String etag ;
532
572
private Boolean validateOnly ;
533
- private Boolean withOptimisticLock ;
534
573
private Boolean withExcludeTxnFromChangeStreams ;
535
574
private Boolean dataBoostEnabled ;
536
575
private DirectedReadOptions directedReadOptions ;
@@ -540,6 +579,7 @@ void appendToOptions(Options options) {
540
579
private Boolean lastStatement ;
541
580
private IsolationLevel isolationLevel ;
542
581
private XGoogSpannerRequestId reqId ;
582
+ private ReadLockMode readLockMode ;
543
583
544
584
// Construction is via factory methods below.
545
585
private Options () {}
@@ -644,10 +684,6 @@ Boolean validateOnly() {
644
684
return validateOnly ;
645
685
}
646
686
647
- Boolean withOptimisticLock () {
648
- return withOptimisticLock ;
649
- }
650
-
651
687
Boolean withExcludeTxnFromChangeStreams () {
652
688
return withExcludeTxnFromChangeStreams ;
653
689
}
@@ -704,6 +740,10 @@ IsolationLevel isolationLevel() {
704
740
return isolationLevel ;
705
741
}
706
742
743
+ ReadLockMode readLockMode () {
744
+ return readLockMode ;
745
+ }
746
+
707
747
@ Override
708
748
public String toString () {
709
749
StringBuilder b = new StringBuilder ();
@@ -740,9 +780,6 @@ public String toString() {
740
780
if (validateOnly != null ) {
741
781
b .append ("validateOnly: " ).append (validateOnly ).append (' ' );
742
782
}
743
- if (withOptimisticLock != null ) {
744
- b .append ("withOptimisticLock: " ).append (withOptimisticLock ).append (' ' );
745
- }
746
783
if (withExcludeTxnFromChangeStreams != null ) {
747
784
b .append ("withExcludeTxnFromChangeStreams: " )
748
785
.append (withExcludeTxnFromChangeStreams )
@@ -772,6 +809,9 @@ public String toString() {
772
809
if (reqId != null ) {
773
810
b .append ("requestId: " ).append (reqId .toString ());
774
811
}
812
+ if (readLockMode != null ) {
813
+ b .append ("readLockMode: " ).append (readLockMode ).append (' ' );
814
+ }
775
815
return b .toString ();
776
816
}
777
817
@@ -807,15 +847,15 @@ public boolean equals(Object o) {
807
847
&& Objects .equals (tag (), that .tag ())
808
848
&& Objects .equals (etag (), that .etag ())
809
849
&& Objects .equals (validateOnly (), that .validateOnly ())
810
- && Objects .equals (withOptimisticLock (), that .withOptimisticLock ())
811
850
&& Objects .equals (withExcludeTxnFromChangeStreams (), that .withExcludeTxnFromChangeStreams ())
812
851
&& Objects .equals (dataBoostEnabled (), that .dataBoostEnabled ())
813
852
&& Objects .equals (directedReadOptions (), that .directedReadOptions ())
814
853
&& Objects .equals (orderBy (), that .orderBy ())
815
854
&& Objects .equals (isLastStatement (), that .isLastStatement ())
816
855
&& Objects .equals (lockHint (), that .lockHint ())
817
856
&& Objects .equals (isolationLevel (), that .isolationLevel ())
818
- && Objects .equals (reqId (), that .reqId ());
857
+ && Objects .equals (reqId (), that .reqId ())
858
+ && Objects .equals (readLockMode (), that .readLockMode ());
819
859
}
820
860
821
861
@ Override
@@ -857,9 +897,6 @@ public int hashCode() {
857
897
if (validateOnly != null ) {
858
898
result = 31 * result + validateOnly .hashCode ();
859
899
}
860
- if (withOptimisticLock != null ) {
861
- result = 31 * result + withOptimisticLock .hashCode ();
862
- }
863
900
if (withExcludeTxnFromChangeStreams != null ) {
864
901
result = 31 * result + withExcludeTxnFromChangeStreams .hashCode ();
865
902
}
@@ -887,6 +924,9 @@ public int hashCode() {
887
924
if (reqId != null ) {
888
925
result = 31 * result + reqId .hashCode ();
889
926
}
927
+ if (readLockMode != null ) {
928
+ result = 31 * result + readLockMode .hashCode ();
929
+ }
890
930
return result ;
891
931
}
892
932
0 commit comments