21
21
import com .google .spanner .v1 .ReadRequest .LockHint ;
22
22
import com .google .spanner .v1 .ReadRequest .OrderBy ;
23
23
import com .google .spanner .v1 .RequestOptions .Priority ;
24
+ import com .google .spanner .v1 .TransactionOptions .IsolationLevel ;
24
25
import java .io .Serializable ;
25
26
import java .time .Duration ;
27
+ import java .util .Arrays ;
26
28
import java .util .Objects ;
29
+ import java .util .function .Predicate ;
30
+ import java .util .stream .Stream ;
27
31
28
32
/** Specifies options for various spanner operations */
29
33
public final class Options implements Serializable {
@@ -131,7 +135,29 @@ public interface UpdateAdminApiOption extends AdminApiOption {}
131
135
public interface QueryOption {}
132
136
133
137
/** Marker interface to mark options applicable to write operations */
134
- public interface TransactionOption {}
138
+ public interface TransactionOption {
139
+ Predicate <TransactionOption > isValidIsolationLevelOption =
140
+ txnOption ->
141
+ txnOption instanceof RepeatableReadOption || txnOption instanceof SerializableOption ;
142
+
143
+ /**
144
+ * Combines two arrays of TransactionOption, with primaryOptions taking precedence in case of
145
+ * conflicts. Note that {@link
146
+ * com.google.cloud.spanner.SpannerOptions.Builder.TransactionOptions} supports only the {@link
147
+ * IsolationLevel} TransactionOption, meaning spannerOptions will contain a maximum of one
148
+ * TransactionOption.
149
+ */
150
+ static TransactionOption [] combine (
151
+ TransactionOption [] primaryOptions , TransactionOption [] spannerOptions ) {
152
+ if (spannerOptions == null
153
+ || Arrays .stream (primaryOptions ).anyMatch (isValidIsolationLevelOption )) {
154
+ return primaryOptions ;
155
+ } else {
156
+ return Stream .concat (Arrays .stream (primaryOptions ), Arrays .stream (spannerOptions ))
157
+ .toArray (TransactionOption []::new );
158
+ }
159
+ }
160
+ }
135
161
136
162
/** Marker interface to mark options applicable to update operation. */
137
163
public interface UpdateOption {}
@@ -159,6 +185,22 @@ public static TransactionOption optimisticLock() {
159
185
return OPTIMISTIC_LOCK_OPTION ;
160
186
}
161
187
188
+ /**
189
+ * Specifying this instructs the transaction to request Repeatable Read Isolation Level from the
190
+ * backend.
191
+ */
192
+ public static TransactionOption repeatableReadIsolationLevel () {
193
+ return REPEATABLE_READ_OPTION ;
194
+ }
195
+
196
+ /**
197
+ * Specifying this instructs the transaction to request Serializable Isolation Level from the
198
+ * backend.
199
+ */
200
+ public static TransactionOption serializableIsolationLevel () {
201
+ return SERIALIZABLE_OPTION ;
202
+ }
203
+
162
204
/**
163
205
* Specifying this instructs the transaction to be excluded from being recorded in change streams
164
206
* with the DDL option `allow_txn_exclusion=true`. This does not exclude the transaction from
@@ -490,6 +532,26 @@ void appendToOptions(Options options) {
490
532
}
491
533
}
492
534
535
+ /** Option to request REPEATABLE READ isolation level for read/write transactions. */
536
+ static final class RepeatableReadOption extends InternalOption implements TransactionOption {
537
+ @ Override
538
+ void appendToOptions (Options options ) {
539
+ options .isolationLevel = IsolationLevel .REPEATABLE_READ ;
540
+ }
541
+ }
542
+
543
+ static final RepeatableReadOption REPEATABLE_READ_OPTION = new RepeatableReadOption ();
544
+
545
+ /** Option to request SERIALIZABLE isolation level for read/write transactions. */
546
+ static final class SerializableOption extends InternalOption implements TransactionOption {
547
+ @ Override
548
+ void appendToOptions (Options options ) {
549
+ options .isolationLevel = IsolationLevel .SERIALIZABLE ;
550
+ }
551
+ }
552
+
553
+ static final SerializableOption SERIALIZABLE_OPTION = new SerializableOption ();
554
+
493
555
private boolean withCommitStats ;
494
556
495
557
private Duration maxCommitDelay ;
@@ -512,6 +574,7 @@ void appendToOptions(Options options) {
512
574
private RpcOrderBy orderBy ;
513
575
private RpcLockHint lockHint ;
514
576
private Boolean lastStatement ;
577
+ private IsolationLevel isolationLevel ;
515
578
516
579
// Construction is via factory methods below.
517
580
private Options () {}
@@ -664,6 +727,10 @@ LockHint lockHint() {
664
727
return lockHint == null ? null : lockHint .proto ;
665
728
}
666
729
730
+ IsolationLevel isolationLevel () {
731
+ return isolationLevel ;
732
+ }
733
+
667
734
@ Override
668
735
public String toString () {
669
736
StringBuilder b = new StringBuilder ();
@@ -726,6 +793,9 @@ public String toString() {
726
793
if (lockHint != null ) {
727
794
b .append ("lockHint: " ).append (lockHint ).append (' ' );
728
795
}
796
+ if (isolationLevel != null ) {
797
+ b .append ("isolationLevel: " ).append (isolationLevel ).append (' ' );
798
+ }
729
799
return b .toString ();
730
800
}
731
801
@@ -767,7 +837,8 @@ public boolean equals(Object o) {
767
837
&& Objects .equals (directedReadOptions (), that .directedReadOptions ())
768
838
&& Objects .equals (orderBy (), that .orderBy ())
769
839
&& Objects .equals (isLastStatement (), that .isLastStatement ())
770
- && Objects .equals (lockHint (), that .lockHint ());
840
+ && Objects .equals (lockHint (), that .lockHint ())
841
+ && Objects .equals (isolationLevel (), that .isolationLevel ());
771
842
}
772
843
773
844
@ Override
@@ -833,6 +904,9 @@ public int hashCode() {
833
904
if (lockHint != null ) {
834
905
result = 31 * result + lockHint .hashCode ();
835
906
}
907
+ if (isolationLevel != null ) {
908
+ result = 31 * result + isolationLevel .hashCode ();
909
+ }
836
910
return result ;
837
911
}
838
912
0 commit comments