1010import java .sql .ResultSet ;
1111import java .sql .SQLException ;
1212import java .sql .Types ;
13- import java .util .List ;
13+ import java .util .Iterator ;
1414import java .util .Locale ;
15+ import java .util .Map ;
1516import java .util .regex .Matcher ;
1617import java .util .regex .Pattern ;
1718
1819import org .hibernate .JDBCException ;
20+ import org .hibernate .LockMode ;
21+ import org .hibernate .LockOptions ;
1922import org .hibernate .QueryTimeoutException ;
2023import org .hibernate .cfg .Environment ;
2124import org .hibernate .dialect .function .NoArgSQLFunction ;
3942import org .hibernate .hql .spi .id .global .GlobalTemporaryTableBulkIdStrategy ;
4043import org .hibernate .hql .spi .id .local .AfterUseAction ;
4144import org .hibernate .internal .util .JdbcExceptionHelper ;
45+ import org .hibernate .internal .util .StringHelper ;
4246import org .hibernate .procedure .internal .StandardCallableStatementSupport ;
4347import org .hibernate .procedure .spi .CallableStatementSupport ;
4448import org .hibernate .sql .CaseFragment ;
@@ -464,12 +468,96 @@ public boolean supportsLimit() {
464468
465469 @ Override
466470 public String getForUpdateString (String aliases ) {
467- return getForUpdateString () + " of " + aliases ;
471+ StringBuilder sb = new StringBuilder ();
472+ sb .append ( getForUpdateString () );
473+ if ( StringHelper .isNotEmpty ( aliases ) ) {
474+ sb .append ( " of " ).append ( aliases );
475+ }
476+ return sb .toString ();
468477 }
469478
470479 @ Override
471480 public String getForUpdateNowaitString (String aliases ) {
472- return getForUpdateString () + " of " + aliases + " nowait" ;
481+ return getForUpdateString ( aliases ) + " nowait" ;
482+ }
483+
484+ /*
485+ * Overwrite because the parent's implementation does not support the `for update of ...` syntax.
486+ *
487+ * Since Oracle 8i (or even prior versions) the syntax of "for update of [table.column]" is already supported.
488+ * Refer to https://docs.oracle.com/cd/A87860_01/doc/server.817/a85397/state21b.htm#2065648
489+ */
490+ @ Override
491+ public String getForUpdateString (String aliases , LockOptions lockOptions ) {
492+ LockMode lockMode = lockOptions .getLockMode ();
493+ final Iterator <Map .Entry <String , LockMode >> itr = lockOptions .getAliasLockIterator ();
494+ while ( itr .hasNext () ) {
495+ // seek the highest lock mode
496+ final Map .Entry <String , LockMode > entry = itr .next ();
497+ final LockMode lm = entry .getValue ();
498+ if ( lm .greaterThan ( lockMode ) ) {
499+ lockMode = lm ;
500+ }
501+ }
502+ lockOptions .setLockMode ( lockMode );
503+ return getForUpdateString ( lockMode , lockOptions .getTimeOut (), aliases );
504+ }
505+
506+ private String getForUpdateString (LockMode lockMode , int timeout , String aliases ) {
507+ switch ( lockMode ) {
508+ case UPGRADE :
509+ return getForUpdateString ( aliases );
510+ case PESSIMISTIC_READ :
511+ return getReadLockString ( aliases , timeout );
512+ case PESSIMISTIC_WRITE :
513+ return getWriteLockString ( aliases , timeout );
514+ case UPGRADE_NOWAIT :
515+ case FORCE :
516+ case PESSIMISTIC_FORCE_INCREMENT :
517+ return getForUpdateNowaitString ( aliases );
518+ case UPGRADE_SKIPLOCKED :
519+ return getForUpdateSkipLockedString ( aliases );
520+ default :
521+ return "" ;
522+ }
523+ }
524+
525+ @ Override
526+ public String getReadLockString (String aliases , int timeout ) {
527+ return forUpdateFragment ( aliases , timeout );
528+ }
529+
530+ @ Override
531+ public String getWriteLockString (String aliases , int timeout ) {
532+ if ( timeout == LockOptions .SKIP_LOCKED ) {
533+ return getForUpdateSkipLockedString ( aliases );
534+ }
535+ else {
536+ return forUpdateFragment ( aliases , timeout );
537+ }
538+ }
539+
540+ private String forUpdateFragment (String aliases , int timeout ) {
541+ StringBuilder forUpdateFragment = new StringBuilder ( getForUpdateString () );
542+
543+ // refer to https://docs.oracle.com/database/121/SQLRF/statements_10002.htm#i2126016
544+ if ( StringHelper .isNotEmpty ( aliases ) ) {
545+ forUpdateFragment .append ( " of " ).append ( aliases );
546+ }
547+
548+ if ( timeout == LockOptions .NO_WAIT ) {
549+ forUpdateFragment .append ( " nowait" );
550+ }
551+ else if ( timeout == LockOptions .SKIP_LOCKED ) {
552+ forUpdateFragment .append ( " skip locked" );
553+ }
554+ else if ( timeout > 0 ) {
555+ // convert from milliseconds to seconds
556+ final float seconds = timeout / 1000.0f ;
557+ forUpdateFragment .append ( " wait " ).append ( Math .round ( seconds ) );
558+ }
559+
560+ return forUpdateFragment .toString ();
473561 }
474562
475563 @ Override
@@ -653,7 +741,7 @@ public boolean isCurrentTimestampSelectStringCallable() {
653741 public boolean supportsEmptyInList () {
654742 return false ;
655743 }
656-
744+
657745 @ Override
658746 public boolean supportsExistsInSelect () {
659747 return false ;
@@ -663,7 +751,7 @@ public boolean supportsExistsInSelect() {
663751 public int getInExpressionCountLimit () {
664752 return PARAM_LIST_SIZE_LIMIT ;
665753 }
666-
754+
667755 @ Override
668756 public boolean forceLobAsLastValue () {
669757 return true ;
@@ -697,12 +785,12 @@ public boolean useFollowOnLocking(QueryParameters parameters) {
697785 return true ;
698786 }
699787 }
700-
788+
701789 @ Override
702790 public String getNotExpression ( String expression ) {
703791 return "not (" + expression + ")" ;
704792 }
705-
793+
706794 @ Override
707795 public String getQueryHintString (String sql , String hints ) {
708796 String statementType = statementType (sql );
@@ -724,7 +812,7 @@ public String getQueryHintString(String sql, String hints) {
724812
725813 return sql ;
726814 }
727-
815+
728816 @ Override
729817 public int getMaxAliasLength () {
730818 // Oracle's max identifier length is 30, but Hibernate needs to add "uniqueing info" so we account for that,
0 commit comments