2222import  com .google .spanner .v1 .ReadRequest .OrderBy ;
2323import  com .google .spanner .v1 .RequestOptions .Priority ;
2424import  com .google .spanner .v1 .TransactionOptions .IsolationLevel ;
25+ import  com .google .spanner .v1 .TransactionOptions .ReadWrite .ReadLockMode ;
2526import  java .io .Serializable ;
2627import  java .time .Duration ;
2728import  java .util .Objects ;
@@ -155,9 +156,50 @@ public static TransactionOption commitStats() {
155156   * process in the commit phase (when any needed locks are acquired). The validation process 
156157   * succeeds only if there are no conflicting committed transactions (that committed mutations to 
157158   * the read data at a commit timestamp after the read timestamp). 
159+    * 
160+    * @deprecated Use {@link Options#readLockMode(ReadLockMode)} instead. 
158161   */ 
162+   @ Deprecated 
159163  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+    *       only applies to {@code SERIALIZABLE} isolation. This mode prevents concurrent 
177+    *       modifications by locking data throughout the transaction. This reduces commit-time aborts 
178+    *       due to conflicts but can increase how long transactions wait for locks and the overall 
179+    *       contention. 
180+    *   <li>{@link ReadLockMode#OPTIMISTIC}: Locks for reads within the transaction are not acquired 
181+    *       on read. Instead the locks are acquired on commit to validate that read/queried data has 
182+    *       not changed since the transaction started. If a conflict is detected, the transaction 
183+    *       will fail. This mode only applies to {@code SERIALIZABLE} isolation. This mode defers 
184+    *       locking until commit, which can reduce contention and improve throughput. However, be 
185+    *       aware that this increases the risk of transaction aborts if there's significant write 
186+    *       competition on the same data. 
187+    *   <li>{@link ReadLockMode#READ_LOCK_MODE_UNSPECIFIED}: This is the default if no mode is set. 
188+    *       The locking behavior depends on the isolation level: 
189+    *       <ul> 
190+    *         <li>For {@code REPEATABLE_READ} isolation: Locking semantics default to {@code 
191+    *             OPTIMISTIC}. However, validation checks at commit are only performed for queries 
192+    *             using {@code SELECT FOR UPDATE}, statements with {@code LOCK_SCANNED_RANGES} hints, 
193+    *             and DML statements. <br> 
194+    *             Note: It is an error to explicitly set {@code ReadLockMode} when the isolation 
195+    *             level is {@code REPEATABLE_READ}. 
196+    *         <li>For all other isolation levels: If the read lock mode is not set, it defaults to 
197+    *             {@code PESSIMISTIC} locking. 
198+    *       </ul> 
199+    * </ul> 
200+    */ 
201+   public  static  TransactionOption  readLockMode (ReadLockMode  readLockMode ) {
202+     return  new  ReadLockModeOption (readLockMode );
161203  }
162204
163205  /** 
@@ -367,16 +409,6 @@ void appendToOptions(Options options) {
367409    }
368410  }
369411
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- 
380412  /** Option to request the transaction to be excluded from change streams. */ 
381413  static  final  class  ExcludeTxnFromChangeStreamsOption  extends  InternalOption 
382414      implements  UpdateTransactionOption  {
@@ -516,6 +548,20 @@ void appendToOptions(Options options) {
516548    }
517549  }
518550
551+   /** Option to set read lock mode for read/write transactions. */ 
552+   static  final  class  ReadLockModeOption  extends  InternalOption  implements  TransactionOption  {
553+     private  final  ReadLockMode  readLockMode ;
554+ 
555+     public  ReadLockModeOption (ReadLockMode  readLockMode ) {
556+       this .readLockMode  = readLockMode ;
557+     }
558+ 
559+     @ Override 
560+     void  appendToOptions (Options  options ) {
561+       options .readLockMode  = readLockMode ;
562+     }
563+   }
564+ 
519565  private  boolean  withCommitStats ;
520566
521567  private  Duration  maxCommitDelay ;
@@ -530,7 +576,6 @@ void appendToOptions(Options options) {
530576  private  String  tag ;
531577  private  String  etag ;
532578  private  Boolean  validateOnly ;
533-   private  Boolean  withOptimisticLock ;
534579  private  Boolean  withExcludeTxnFromChangeStreams ;
535580  private  Boolean  dataBoostEnabled ;
536581  private  DirectedReadOptions  directedReadOptions ;
@@ -540,6 +585,7 @@ void appendToOptions(Options options) {
540585  private  Boolean  lastStatement ;
541586  private  IsolationLevel  isolationLevel ;
542587  private  XGoogSpannerRequestId  reqId ;
588+   private  ReadLockMode  readLockMode ;
543589
544590  // Construction is via factory methods below. 
545591  private  Options () {}
@@ -644,10 +690,6 @@ Boolean validateOnly() {
644690    return  validateOnly ;
645691  }
646692
647-   Boolean  withOptimisticLock () {
648-     return  withOptimisticLock ;
649-   }
650- 
651693  Boolean  withExcludeTxnFromChangeStreams () {
652694    return  withExcludeTxnFromChangeStreams ;
653695  }
@@ -704,6 +746,10 @@ IsolationLevel isolationLevel() {
704746    return  isolationLevel ;
705747  }
706748
749+   ReadLockMode  readLockMode () {
750+     return  readLockMode ;
751+   }
752+ 
707753  @ Override 
708754  public  String  toString () {
709755    StringBuilder  b  = new  StringBuilder ();
@@ -740,9 +786,6 @@ public String toString() {
740786    if  (validateOnly  != null ) {
741787      b .append ("validateOnly: " ).append (validateOnly ).append (' ' );
742788    }
743-     if  (withOptimisticLock  != null ) {
744-       b .append ("withOptimisticLock: " ).append (withOptimisticLock ).append (' ' );
745-     }
746789    if  (withExcludeTxnFromChangeStreams  != null ) {
747790      b .append ("withExcludeTxnFromChangeStreams: " )
748791          .append (withExcludeTxnFromChangeStreams )
@@ -772,6 +815,9 @@ public String toString() {
772815    if  (reqId  != null ) {
773816      b .append ("requestId: " ).append (reqId .toString ());
774817    }
818+     if  (readLockMode  != null ) {
819+       b .append ("readLockMode: " ).append (readLockMode ).append (' ' );
820+     }
775821    return  b .toString ();
776822  }
777823
@@ -807,15 +853,15 @@ public boolean equals(Object o) {
807853        && Objects .equals (tag (), that .tag ())
808854        && Objects .equals (etag (), that .etag ())
809855        && Objects .equals (validateOnly (), that .validateOnly ())
810-         && Objects .equals (withOptimisticLock (), that .withOptimisticLock ())
811856        && Objects .equals (withExcludeTxnFromChangeStreams (), that .withExcludeTxnFromChangeStreams ())
812857        && Objects .equals (dataBoostEnabled (), that .dataBoostEnabled ())
813858        && Objects .equals (directedReadOptions (), that .directedReadOptions ())
814859        && Objects .equals (orderBy (), that .orderBy ())
815860        && Objects .equals (isLastStatement (), that .isLastStatement ())
816861        && Objects .equals (lockHint (), that .lockHint ())
817862        && Objects .equals (isolationLevel (), that .isolationLevel ())
818-         && Objects .equals (reqId (), that .reqId ());
863+         && Objects .equals (reqId (), that .reqId ())
864+         && Objects .equals (readLockMode (), that .readLockMode ());
819865  }
820866
821867  @ Override 
@@ -857,9 +903,6 @@ public int hashCode() {
857903    if  (validateOnly  != null ) {
858904      result  = 31  * result  + validateOnly .hashCode ();
859905    }
860-     if  (withOptimisticLock  != null ) {
861-       result  = 31  * result  + withOptimisticLock .hashCode ();
862-     }
863906    if  (withExcludeTxnFromChangeStreams  != null ) {
864907      result  = 31  * result  + withExcludeTxnFromChangeStreams .hashCode ();
865908    }
@@ -887,6 +930,9 @@ public int hashCode() {
887930    if  (reqId  != null ) {
888931      result  = 31  * result  + reqId .hashCode ();
889932    }
933+     if  (readLockMode  != null ) {
934+       result  = 31  * result  + readLockMode .hashCode ();
935+     }
890936    return  result ;
891937  }
892938
0 commit comments