Skip to content

Commit caf7513

Browse files
bchen-godaddygsmet
authored andcommitted
HHH-12866 Apply "for update of" with aliases in Oracle dialect
Inspired by the similar fix made in HHH-5654
1 parent 725da27 commit caf7513

File tree

6 files changed

+209
-14
lines changed

6 files changed

+209
-14
lines changed

hibernate-core/src/main/java/org/hibernate/dialect/Oracle8iDialect.java

Lines changed: 96 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@
1010
import java.sql.ResultSet;
1111
import java.sql.SQLException;
1212
import java.sql.Types;
13-
import java.util.List;
13+
import java.util.Iterator;
1414
import java.util.Locale;
15+
import java.util.Map;
1516
import java.util.regex.Matcher;
1617
import java.util.regex.Pattern;
1718

1819
import org.hibernate.JDBCException;
20+
import org.hibernate.LockMode;
21+
import org.hibernate.LockOptions;
1922
import org.hibernate.QueryTimeoutException;
2023
import org.hibernate.cfg.Environment;
2124
import org.hibernate.dialect.function.NoArgSQLFunction;
@@ -39,6 +42,7 @@
3942
import org.hibernate.hql.spi.id.global.GlobalTemporaryTableBulkIdStrategy;
4043
import org.hibernate.hql.spi.id.local.AfterUseAction;
4144
import org.hibernate.internal.util.JdbcExceptionHelper;
45+
import org.hibernate.internal.util.StringHelper;
4246
import org.hibernate.procedure.internal.StandardCallableStatementSupport;
4347
import org.hibernate.procedure.spi.CallableStatementSupport;
4448
import 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,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.dialect;
8+
9+
import static org.junit.Assert.assertEquals;
10+
11+
import org.hibernate.LockMode;
12+
import org.hibernate.LockOptions;
13+
import org.junit.Test;
14+
15+
public class Oracle10gDialectTestCase {
16+
17+
@Test
18+
public void testGetForUpdateStringWithAliasesAndLockOptions() {
19+
Oracle10gDialect dialect = new Oracle10gDialect();
20+
LockOptions lockOptions = new LockOptions();
21+
lockOptions.setAliasSpecificLockMode( "tableAlias1", LockMode.PESSIMISTIC_WRITE );
22+
23+
String forUpdateClause = dialect.getForUpdateString( "tableAlias1", lockOptions );
24+
assertEquals( " for update of tableAlias1", forUpdateClause );
25+
26+
lockOptions.setAliasSpecificLockMode( "tableAlias2", LockMode.PESSIMISTIC_WRITE );
27+
forUpdateClause = dialect.getForUpdateString( "tableAlias1,tableAlias2", lockOptions );
28+
assertEquals( " for update of tableAlias1,tableAlias2", forUpdateClause );
29+
}
30+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.dialect;
8+
9+
import static org.junit.Assert.assertEquals;
10+
11+
import org.hibernate.LockMode;
12+
import org.hibernate.LockOptions;
13+
import org.junit.Test;
14+
15+
public class Oracle12cDialectTestCase {
16+
17+
@Test
18+
public void testGetForUpdateStringWithAliasesAndLockOptions() {
19+
Oracle12cDialect dialect = new Oracle12cDialect();
20+
LockOptions lockOptions = new LockOptions();
21+
22+
lockOptions.setAliasSpecificLockMode( "tableAlias1", LockMode.PESSIMISTIC_WRITE );
23+
String forUpdateClause = dialect.getForUpdateString( "tableAlias1", lockOptions );
24+
assertEquals( " for update of tableAlias1", forUpdateClause );
25+
26+
lockOptions.setAliasSpecificLockMode( "tableAlias2", LockMode.PESSIMISTIC_WRITE );
27+
forUpdateClause = dialect.getForUpdateString( "tableAlias1,tableAlias2", lockOptions );
28+
assertEquals( " for update of tableAlias1,tableAlias2", forUpdateClause );
29+
}
30+
}

hibernate-core/src/test/java/org/hibernate/dialect/Oracle8iDialectTestCase.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
*/
77
package org.hibernate.dialect;
88

9+
import org.hibernate.LockMode;
10+
import org.hibernate.LockOptions;
911
import org.hibernate.hql.spi.id.AbstractMultiTableBulkIdStrategyImpl;
1012

1113
import org.junit.Test;
@@ -37,4 +39,19 @@ public void testTemporaryTableNameTruncation() throws Exception {
3739
temporaryTableName
3840
);
3941
}
42+
43+
@Test
44+
public void testGetForUpdateStringWithAliasesAndLockOptions() {
45+
Oracle8iDialect dialect = new Oracle8iDialect();
46+
LockOptions lockOptions = new LockOptions();
47+
lockOptions.setAliasSpecificLockMode( "tableAlias1", LockMode.PESSIMISTIC_WRITE );
48+
49+
String forUpdateClause = dialect.getForUpdateString( "tableAlias1", lockOptions );
50+
assertEquals( " for update of tableAlias1", forUpdateClause );
51+
52+
lockOptions.setAliasSpecificLockMode( "tableAlias2", LockMode.PESSIMISTIC_WRITE );
53+
forUpdateClause = dialect.getForUpdateString( "tableAlias1,tableAlias2", lockOptions );
54+
assertEquals( " for update of tableAlias1,tableAlias2", forUpdateClause );
55+
}
56+
4057
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.dialect;
8+
9+
import static org.junit.Assert.assertEquals;
10+
11+
import org.hibernate.LockMode;
12+
import org.hibernate.LockOptions;
13+
import org.junit.Test;
14+
15+
public class Oracle9iDialectTestCase {
16+
17+
@Test
18+
public void testGetForUpdateStringWithAliasesAndLockOptions() {
19+
Oracle9iDialect dialect = new Oracle9iDialect();
20+
LockOptions lockOptions = new LockOptions();
21+
lockOptions.setAliasSpecificLockMode( "tableAlias1", LockMode.PESSIMISTIC_WRITE );
22+
23+
String forUpdateClause = dialect.getForUpdateString( "tableAlias1", lockOptions );
24+
assertEquals( " for update of tableAlias1", forUpdateClause );
25+
26+
lockOptions.setAliasSpecificLockMode( "tableAlias2", LockMode.PESSIMISTIC_WRITE );
27+
forUpdateClause = dialect.getForUpdateString( "tableAlias1,tableAlias2", lockOptions );
28+
assertEquals( " for update of tableAlias1,tableAlias2", forUpdateClause );
29+
}
30+
}

hibernate-core/src/test/java/org/hibernate/test/dialect/unit/locktimeout/OracleLockTimeoutTest.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public void testLockTimeoutNoAliasSkipLocked() {
6767
public void testLockTimeoutAliasNoTimeout() {
6868
String alias = "a";
6969
assertEquals(
70-
" for update",
70+
" for update of a",
7171
dialect.getForUpdateString(
7272
alias,
7373
new LockOptions( LockMode.PESSIMISTIC_READ ).setAliasSpecificLockMode(
@@ -77,7 +77,7 @@ public void testLockTimeoutAliasNoTimeout() {
7777
)
7878
);
7979
assertEquals(
80-
" for update",
80+
" for update of a",
8181
dialect.getForUpdateString(
8282
alias,
8383
new LockOptions( LockMode.PESSIMISTIC_WRITE ).setAliasSpecificLockMode(
@@ -92,7 +92,7 @@ public void testLockTimeoutAliasNoTimeout() {
9292
public void testLockTimeoutAliasNoWait() {
9393
String alias = "a";
9494
assertEquals(
95-
" for update nowait",
95+
" for update of a nowait",
9696
dialect.getForUpdateString(
9797
alias,
9898
new LockOptions( LockMode.PESSIMISTIC_READ ).setAliasSpecificLockMode(
@@ -103,7 +103,7 @@ public void testLockTimeoutAliasNoWait() {
103103
)
104104
);
105105
assertEquals(
106-
" for update nowait",
106+
" for update of a nowait",
107107
dialect.getForUpdateString(
108108
alias,
109109
new LockOptions( LockMode.PESSIMISTIC_WRITE ).setAliasSpecificLockMode(
@@ -119,7 +119,7 @@ public void testLockTimeoutAliasNoWait() {
119119
public void testLockTimeoutAliasSkipLocked() {
120120
String alias = "a";
121121
assertEquals(
122-
" for update skip locked",
122+
" for update of a skip locked",
123123
dialect.getForUpdateString(
124124
alias,
125125
new LockOptions( LockMode.PESSIMISTIC_READ ).setAliasSpecificLockMode(
@@ -130,7 +130,7 @@ public void testLockTimeoutAliasSkipLocked() {
130130
)
131131
);
132132
assertEquals(
133-
" for update skip locked",
133+
" for update of a skip locked",
134134
dialect.getForUpdateString(
135135
alias,
136136
new LockOptions( LockMode.PESSIMISTIC_WRITE ).setAliasSpecificLockMode(

0 commit comments

Comments
 (0)