diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java index 17b4f3e45093..9ac4daeb3820 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java @@ -4,27 +4,29 @@ */ package org.hibernate.community.dialect; -import java.sql.DatabaseMetaData; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Types; -import java.time.temporal.ChronoField; -import java.time.temporal.TemporalAccessor; -import java.util.Calendar; -import java.util.Date; -import java.util.Map; -import java.util.TimeZone; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - +import jakarta.persistence.GenerationType; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Timeout; import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.PessimisticLockException; import org.hibernate.QueryTimeoutException; +import org.hibernate.Timeouts; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; -import org.hibernate.dialect.*; +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.DmlTargetColumnQualifierSupport; +import org.hibernate.dialect.FunctionalDependencyAnalysisSupport; +import org.hibernate.dialect.FunctionalDependencyAnalysisSupportImpl; +import org.hibernate.dialect.NationalizationSupport; +import org.hibernate.dialect.NullOrdering; +import org.hibernate.dialect.PostgreSQLDriverKind; +import org.hibernate.dialect.RowLockStrategy; +import org.hibernate.dialect.SimpleDatabaseVersion; +import org.hibernate.dialect.SpannerDialect; +import org.hibernate.dialect.TimeZoneSupport; import org.hibernate.dialect.aggregate.AggregateSupport; import org.hibernate.dialect.aggregate.CockroachDBAggregateSupport; import org.hibernate.dialect.function.CommonFunctionFactory; @@ -58,9 +60,8 @@ import org.hibernate.internal.util.JdbcExceptionHelper; import org.hibernate.internal.util.StringHelper; import org.hibernate.query.SemanticException; -import org.hibernate.query.sqm.IntervalType; -import org.hibernate.dialect.NullOrdering; import org.hibernate.query.common.TemporalUnit; +import org.hibernate.query.sqm.IntervalType; import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers; import org.hibernate.service.ServiceRegistry; import org.hibernate.sql.ast.SqlAstTranslator; @@ -83,9 +84,18 @@ import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; import org.hibernate.type.spi.TypeConfiguration; - -import jakarta.persistence.GenerationType; -import jakarta.persistence.TemporalType; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Date; +import java.util.Map; +import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; import static org.hibernate.query.common.TemporalUnit.DAY; @@ -1009,14 +1019,38 @@ public String getForUpdateString(String aliases, LockOptions lockOptions) { }; } + private String withTimeout(String lockString, Timeout timeout) { + return withTimeout( lockString, timeout.milliseconds() ); + } + private String withTimeout(String lockString, int timeout) { return switch ( timeout ) { - case LockOptions.NO_WAIT -> supportsNoWait() ? lockString + " nowait" : lockString; - case LockOptions.SKIP_LOCKED -> supportsSkipLocked() ? lockString + " skip locked" : lockString; + case Timeouts.NO_WAIT_MILLI -> supportsNoWait() ? lockString + " nowait" : lockString; + case Timeouts.SKIP_LOCKED_MILLI -> supportsSkipLocked() ? lockString + " skip locked" : lockString; default -> lockString; }; } + @Override + public String getWriteLockString(Timeout timeout) { + return withTimeout( getForUpdateString(), timeout ); + } + + @Override + public String getWriteLockString(String aliases, Timeout timeout) { + return withTimeout( getForUpdateString( aliases ), timeout ); + } + + @Override + public String getReadLockString(Timeout timeout) { + return withTimeout(" for share", timeout ); + } + + @Override + public String getReadLockString(String aliases, Timeout timeout) { + return withTimeout(" for share of " + aliases, timeout ); + } + @Override public String getWriteLockString(int timeout) { return withTimeout( getForUpdateString(), timeout ); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java index b57f4cd7d42f..6d4bada74490 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java @@ -4,31 +4,14 @@ */ package org.hibernate.community.dialect; -import java.sql.CallableStatement; -import java.sql.DatabaseMetaData; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Types; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.OffsetDateTime; -import java.time.OffsetTime; -import java.time.ZonedDateTime; -import java.time.temporal.TemporalAccessor; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import java.util.TimeZone; - -import org.hibernate.LockOptions; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Timeout; +import org.hibernate.Timeouts; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.community.dialect.sequence.LegacyDB2SequenceSupport; import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.DB2GetObjectExtractor; -import org.hibernate.dialect.type.DB2StructJdbcType; import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.DmlTargetColumnQualifierSupport; @@ -49,6 +32,7 @@ import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.sequence.DB2SequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; +import org.hibernate.dialect.type.DB2StructJdbcType; import org.hibernate.dialect.unique.AlterTableUniqueIndexDelegate; import org.hibernate.dialect.unique.SkipNullableUniqueDelegate; import org.hibernate.dialect.unique.UniqueDelegate; @@ -70,9 +54,9 @@ import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.procedure.internal.DB2CallableStatementSupport; import org.hibernate.procedure.spi.CallableStatementSupport; +import org.hibernate.query.common.TemporalUnit; import org.hibernate.query.sqm.CastType; import org.hibernate.query.sqm.IntervalType; -import org.hibernate.query.common.TemporalUnit; import org.hibernate.query.sqm.mutation.internal.cte.CteInsertStrategy; import org.hibernate.query.sqm.mutation.internal.cte.CteMutationStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; @@ -118,7 +102,23 @@ import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; import org.hibernate.type.spi.TypeConfiguration; -import jakarta.persistence.TemporalType; +import java.sql.CallableStatement; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.ZonedDateTime; +import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; import static org.hibernate.type.SqlTypes.BINARY; @@ -781,16 +781,30 @@ public String getForUpdateSkipLockedString(String aliases) { return getForUpdateSkipLockedString(); } + @Override + public String getWriteLockString(Timeout timeout) { + return timeout.milliseconds() == Timeouts.SKIP_LOCKED_MILLI && supportsSkipLocked() + ? FOR_UPDATE_SKIP_LOCKED_SQL + : FOR_UPDATE_SQL; + } + + @Override + public String getReadLockString(Timeout timeout) { + return timeout.milliseconds() == Timeouts.SKIP_LOCKED_MILLI && supportsSkipLocked() + ? FOR_SHARE_SKIP_LOCKED_SQL + : FOR_SHARE_SQL; + } + @Override public String getWriteLockString(int timeout) { - return timeout == LockOptions.SKIP_LOCKED && supportsSkipLocked() + return timeout == Timeouts.SKIP_LOCKED_MILLI && supportsSkipLocked() ? FOR_UPDATE_SKIP_LOCKED_SQL : FOR_UPDATE_SQL; } @Override public String getReadLockString(int timeout) { - return timeout == LockOptions.SKIP_LOCKED && supportsSkipLocked() + return timeout == Timeouts.SKIP_LOCKED_MILLI && supportsSkipLocked() ? FOR_SHARE_SKIP_LOCKED_SQL : FOR_SHARE_SQL; } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyDialect.java index d32e7034c64e..d0fc7a0cc866 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyDialect.java @@ -9,6 +9,7 @@ import java.sql.Types; import java.util.Locale; +import jakarta.persistence.Timeout; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.dialect.DB2Dialect; @@ -577,6 +578,16 @@ public String getForUpdateString() { return " for update with rs"; } + @Override + public String getWriteLockString(Timeout timeout) { + return " for update with rs"; + } + + @Override + public String getReadLockString(Timeout timeout) { + return " for read only with rs"; + } + @Override public String getWriteLockString(int timeout) { return " for update with rs"; diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyLegacyDialect.java index dfdf3dadc957..b6a7e68de364 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyLegacyDialect.java @@ -8,6 +8,7 @@ import java.sql.SQLException; import java.sql.Types; +import jakarta.persistence.Timeout; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.dialect.DB2Dialect; @@ -593,6 +594,16 @@ public String getForUpdateString() { return " for update with rs"; } + @Override + public String getWriteLockString(Timeout timeout) { + return " for update with rs"; + } + + @Override + public String getReadLockString(Timeout timeout) { + return " for read only with rs"; + } + @Override public String getWriteLockString(int timeout) { return " for update with rs"; diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HANALegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HANALegacyDialect.java index 63ae08c7068c..a4742e07471b 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HANALegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HANALegacyDialect.java @@ -4,41 +4,12 @@ */ package org.hibernate.community.dialect; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.FilterInputStream; -import java.io.FilterReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Reader; -import java.io.StringReader; -import java.io.Writer; -import java.nio.charset.StandardCharsets; -import java.sql.Blob; -import java.sql.CallableStatement; -import java.sql.Clob; -import java.sql.DatabaseMetaData; -import java.sql.NClob; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; -import java.sql.Types; -import java.time.temporal.TemporalAccessor; -import java.util.Arrays; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - +import jakarta.persistence.TemporalType; +import jakarta.persistence.Timeout; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.ScrollMode; +import org.hibernate.Timeouts; import org.hibernate.boot.Metadata; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; @@ -48,7 +19,6 @@ import org.hibernate.dialect.Dialect; import org.hibernate.dialect.DmlTargetColumnQualifierSupport; import org.hibernate.dialect.HANAServerConfiguration; -import org.hibernate.dialect.sql.ast.HANASqlAstTranslator; import org.hibernate.dialect.NullOrdering; import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.RowLockStrategy; @@ -62,6 +32,7 @@ import org.hibernate.dialect.pagination.LimitOffsetLimitHandler; import org.hibernate.dialect.sequence.HANASequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; +import org.hibernate.dialect.sql.ast.HANASqlAstTranslator; import org.hibernate.dialect.temptable.TemporaryTable; import org.hibernate.dialect.temptable.TemporaryTableKind; import org.hibernate.engine.config.spi.ConfigurationService; @@ -131,7 +102,37 @@ import org.hibernate.type.internal.BasicTypeImpl; import org.hibernate.type.spi.TypeConfiguration; -import jakarta.persistence.TemporalType; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FilterInputStream; +import java.io.FilterReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; +import java.sql.DatabaseMetaData; +import java.sql.NClob; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.sql.Types; +import java.time.temporal.TemporalAccessor; +import java.util.Arrays; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static org.hibernate.dialect.HANAServerConfiguration.MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE; import static org.hibernate.query.sqm.produce.function.FunctionParameterType.ANY; @@ -992,6 +993,42 @@ public String getForUpdateNowaitString(String aliases) { return getForUpdateString( aliases ) + " nowait"; } + @Override + public String getReadLockString(Timeout timeout) { + return getWriteLockString( timeout ); + } + + @Override + public String getReadLockString(String aliases, Timeout timeout) { + return getWriteLockString( aliases, timeout ); + } + + @Override + public String getWriteLockString(Timeout timeout) { + if ( Timeouts.isRealTimeout( timeout ) ) { + return getForUpdateString() + " wait " + getTimeoutInSeconds( timeout.milliseconds() ); + } + else if ( timeout.milliseconds() == Timeouts.NO_WAIT_MILLI ) { + return getForUpdateNowaitString(); + } + else { + return getForUpdateString(); + } + } + + @Override + public String getWriteLockString(String aliases, Timeout timeout) { + if ( Timeouts.isRealTimeout( timeout ) ) { + return getForUpdateString( aliases ) + " wait " + getTimeoutInSeconds( timeout.milliseconds() ); + } + else if ( timeout.milliseconds() == Timeouts.NO_WAIT_MILLI ) { + return getForUpdateNowaitString( aliases ); + } + else { + return getForUpdateString( aliases ); + } + } + @Override public String getReadLockString(int timeout) { return getWriteLockString( timeout ); @@ -1004,10 +1041,10 @@ public String getReadLockString(String aliases, int timeout) { @Override public String getWriteLockString(int timeout) { - if ( timeout > 0 ) { - return getForUpdateString() + " wait " + getTimeoutInSeconds( timeout ); + if ( Timeouts.isRealTimeout( timeout ) ) { + return getForUpdateString() + " wait " + Timeouts.getTimeoutInSeconds( timeout ); } - else if ( timeout == 0 ) { + else if ( timeout == Timeouts.NO_WAIT_MILLI ) { return getForUpdateNowaitString(); } else { diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java index b713c3b63c36..17f4eff07d66 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java @@ -4,18 +4,26 @@ */ package org.hibernate.community.dialect; -import java.sql.CallableStatement; -import java.sql.DatabaseMetaData; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Types; - -import org.hibernate.LockOptions; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Timeout; import org.hibernate.PessimisticLockException; +import org.hibernate.Timeouts; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.cfg.Environment; -import org.hibernate.dialect.*; +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.DmlTargetColumnQualifierSupport; +import org.hibernate.dialect.FunctionalDependencyAnalysisSupport; +import org.hibernate.dialect.FunctionalDependencyAnalysisSupportImpl; +import org.hibernate.dialect.InnoDBStorageEngine; +import org.hibernate.dialect.MyISAMStorageEngine; +import org.hibernate.dialect.MySQLServerConfiguration; +import org.hibernate.dialect.MySQLStorageEngine; +import org.hibernate.dialect.NullOrdering; +import org.hibernate.dialect.Replacer; +import org.hibernate.dialect.RowLockStrategy; +import org.hibernate.dialect.SelectItemReferenceStrategy; import org.hibernate.dialect.aggregate.AggregateSupport; import org.hibernate.dialect.aggregate.MySQLAggregateSupport; import org.hibernate.dialect.function.CommonFunctionFactory; @@ -46,15 +54,14 @@ import org.hibernate.mapping.CheckConstraint; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; +import org.hibernate.query.common.TemporalUnit; import org.hibernate.query.sqm.CastType; import org.hibernate.query.sqm.IntervalType; -import org.hibernate.dialect.NullOrdering; -import org.hibernate.query.common.TemporalUnit; import org.hibernate.query.sqm.function.SqmFunctionRegistry; -import org.hibernate.query.sqm.mutation.spi.AfterUseAction; -import org.hibernate.query.sqm.mutation.spi.BeforeUseAction; import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy; import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy; +import org.hibernate.query.sqm.mutation.spi.AfterUseAction; +import org.hibernate.query.sqm.mutation.spi.BeforeUseAction; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.produce.function.FunctionParameterType; @@ -79,7 +86,11 @@ import org.hibernate.type.descriptor.sql.internal.NativeOrdinalEnumDdlTypeImpl; import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; -import jakarta.persistence.TemporalType; +import java.sql.CallableStatement; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; import static org.hibernate.type.SqlTypes.BIGINT; @@ -1326,12 +1337,47 @@ public static Replacer datetimeFormat(String format) { .replace("S", "%f"); } + private String withTimeout(String lockString, Timeout timeout) { + return switch ( timeout.milliseconds() ) { + case Timeouts.NO_WAIT_MILLI -> supportsNoWait() ? lockString + " nowait" : lockString; + case Timeouts.SKIP_LOCKED_MILLI -> supportsSkipLocked() ? lockString + " skip locked" : lockString; + case Timeouts.WAIT_FOREVER_MILLI -> lockString; + default -> supportsWait() ? lockString + " wait " + Timeouts.getTimeoutInSeconds( timeout ) : lockString; + }; + } + + @Override + public String getWriteLockString(Timeout timeout) { + return withTimeout( getForUpdateString(), timeout ); + } + + @Override + public String getWriteLockString(String aliases, Timeout timeout) { + return withTimeout( getForUpdateString( aliases ), timeout ); + } + + @Override + public String getReadLockString(Timeout timeout) { + return withTimeout( supportsForShare() ? " for share" : " lock in share mode", timeout ); + } + + @Override + public String getReadLockString(String aliases, Timeout timeout) { + if ( supportsAliasLocks() && supportsForShare() ) { + return withTimeout( " for share of " + aliases, timeout ); + } + else { + // fall back to locking all aliases + return getReadLockString( timeout ); + } + } + private String withTimeout(String lockString, int timeout) { return switch ( timeout ) { - case LockOptions.NO_WAIT -> supportsNoWait() ? lockString + " nowait" : lockString; - case LockOptions.SKIP_LOCKED -> supportsSkipLocked() ? lockString + " skip locked" : lockString; - case LockOptions.WAIT_FOREVER -> lockString; - default -> supportsWait() ? lockString + " wait " + getTimeoutInSeconds( timeout ) : lockString; + case Timeouts.NO_WAIT_MILLI -> supportsNoWait() ? lockString + " nowait" : lockString; + case Timeouts.SKIP_LOCKED_MILLI -> supportsSkipLocked() ? lockString + " skip locked" : lockString; + case Timeouts.WAIT_FOREVER_MILLI -> lockString; + default -> supportsWait() ? lockString + " wait " + Timeouts.getTimeoutInSeconds( timeout ) : lockString; }; } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java index e8b22f66405b..1b545ae97ff6 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java @@ -16,8 +16,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.hibernate.LockOptions; +import jakarta.persistence.Timeout; import org.hibernate.QueryTimeoutException; +import org.hibernate.Timeouts; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.dialect.BooleanDecoder; @@ -1410,12 +1411,36 @@ public String getForUpdateSkipLockedString(String aliases) { return " for update of " + aliases + " skip locked"; } + private String withTimeout(String lockString, Timeout timeout) { + return withTimeout( lockString, timeout.milliseconds() ); + } + + @Override + public String getWriteLockString(Timeout timeout) { + return withTimeout( getForUpdateString(), timeout ); + } + + @Override + public String getWriteLockString(String aliases, Timeout timeout) { + return withTimeout( getForUpdateString(aliases), timeout ); + } + + @Override + public String getReadLockString(Timeout timeout) { + return getWriteLockString( timeout ); + } + + @Override + public String getReadLockString(String aliases, Timeout timeout) { + return getWriteLockString( aliases, timeout ); + } + private String withTimeout(String lockString, int timeout) { return switch ( timeout ) { - case LockOptions.NO_WAIT -> supportsNoWait() ? lockString + " nowait" : lockString; - case LockOptions.SKIP_LOCKED -> supportsSkipLocked() ? lockString + " skip locked" : lockString; - case LockOptions.WAIT_FOREVER -> lockString; - default -> supportsWait() ? lockString + " wait " + getTimeoutInSeconds( timeout ) : lockString; + case Timeouts.NO_WAIT_MILLI -> supportsNoWait() ? lockString + " nowait" : lockString; + case Timeouts.SKIP_LOCKED_MILLI -> supportsSkipLocked() ? lockString + " skip locked" : lockString; + case Timeouts.WAIT_FOREVER_MILLI -> lockString; + default -> supportsWait() ? lockString + " wait " + Timeouts.getTimeoutInSeconds( timeout ) : lockString; }; } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java index aced818d070c..4865fec5d4cc 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java @@ -17,12 +17,14 @@ import java.util.Map; import java.util.TimeZone; +import jakarta.persistence.Timeout; import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.Length; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.PessimisticLockException; import org.hibernate.QueryTimeoutException; +import org.hibernate.Timeouts; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.community.dialect.sequence.PostgreSQLLegacySequenceSupport; @@ -1317,10 +1319,38 @@ public void appendDateTimeLiteral( } } + private String withTimeout(String lockString, Timeout timeout) { + return switch (timeout.milliseconds()) { + case Timeouts.NO_WAIT_MILLI -> supportsNoWait() ? lockString + " nowait" : lockString; + case Timeouts.SKIP_LOCKED_MILLI -> supportsSkipLocked() ? lockString + " skip locked" : lockString; + default -> lockString; + }; + } + + @Override + public String getWriteLockString(Timeout timeout) { + return withTimeout( getForUpdateString(), timeout ); + } + + @Override + public String getWriteLockString(String aliases, Timeout timeout) { + return withTimeout( getForUpdateString( aliases ), timeout ); + } + + @Override + public String getReadLockString(Timeout timeout) { + return withTimeout(" for share", timeout ); + } + + @Override + public String getReadLockString(String aliases, Timeout timeout) { + return withTimeout(" for share of " + aliases, timeout ); + } + private String withTimeout(String lockString, int timeout) { return switch ( timeout ) { - case LockOptions.NO_WAIT -> supportsNoWait() ? lockString + " nowait" : lockString; - case LockOptions.SKIP_LOCKED -> supportsSkipLocked() ? lockString + " skip locked" : lockString; + case Timeouts.NO_WAIT_MILLI -> supportsNoWait() ? lockString + " nowait" : lockString; + case Timeouts.SKIP_LOCKED_MILLI -> supportsSkipLocked() ? lockString + " skip locked" : lockString; default -> lockString; }; } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SingleStoreDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SingleStoreDialect.java index c5cc9696177e..da138c1bda44 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SingleStoreDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SingleStoreDialect.java @@ -16,6 +16,7 @@ import java.util.Date; import java.util.TimeZone; +import jakarta.persistence.Timeout; import org.hibernate.Length; import org.hibernate.PessimisticLockException; import org.hibernate.boot.Metadata; @@ -1224,6 +1225,11 @@ public String getAddPrimaryKeyConstraintString(String constraintName) { throw new UnsupportedOperationException( "SingleStore does not support altering primary key." ); } + @Override + public String getWriteLockString(String aliases, Timeout timeout) { + return getForUpdateString( aliases ); + } + @Override public String getWriteLockString(String aliases, int timeout) { return getForUpdateString( aliases ); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TiDBDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TiDBDialect.java index 0d176dee4302..28363d72e7f8 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TiDBDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TiDBDialect.java @@ -4,7 +4,11 @@ */ package org.hibernate.community.dialect; -import org.hibernate.LockOptions; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Timeout; +import org.hibernate.Timeouts; +import org.hibernate.community.dialect.sequence.SequenceInformationExtractorTiDBDatabaseImpl; +import org.hibernate.community.dialect.sequence.TiDBSequenceSupport; import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.FunctionalDependencyAnalysisSupport; @@ -14,21 +18,17 @@ import org.hibernate.dialect.aggregate.AggregateSupport; import org.hibernate.dialect.aggregate.MySQLAggregateSupport; import org.hibernate.dialect.sequence.SequenceSupport; -import org.hibernate.community.dialect.sequence.TiDBSequenceSupport; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.query.sqm.IntervalType; import org.hibernate.query.common.TemporalUnit; +import org.hibernate.query.sqm.IntervalType; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; -import org.hibernate.community.dialect.sequence.SequenceInformationExtractorTiDBDatabaseImpl; import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; -import jakarta.persistence.TemporalType; - /** * A {@linkplain Dialect SQL dialect} for TiDB. * @@ -154,9 +154,38 @@ public boolean supportsRowValueConstructorSyntaxInInList() { return getVersion().isSameOrAfter( 5, 7 ); } + @Override + public String getReadLockString(Timeout timeout) { + if ( timeout.milliseconds() == Timeouts.NO_WAIT_MILLI ) { + return getForUpdateNowaitString(); + } + return super.getReadLockString( timeout ); + } + + @Override + public String getReadLockString(String aliases, Timeout timeout) { + if ( timeout.milliseconds() == Timeouts.NO_WAIT_MILLI ) { + return getForUpdateNowaitString( aliases ); + } + return super.getReadLockString( aliases, timeout ); + } + + @Override + public String getWriteLockString(Timeout timeout) { + if ( timeout.milliseconds() == Timeouts.NO_WAIT_MILLI ) { + return getForUpdateNowaitString(); + } + + if ( Timeouts.isRealTimeout( timeout ) ) { + return getForUpdateString() + " wait " + Timeouts.getTimeoutInSeconds( timeout ); + } + + return getForUpdateString(); + } + @Override public String getReadLockString(int timeout) { - if ( timeout == LockOptions.NO_WAIT ) { + if ( timeout == Timeouts.NO_WAIT_MILLI ) { return getForUpdateNowaitString(); } return super.getReadLockString( timeout ); @@ -164,7 +193,7 @@ public String getReadLockString(int timeout) { @Override public String getReadLockString(String aliases, int timeout) { - if ( timeout == LockOptions.NO_WAIT ) { + if ( timeout == Timeouts.NO_WAIT_MILLI ) { return getForUpdateNowaitString( aliases ); } return super.getReadLockString( aliases, timeout ); @@ -172,12 +201,12 @@ public String getReadLockString(String aliases, int timeout) { @Override public String getWriteLockString(int timeout) { - if ( timeout == LockOptions.NO_WAIT ) { + if ( timeout == Timeouts.NO_WAIT_MILLI ) { return getForUpdateNowaitString(); } - if ( timeout > 0 ) { - return getForUpdateString() + " wait " + getTimeoutInSeconds( timeout ); + if ( Timeouts.isRealTimeout( timeout ) ) { + return getForUpdateString() + " wait " + Timeouts.getTimeoutInSeconds( timeout ); } return getForUpdateString(); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TimesTenDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TimesTenDialect.java index 554964d99529..47bf46cb80c0 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TimesTenDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TimesTenDialect.java @@ -6,8 +6,9 @@ import java.sql.Types; +import jakarta.persistence.Timeout; import org.hibernate.LockMode; -import org.hibernate.LockOptions; +import org.hibernate.Timeouts; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.community.dialect.pagination.TimesTenLimitHandler; import org.hibernate.community.dialect.sequence.SequenceInformationExtractorTimesTenDatabaseImpl; @@ -259,6 +260,35 @@ public String getForUpdateNowaitString() { return " for update nowait"; } + @Override + public String getWriteLockString(Timeout timeout) { + return withTimeout( getForUpdateString(), timeout ); + } + + @Override + public String getWriteLockString(String aliases, Timeout timeout) { + return withTimeout( getForUpdateString(aliases), timeout ); + } + + @Override + public String getReadLockString(Timeout timeout) { + return getWriteLockString( timeout ); + } + + @Override + public String getReadLockString(String aliases, Timeout timeout) { + return getWriteLockString( aliases, timeout ); + } + + + private String withTimeout(String lockString, Timeout timeout) { + return switch ( timeout.milliseconds() ) { + case Timeouts.NO_WAIT_MILLI -> supportsNoWait() ? lockString + " nowait" : lockString; + case Timeouts.SKIP_LOCKED_MILLI, Timeouts.WAIT_FOREVER_MILLI -> lockString; + default -> supportsWait() ? lockString + " wait " + Timeouts.getTimeoutInSeconds( timeout ) : lockString; + }; + } + @Override public String getWriteLockString(int timeout) { return withTimeout( getForUpdateString(), timeout ); @@ -282,8 +312,8 @@ public String getReadLockString(String aliases, int timeout) { private String withTimeout(String lockString, int timeout) { return switch ( timeout ) { - case LockOptions.NO_WAIT -> supportsNoWait() ? lockString + " nowait" : lockString; - case LockOptions.SKIP_LOCKED, LockOptions.WAIT_FOREVER -> lockString; + case Timeouts.NO_WAIT_MILLI -> supportsNoWait() ? lockString + " nowait" : lockString; + case Timeouts.SKIP_LOCKED_MILLI, Timeouts.WAIT_FOREVER_MILLI -> lockString; default -> supportsWait() ? lockString + " wait " + getTimeoutInSeconds( timeout ) : lockString; }; } diff --git a/hibernate-core/src/main/java/org/hibernate/IdentifierLoadAccess.java b/hibernate-core/src/main/java/org/hibernate/IdentifierLoadAccess.java index ab30b71eb522..20c66cf468bc 100644 --- a/hibernate-core/src/main/java/org/hibernate/IdentifierLoadAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/IdentifierLoadAccess.java @@ -8,6 +8,8 @@ import jakarta.persistence.EntityGraph; +import jakarta.persistence.PessimisticLockScope; +import jakarta.persistence.Timeout; import org.hibernate.graph.GraphSemantic; /** @@ -44,6 +46,38 @@ * @see Session#byId(Class) */ public interface IdentifierLoadAccess { + + /** + * Specify the {@linkplain LockMode lock mode} to use when + * querying the database. + * + * @param lockMode The lock mode to apply + * @return {@code this}, for method chaining + */ + default IdentifierLoadAccess with(LockMode lockMode) { + return with( lockMode, PessimisticLockScope.NORMAL ); + } + + /** + * Specify the {@linkplain LockMode lock mode} to use when + * querying the database. + * + * @param lockMode The lock mode to apply + * + * @return {@code this}, for method chaining + */ + IdentifierLoadAccess with(LockMode lockMode, PessimisticLockScope lockScope); + + /** + * Specify the {@linkplain Timeout timeout} to use when + * querying the database. + * + * @param timeout The timeout to apply to the database operation + * + * @return {@code this}, for method chaining + */ + IdentifierLoadAccess with(Timeout timeout); + /** * Specify the {@linkplain LockOptions lock options} to use when * querying the database. @@ -51,7 +85,12 @@ public interface IdentifierLoadAccess { * @param lockOptions The lock options to use * * @return {@code this}, for method chaining + * + * @deprecated Use one of {@linkplain #with(LockMode)}, + * {@linkplain #with(LockMode, PessimisticLockScope)} + * and/or {@linkplain #with(Timeout)} instead. */ + @Deprecated(since = "7.0", forRemoval = true) IdentifierLoadAccess with(LockOptions lockOptions); /** diff --git a/hibernate-core/src/main/java/org/hibernate/LockOptions.java b/hibernate-core/src/main/java/org/hibernate/LockOptions.java index 1869932e6c9b..fcc02a47b186 100644 --- a/hibernate-core/src/main/java/org/hibernate/LockOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/LockOptions.java @@ -4,6 +4,10 @@ */ package org.hibernate; +import jakarta.persistence.PessimisticLockScope; +import jakarta.persistence.Timeout; +import org.hibernate.query.Query; + import java.io.Serializable; import java.util.HashMap; import java.util.Iterator; @@ -12,11 +16,6 @@ import java.util.Objects; import java.util.Set; -import jakarta.persistence.FindOption; -import jakarta.persistence.PessimisticLockScope; -import jakarta.persistence.RefreshOption; -import org.hibernate.query.Query; - import static jakarta.persistence.PessimisticLockScope.NORMAL; import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableSet; @@ -51,7 +50,7 @@ * * @author Scott Marlow */ -public class LockOptions implements FindOption, RefreshOption, Serializable { +public class LockOptions implements Serializable { /** * Represents {@link LockMode#NONE}, to which timeout and scope are * not applicable. @@ -109,26 +108,22 @@ public class LockOptions implements FindOption, RefreshOption, Serializable { public static final LockOptions UPGRADE = PESSIMISTIC_WRITE; /** - * Indicates that the database should not wait at all to acquire - * a pessimistic lock which is not immediately available. This - * has the same effect as {@link LockMode#UPGRADE_NOWAIT}. - * + * @see Timeouts#NO_WAIT_MILLI + * @see Timeouts#NO_WAIT * @see #getTimeOut */ - public static final int NO_WAIT = 0; + public static final int NO_WAIT = Timeouts.NO_WAIT_MILLI; /** - * Indicates that there is no timeout for the lock acquisition, - * that is, that the database should in principle wait forever - * to obtain the lock. - * + * @see Timeouts#WAIT_FOREVER_MILLI + * @see Timeouts#WAIT_FOREVER * @see #getTimeOut */ - public static final int WAIT_FOREVER = -1; + public static final int WAIT_FOREVER = Timeouts.WAIT_FOREVER_MILLI; /** - * Indicates that rows which are already locked should be skipped. - * + * @see Timeouts#SKIP_LOCKED_MILLI + * @see Timeouts#SKIP_LOCKED * @see #getTimeOut() * @deprecated use {@link LockMode#UPGRADE_SKIPLOCKED} */ @@ -137,7 +132,7 @@ public class LockOptions implements FindOption, RefreshOption, Serializable { private final boolean immutable; private LockMode lockMode; - private int timeout; + private Timeout timeout; private PessimisticLockScope pessimisticLockScope; private Boolean followOnLocking; private Map aliasSpecificLockModes; @@ -149,7 +144,7 @@ public class LockOptions implements FindOption, RefreshOption, Serializable { public LockOptions() { immutable = false; lockMode = LockMode.NONE; - timeout = WAIT_FOREVER; + timeout = Timeouts.WAIT_FOREVER; pessimisticLockScope = NORMAL; } @@ -162,7 +157,7 @@ public LockOptions() { public LockOptions(LockMode lockMode) { immutable = false; this.lockMode = lockMode; - timeout = WAIT_FOREVER; + timeout = Timeouts.WAIT_FOREVER; pessimisticLockScope = NORMAL; } @@ -171,15 +166,29 @@ public LockOptions(LockMode lockMode) { * and timeout. * * @param lockMode The initial lock mode - * @param timeout The initial timeout + * @param timeout The initial timeout, in milliseconds */ - public LockOptions(LockMode lockMode, int timeout) { + public LockOptions(LockMode lockMode, Timeout timeout) { immutable = false; this.lockMode = lockMode; this.timeout = timeout; pessimisticLockScope = NORMAL; } + /** + * Construct an instance with the given {@linkplain LockMode mode} + * and timeout. + * + * @param lockMode The initial lock mode + * @param timeout The initial timeout, in milliseconds + * + * @deprecated Use {@linkplain #LockOptions(LockMode, Timeout)} instead + */ + @Deprecated(since = "7.0") + public LockOptions(LockMode lockMode, int timeout) { + this( lockMode, Timeouts.interpretMilliSeconds( timeout ) ); + } + /** * Construct an instance with the given {@linkplain LockMode mode}, * timeout, and {@link PessimisticLockScope scope}. @@ -188,20 +197,35 @@ public LockOptions(LockMode lockMode, int timeout) { * @param timeout The initial timeout * @param scope The initial lock scope */ - public LockOptions(LockMode lockMode, int timeout, PessimisticLockScope scope) { + public LockOptions(LockMode lockMode, Timeout timeout, PessimisticLockScope scope) { immutable = false; this.lockMode = lockMode; this.timeout = timeout; this.pessimisticLockScope = scope; } + /** + * Construct an instance with the given {@linkplain LockMode mode}, + * timeout, and {@link PessimisticLockScope scope}. + * + * @param lockMode The initial lock mode + * @param timeout The initial timeout, in milliseconds + * @param scope The initial lock scope + * + * @deprecated Use {@linkplain #LockOptions(LockMode, Timeout, PessimisticLockScope)} instead + */ + @Deprecated(since = "7.0") + public LockOptions(LockMode lockMode, int timeout, PessimisticLockScope scope) { + this( lockMode, Timeouts.interpretMilliSeconds( timeout ), scope ); + } + /** * Internal operation used to create immutable global instances. */ private LockOptions(boolean immutable, LockMode lockMode) { this.immutable = immutable; this.lockMode = lockMode; - timeout = WAIT_FOREVER; + timeout = Timeouts.WAIT_FOREVER; pessimisticLockScope = NORMAL; } /** @@ -212,7 +236,7 @@ private LockOptions(boolean immutable, LockMode lockMode) { */ public boolean isEmpty() { return lockMode == LockMode.NONE - && timeout == WAIT_FOREVER + && timeout == Timeouts.WAIT_FOREVER && followOnLocking == null && pessimisticLockScope == NORMAL && !hasAliasSpecificLockModes(); @@ -370,10 +394,33 @@ public LockMode findGreatestLockMode() { } /** - * The current timeout, a maximum amount of time in milliseconds - * that the database should wait to obtain a pessimistic lock before - * returning an error to the client. - *

+ * The timeout associated with {@code this} options, defining a maximum + * amount of time that the database should wait to obtain a pessimistic + * lock before returning an error to the client. + */ + public Timeout getTimeout() { + return timeout; + } + + /** + * Set the {@linkplain #getTimeout() timeout} associated with {@code this} options. + * + * @return {@code this} for method chaining + * + * @see #getTimeout() + */ + public LockOptions setTimeout(Timeout timeout) { + if ( immutable ) { + throw new UnsupportedOperationException("immutable global instance of LockMode"); + } + this.timeout = timeout; + return this; + } + + /** + * The {@linkplain #getTimeout() timeout}, in milliseconds, associated + * with {@code this} options. + *

* {@link #NO_WAIT}, {@link #WAIT_FOREVER}, or {@link #SKIP_LOCKED} * represent 3 "magic" values. * @@ -381,18 +428,15 @@ public LockMode findGreatestLockMode() { * {@link #WAIT_FOREVER}, or {@link #SKIP_LOCKED} */ public int getTimeOut() { - return timeout; + return timeout.milliseconds(); } /** - * Set the timeout, that is, the maximum amount of time in milliseconds - * that the database should wait to obtain a pessimistic lock before - * returning an error to the client. - *

+ * Set the {@linkplain #getTimeout() timeout}, in milliseconds, associated with {@code this} options. + *

* {@link #NO_WAIT}, {@link #WAIT_FOREVER}, or {@link #SKIP_LOCKED} * represent 3 "magic" values. * - * @param timeout the new timeout setting, in milliseconds * @return {@code this} for method chaining * * @see #getTimeOut @@ -401,8 +445,7 @@ public LockOptions setTimeOut(int timeout) { if ( immutable ) { throw new UnsupportedOperationException("immutable global instance of LockMode"); } - this.timeout = timeout; - return this; + return setTimeout( Timeouts.interpretMilliSeconds( timeout ) ); } /** diff --git a/hibernate-core/src/main/java/org/hibernate/MultiIdentifierLoadAccess.java b/hibernate-core/src/main/java/org/hibernate/MultiIdentifierLoadAccess.java index 5debe9552c66..0d33cbff22db 100644 --- a/hibernate-core/src/main/java/org/hibernate/MultiIdentifierLoadAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/MultiIdentifierLoadAccess.java @@ -8,6 +8,8 @@ import jakarta.persistence.EntityGraph; +import jakarta.persistence.PessimisticLockScope; +import jakarta.persistence.Timeout; import org.hibernate.graph.GraphSemantic; /** @@ -31,6 +33,38 @@ * @author Steve Ebersole */ public interface MultiIdentifierLoadAccess { + + /** + * Specify the {@linkplain LockMode lock mode} to use when + * querying the database. + * + * @param lockMode The lock mode to apply + * @return {@code this}, for method chaining + */ + default MultiIdentifierLoadAccess with(LockMode lockMode) { + return with( lockMode, PessimisticLockScope.NORMAL ); + } + + /** + * Specify the {@linkplain LockMode lock mode} to use when + * querying the database. + * + * @param lockMode The lock mode to apply + * + * @return {@code this}, for method chaining + */ + MultiIdentifierLoadAccess with(LockMode lockMode, PessimisticLockScope lockScope); + + /** + * Specify the {@linkplain Timeout timeout} to use when + * querying the database. + * + * @param timeout The timeout to apply to the database operation + * + * @return {@code this}, for method chaining + */ + MultiIdentifierLoadAccess with(Timeout timeout); + /** * Specify the {@linkplain LockOptions lock options} to use when * querying the database. @@ -38,7 +72,12 @@ public interface MultiIdentifierLoadAccess { * @param lockOptions The lock options to use * * @return {@code this}, for method chaining + * + * @deprecated Use one of {@linkplain #with(LockMode)}, + * {@linkplain #with(LockMode, PessimisticLockScope)} + * and/or {@linkplain #with(Timeout)} instead. */ + @Deprecated(since = "7.0", forRemoval = true) MultiIdentifierLoadAccess with(LockOptions lockOptions); /** diff --git a/hibernate-core/src/main/java/org/hibernate/NaturalIdLoadAccess.java b/hibernate-core/src/main/java/org/hibernate/NaturalIdLoadAccess.java index bb2a9c09a1e8..d60e404cfc9b 100644 --- a/hibernate-core/src/main/java/org/hibernate/NaturalIdLoadAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/NaturalIdLoadAccess.java @@ -6,6 +6,8 @@ import jakarta.persistence.EntityGraph; +import jakarta.persistence.PessimisticLockScope; +import jakarta.persistence.Timeout; import jakarta.persistence.metamodel.SingularAttribute; import org.hibernate.graph.GraphSemantic; @@ -35,6 +37,38 @@ * @see SimpleNaturalIdLoadAccess */ public interface NaturalIdLoadAccess { + + /** + * Specify the {@linkplain LockMode lock mode} to use when + * querying the database. + * + * @param lockMode The lock mode to apply + * @return {@code this}, for method chaining + */ + default NaturalIdLoadAccess with(LockMode lockMode) { + return with( lockMode, PessimisticLockScope.NORMAL ); + } + + /** + * Specify the {@linkplain LockMode lock mode} to use when + * querying the database. + * + * @param lockMode The lock mode to apply + * + * @return {@code this}, for method chaining + */ + NaturalIdLoadAccess with(LockMode lockMode, PessimisticLockScope lockScope); + + /** + * Specify the {@linkplain Timeout timeout} to use when + * querying the database. + * + * @param timeout The timeout to apply to the database operation + * + * @return {@code this}, for method chaining + */ + NaturalIdLoadAccess with(Timeout timeout); + /** * Specify the {@linkplain LockOptions lock options} to use when * querying the database. @@ -42,7 +76,12 @@ public interface NaturalIdLoadAccess { * @param lockOptions The lock options to use. * * @return {@code this}, for method chaining + * + * @deprecated Use one of {@linkplain #with(LockMode)}, + * {@linkplain #with(LockMode, PessimisticLockScope)} + * and/or {@linkplain #with(Timeout)} instead. */ + @Deprecated(since = "7.0", forRemoval = true) NaturalIdLoadAccess with(LockOptions lockOptions); /** diff --git a/hibernate-core/src/main/java/org/hibernate/NaturalIdMultiLoadAccess.java b/hibernate-core/src/main/java/org/hibernate/NaturalIdMultiLoadAccess.java index dd5d329e8130..29c21475af44 100644 --- a/hibernate-core/src/main/java/org/hibernate/NaturalIdMultiLoadAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/NaturalIdMultiLoadAccess.java @@ -6,6 +6,8 @@ import jakarta.persistence.EntityGraph; +import jakarta.persistence.PessimisticLockScope; +import jakarta.persistence.Timeout; import org.hibernate.graph.GraphSemantic; import java.util.List; @@ -36,6 +38,38 @@ * @see org.hibernate.annotations.NaturalId */ public interface NaturalIdMultiLoadAccess { + + /** + * Specify the {@linkplain LockMode lock mode} to use when + * querying the database. + * + * @param lockMode The lock mode to apply + * @return {@code this}, for method chaining + */ + default NaturalIdMultiLoadAccess with(LockMode lockMode) { + return with( lockMode, PessimisticLockScope.NORMAL ); + } + + /** + * Specify the {@linkplain LockMode lock mode} to use when + * querying the database. + * + * @param lockMode The lock mode to apply + * + * @return {@code this}, for method chaining + */ + NaturalIdMultiLoadAccess with(LockMode lockMode, PessimisticLockScope lockScope); + + /** + * Specify the {@linkplain Timeout timeout} to use when + * querying the database. + * + * @param timeout The timeout to apply to the database operation + * + * @return {@code this}, for method chaining + */ + NaturalIdMultiLoadAccess with(Timeout timeout); + /** * Specify the {@linkplain LockOptions lock options} to use when * querying the database. @@ -43,7 +77,12 @@ public interface NaturalIdMultiLoadAccess { * @param lockOptions The lock options to use * * @return {@code this}, for method chaining + * + * @deprecated Use one of {@linkplain #with(LockMode)}, + * {@linkplain #with(LockMode, PessimisticLockScope)} + * and/or {@linkplain #with(Timeout)} instead. */ + @Deprecated(since = "7.0", forRemoval = true) NaturalIdMultiLoadAccess with(LockOptions lockOptions); /** diff --git a/hibernate-core/src/main/java/org/hibernate/Session.java b/hibernate-core/src/main/java/org/hibernate/Session.java index e345b9f90039..151c10297441 100644 --- a/hibernate-core/src/main/java/org/hibernate/Session.java +++ b/hibernate-core/src/main/java/org/hibernate/Session.java @@ -9,6 +9,8 @@ import java.util.function.Consumer; import jakarta.persistence.FindOption; +import jakarta.persistence.LockOption; +import jakarta.persistence.RefreshOption; import jakarta.persistence.metamodel.EntityType; import org.hibernate.graph.RootGraph; import org.hibernate.jdbc.Work; @@ -507,7 +509,7 @@ public interface Session extends SharedSessionContract, EntityManager { *

    *
  • call {@link #find(Class, Object, FindOption...)}, passing * {@link CacheRetrieveMode#BYPASS} as an option, - *
  • call {@link #find(Class, Object, LockMode)} with the explicit lock mode + *
  • call {@link #find(Class, Object, FindOption...)} with the explicit lock mode * {@link LockMode#READ}, or *
  • {@linkplain #setCacheRetrieveMode set the cache mode} to * {@link CacheRetrieveMode#BYPASS} before calling this method. @@ -523,42 +525,6 @@ public interface Session extends SharedSessionContract, EntityManager { @Override T find(Class entityType, Object id); - /** - * Return the persistent instance of the given entity class with the given identifier, - * or null if there is no such persistent instance. If the instance is already associated - * with the session, return that instance. This method never returns an uninitialized - * instance. Obtain the specified lock mode if the instance exists. - *

    - * Convenient form of {@link #find(Class, Object, LockOptions)}. - * - * @param entityType the entity type - * @param id an identifier - * @param lockMode the lock mode - * - * @return a fully-fetched persistent instance or null - * - * @since 7.0 - * - * @see #find(Class, Object, LockOptions) - */ - T find(Class entityType, Object id, LockMode lockMode); - - /** - * Return the persistent instance of the given entity class with the given identifier, - * or null if there is no such persistent instance. If the instance is already associated - * with the session, return that instance. This method never returns an uninitialized - * instance. Obtain the specified lock mode if the instance exists. - * - * @param entityType the entity type - * @param id an identifier - * @param lockOptions the lock mode - * - * @return a fully-fetched persistent instance or null - * - * @since 7.0 - */ - T find(Class entityType, Object id, LockOptions lockOptions); - /** * Return the persistent instances of the given entity class with the given identifiers * as a list. The position of an instance in the returned list matches the position of its @@ -782,22 +748,43 @@ public interface Session extends SharedSessionContract, EntityManager { * * @param object a persistent instance associated with this session * @param lockMode the lock level + * + * @see #lock(Object, LockModeType) */ void lock(Object object, LockMode lockMode); /** - * Obtain a lock on the given managed instance associated with this session, - * using the given {@linkplain LockOptions lock options}. + * Obtain the specified lock level on the given managed instance associated + * with this session, applying any other specified options. This operation may + * be used to: + *

      + *
    • perform a version check on an entity read from the second-level cache + * by requesting {@link LockMode#READ}, + *
    • schedule a version check at transaction commit by requesting + * {@link LockMode#OPTIMISTIC}, + *
    • schedule a version increment at transaction commit by requesting + * {@link LockMode#OPTIMISTIC_FORCE_INCREMENT} + *
    • upgrade to a pessimistic lock with {@link LockMode#PESSIMISTIC_READ} + * or {@link LockMode#PESSIMISTIC_WRITE}, or + *
    • immediately increment the version of the given instance by requesting + * {@link LockMode#PESSIMISTIC_FORCE_INCREMENT}. + *
    + *

    + * If the requested lock mode is already held on the given entity, this + * operation has no effect. *

    * This operation cascades to associated instances if the association is * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. + *

    + * The modes {@link LockMode#WRITE} and {@link LockMode#UPGRADE_SKIPLOCKED} + * are not legal arguments to {@code lock()}. * * @param object a persistent instance associated with this session - * @param lockOptions the lock options + * @param lockMode the lock level * - * @since 6.2 + * @see #lock(Object, LockModeType, LockOption...) */ - void lock(Object object, LockOptions lockOptions); + void lock(Object object, LockMode lockMode, LockOption... lockOptions); /** * Reread the state of the given managed instance associated with this session @@ -822,28 +809,6 @@ public interface Session extends SharedSessionContract, EntityManager { @Override void refresh(Object object); - /** - * Reread the state of the given managed instance from the underlying database, - * obtaining the given {@link LockMode}. - *

    - * Convenient form of {@link #refresh(Object, LockOptions)} - * - * @param object a persistent instance associated with this session - * @param lockMode the lock mode to use - * - * @see #refresh(Object, LockOptions) - */ - void refresh(Object object, LockMode lockMode); - - /** - * Reread the state of the given managed instance from the underlying database, - * obtaining the given {@link LockMode}. - * - * @param object a persistent instance associated with this session - * @param lockOptions contains the lock mode to use - */ - void refresh(Object object, LockOptions lockOptions); - /** * Mark a persistence instance associated with this session for removal from * the underlying database. Ths operation cascades to associated instances if @@ -909,7 +874,7 @@ public interface Session extends SharedSessionContract, EntityManager { * @deprecated Because the semantics of this method may change in a future release. * Use {@link #find(Class, Object)} instead. */ - @Deprecated(since = "7") + @Deprecated(since = "7.0", forRemoval = true) T get(Class entityType, Object id); /** @@ -917,8 +882,6 @@ public interface Session extends SharedSessionContract, EntityManager { * or null if there is no such persistent instance. If the instance is already associated * with the session, return that instance. This method never returns an uninitialized * instance. Obtain the specified lock mode if the instance exists. - *

    - * Convenient form of {@link #get(Class, Object, LockOptions)}. * * @apiNote This operation is very similar to {@link #find(Class, Object, LockModeType)}. * @@ -928,32 +891,11 @@ public interface Session extends SharedSessionContract, EntityManager { * * @return a persistent instance or null * - * @see #get(Class, Object, LockOptions) - * - * @deprecated The semantics of this method may change in a future release. - * Use {@link #find(Class, Object, LockMode)} instead. + * @deprecated Use {@link #find(Class, Object, FindOption...)} instead. */ - @Deprecated(since = "7") + @Deprecated(since = "7.0", forRemoval = true) T get(Class entityType, Object id, LockMode lockMode); - /** - * Return the persistent instance of the given entity class with the given identifier, - * or null if there is no such persistent instance. If the instance is already associated - * with the session, return that instance. This method never returns an uninitialized - * instance. Obtain the specified lock mode if the instance exists. - * - * @param entityType the entity type - * @param id an identifier - * @param lockOptions the lock mode - * - * @return a persistent instance or null - * - * @deprecated The semantics of this method may change in a future release. - * Use {@link #find(Class, Object, LockOptions)} instead. - */ - @Deprecated(since = "7") - T get(Class entityType, Object id, LockOptions lockOptions); - /** * Return the persistent instance of the given named entity with the given identifier, * or null if there is no such persistent instance. If the instance is already associated @@ -974,7 +916,7 @@ public interface Session extends SharedSessionContract, EntityManager { * @see SessionFactory#createGraphForDynamicEntity(String) * @see #find(EntityGraph, Object, FindOption...) */ - @Deprecated(since = "7") + @Deprecated(since = "7", forRemoval = true) Object get(String entityName, Object id); /** @@ -982,8 +924,6 @@ public interface Session extends SharedSessionContract, EntityManager { * or null if there is no such persistent instance. If the instance is already associated * with the session, return that instance. This method never returns an uninitialized * instance. Obtain the specified lock mode if the instance exists. - *

    - * Convenient form of {@link #get(String, Object, LockOptions)} * * @param entityName the entity name * @param id an identifier @@ -995,7 +935,7 @@ public interface Session extends SharedSessionContract, EntityManager { * * @deprecated The semantics of this method may change in a future release. */ - @Deprecated(since = "7") + @Deprecated(since = "7.0", forRemoval = true) Object get(String entityName, Object id, LockMode lockMode); /** @@ -1010,11 +950,40 @@ public interface Session extends SharedSessionContract, EntityManager { * * @return a persistent instance or null * - * @deprecated The semantics of this method may change in a future release. + * @deprecated Use {@linkplain #find(Class, Object, FindOption...)} instead */ - @Deprecated(since = "7") + @Deprecated(since = "7.0", forRemoval = true) Object get(String entityName, Object id, LockOptions lockOptions); + /** + * Obtain a lock on the given managed instance associated with this session, + * using the given {@linkplain LockOptions lock options}. + *

    + * This operation cascades to associated instances if the association is + * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. + * + * @param object a persistent instance associated with this session + * @param lockOptions the lock options + * + * @since 6.2 + * + * @deprecated Use {@linkplain #lock(Object, LockModeType, LockOption...)} instead + */ + @Deprecated(since = "7.0", forRemoval = true) + void lock(Object object, LockOptions lockOptions); + + /** + * Reread the state of the given managed instance from the underlying database, + * obtaining the given {@link LockMode}. + * + * @param object a persistent instance associated with this session + * @param lockOptions contains the lock mode to use + * + * @deprecated Use {@linkplain #refresh(Object, RefreshOption...)} instead + */ + @Deprecated(since = "7.0", forRemoval = true) + void refresh(Object object, LockOptions lockOptions); + /** * Return the entity name for a persistent entity. * diff --git a/hibernate-core/src/main/java/org/hibernate/SimpleNaturalIdLoadAccess.java b/hibernate-core/src/main/java/org/hibernate/SimpleNaturalIdLoadAccess.java index 4b3ad6c3d77b..13130fd7c18e 100644 --- a/hibernate-core/src/main/java/org/hibernate/SimpleNaturalIdLoadAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/SimpleNaturalIdLoadAccess.java @@ -6,6 +6,8 @@ import jakarta.persistence.EntityGraph; +import jakarta.persistence.PessimisticLockScope; +import jakarta.persistence.Timeout; import org.hibernate.graph.GraphSemantic; import java.util.Optional; @@ -29,6 +31,38 @@ * @see NaturalIdLoadAccess */ public interface SimpleNaturalIdLoadAccess { + + /** + * Specify the {@linkplain LockMode lock mode} to use when + * querying the database. + * + * @param lockMode The lock mode to apply + * @return {@code this}, for method chaining + */ + default SimpleNaturalIdLoadAccess with(LockMode lockMode) { + return with( lockMode, PessimisticLockScope.NORMAL ); + } + + /** + * Specify the {@linkplain LockMode lock mode} to use when + * querying the database. + * + * @param lockMode The lock mode to apply + * + * @return {@code this}, for method chaining + */ + SimpleNaturalIdLoadAccess with(LockMode lockMode, PessimisticLockScope lockScope); + + /** + * Specify the {@linkplain Timeout timeout} to use when + * querying the database. + * + * @param timeout The timeout to apply to the database operation + * + * @return {@code this}, for method chaining + */ + SimpleNaturalIdLoadAccess with(Timeout timeout); + /** * Specify the {@linkplain LockOptions lock options} to use when * querying the database. @@ -36,7 +70,12 @@ public interface SimpleNaturalIdLoadAccess { * @param lockOptions The lock options to use * * @return {@code this}, for method chaining + * + * @deprecated Use one of {@linkplain #with(LockMode)}, + * {@linkplain #with(LockMode, PessimisticLockScope)} + * and/or {@linkplain #with(Timeout)} instead. */ + @Deprecated(since = "7.0", forRemoval = true) SimpleNaturalIdLoadAccess with(LockOptions lockOptions); /** diff --git a/hibernate-core/src/main/java/org/hibernate/Timeouts.java b/hibernate-core/src/main/java/org/hibernate/Timeouts.java new file mode 100644 index 000000000000..e5f1e32a1dab --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/Timeouts.java @@ -0,0 +1,113 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate; + +import jakarta.persistence.Timeout; + +/** + * Helpers for dealing with {@linkplain jakarta.persistence.Timeout time out} values, + * including some "magic values". + * + * @apiNote The {@linkplain #NO_WAIT} and {@linkplain #SKIP_LOCKED} magic values have + * special {@linkplain LockMode} values as well ({@linkplain LockMode#UPGRADE_NOWAIT} + * and {@linkplain LockMode#UPGRADE_SKIPLOCKED}, respectively). + * + * @author Steve Ebersole + * + * @since 7.0 + */ +public interface Timeouts { + /** + * Raw magic millisecond value for {@linkplain #NO_WAIT} + */ + int NO_WAIT_MILLI = 0; + + /** + * Raw magic millisecond value for {@linkplain #WAIT_FOREVER} + */ + int WAIT_FOREVER_MILLI = -1; + + /** + * Raw magic millisecond value for {@linkplain #SKIP_LOCKED} + */ + int SKIP_LOCKED_MILLI = -2; + + /** + * Indicates that the database should not wait at all to acquire + * a pessimistic lock which is not immediately available. + * + * @see #NO_WAIT_MILLI + * @see LockMode#UPGRADE_NOWAIT + */ + Timeout NO_WAIT = Timeout.milliseconds( NO_WAIT_MILLI ); + + /** + * Indicates that there is no timeout for the lock acquisition, + * that is, that the database should in principle wait forever + * to obtain the lock. + * + * @see #WAIT_FOREVER_MILLI + */ + Timeout WAIT_FOREVER = Timeout.milliseconds( WAIT_FOREVER_MILLI ); + + /** + * Indicates that rows which are already locked should be skipped. + * + * @see #SKIP_LOCKED_MILLI + * @see LockMode#UPGRADE_SKIPLOCKED + */ + Timeout SKIP_LOCKED = Timeout.milliseconds( SKIP_LOCKED_MILLI ); + + /** + * Similar to simply calling {@linkplain Timeout#milliseconds(int)}, but accounting for + * the "magic values". + */ + static Timeout interpretMilliSeconds(int timeoutInMilliseconds) { + if ( timeoutInMilliseconds == NO_WAIT_MILLI ) { + return NO_WAIT; + } + else if ( timeoutInMilliseconds == WAIT_FOREVER_MILLI ) { + return WAIT_FOREVER; + } + else if ( timeoutInMilliseconds == SKIP_LOCKED_MILLI ) { + return SKIP_LOCKED; + } + return Timeout.milliseconds( timeoutInMilliseconds ); + } + + /** + * Is the timeout value a real value, as opposed to one of the + * "magic values". Functionally, returns whether the value is + * greater than zero. + */ + static boolean isRealTimeout(Timeout timeout) { + return isRealTimeout( timeout.milliseconds() ); + } + + /** + * Is the timeout value a real value, as opposed to one of the + * "magic values". Functionally, returns whether the value is + * greater than zero. + */ + static boolean isRealTimeout(int timeoutInMilliseconds) { + return timeoutInMilliseconds > 0; + } + + /** + * Get the number of (whole) seconds represented by the given {@code timeout}. + */ + static int getTimeoutInSeconds(Timeout timeout) { + return getTimeoutInSeconds( timeout.milliseconds() ); + } + + /** + * Get the number of (whole) seconds represented by the given {@code timeout}. + */ + static int getTimeoutInSeconds(int timeoutInMilliseconds) { + // should never be negative here... + assert timeoutInMilliseconds >= 0; + return timeoutInMilliseconds == 0 ? 0 : Math.max( 1, Math.round( timeoutInMilliseconds / 1e3f ) ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java index 210e48570131..5e152b9ff881 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java @@ -4,24 +4,14 @@ */ package org.hibernate.dialect; -import java.sql.DatabaseMetaData; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Types; -import java.time.temporal.ChronoField; -import java.time.temporal.TemporalAccessor; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.TimeZone; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - +import jakarta.persistence.GenerationType; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Timeout; import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.QueryTimeoutException; +import org.hibernate.Timeouts; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.dialect.aggregate.AggregateSupport; @@ -60,8 +50,8 @@ import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.query.SemanticException; -import org.hibernate.query.sqm.IntervalType; import org.hibernate.query.common.TemporalUnit; +import org.hibernate.query.sqm.IntervalType; import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers; import org.hibernate.service.ServiceRegistry; import org.hibernate.sql.ast.SqlAstTranslator; @@ -85,8 +75,19 @@ import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; import org.hibernate.type.spi.TypeConfiguration; -import jakarta.persistence.GenerationType; -import jakarta.persistence.TemporalType; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static java.lang.Integer.parseInt; import static org.hibernate.cfg.DialectSpecificSettings.COCKROACH_VERSION_STRING; @@ -930,14 +931,38 @@ public String getForUpdateString(String aliases, LockOptions lockOptions) { }; } + private String withTimeout(String lockString, Timeout timeout) { + return withTimeout( lockString, timeout.milliseconds() ); + } + private String withTimeout(String lockString, int timeout) { return switch (timeout) { - case LockOptions.NO_WAIT -> supportsNoWait() ? lockString + " nowait" : lockString; - case LockOptions.SKIP_LOCKED -> supportsSkipLocked() ? lockString + " skip locked" : lockString; + case Timeouts.NO_WAIT_MILLI -> supportsNoWait() ? lockString + " nowait" : lockString; + case Timeouts.SKIP_LOCKED_MILLI -> supportsSkipLocked() ? lockString + " skip locked" : lockString; default -> lockString; }; } + @Override + public String getWriteLockString(Timeout timeout) { + return withTimeout( getForUpdateString(), timeout ); + } + + @Override + public String getWriteLockString(String aliases, Timeout timeout) { + return withTimeout( getForUpdateString( aliases ), timeout ); + } + + @Override + public String getReadLockString(Timeout timeout) { + return withTimeout( " for share", timeout ); + } + + @Override + public String getReadLockString(String aliases, Timeout timeout) { + return withTimeout( " for share of " + aliases, timeout ); + } + @Override public String getWriteLockString(int timeout) { return withTimeout( getForUpdateString(), timeout ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java index 2bfcff6efd78..135f74e73391 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java @@ -4,25 +4,9 @@ */ package org.hibernate.dialect; -import java.sql.CallableStatement; -import java.sql.DatabaseMetaData; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Types; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.OffsetDateTime; -import java.time.OffsetTime; -import java.time.ZonedDateTime; -import java.time.temporal.TemporalAccessor; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import java.util.TimeZone; - -import org.hibernate.LockOptions; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Timeout; +import org.hibernate.Timeouts; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.dialect.aggregate.AggregateSupport; @@ -63,9 +47,9 @@ import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.procedure.internal.DB2CallableStatementSupport; import org.hibernate.procedure.spi.CallableStatementSupport; +import org.hibernate.query.common.TemporalUnit; import org.hibernate.query.sqm.CastType; import org.hibernate.query.sqm.IntervalType; -import org.hibernate.query.common.TemporalUnit; import org.hibernate.query.sqm.mutation.internal.cte.CteInsertStrategy; import org.hibernate.query.sqm.mutation.internal.cte.CteMutationStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; @@ -108,7 +92,23 @@ import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; import org.hibernate.type.spi.TypeConfiguration; -import jakarta.persistence.TemporalType; +import java.sql.CallableStatement; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.ZonedDateTime; +import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; import static org.hibernate.internal.util.JdbcExceptionHelper.extractErrorCode; @@ -851,16 +851,30 @@ public String getForUpdateSkipLockedString(String aliases) { return getForUpdateSkipLockedString(); } + @Override + public String getWriteLockString(Timeout timeout) { + return timeout.milliseconds() == Timeouts.SKIP_LOCKED_MILLI && supportsSkipLocked() + ? FOR_UPDATE_SKIP_LOCKED_SQL + : FOR_UPDATE_SQL; + } + + @Override + public String getReadLockString(Timeout timeout) { + return timeout.milliseconds() == Timeouts.SKIP_LOCKED_MILLI && supportsSkipLocked() + ? FOR_SHARE_SKIP_LOCKED_SQL + : FOR_SHARE_SQL; + } + @Override public String getWriteLockString(int timeout) { - return timeout == LockOptions.SKIP_LOCKED && supportsSkipLocked() + return timeout == Timeouts.SKIP_LOCKED_MILLI && supportsSkipLocked() ? FOR_UPDATE_SKIP_LOCKED_SQL : FOR_UPDATE_SQL; } @Override public String getReadLockString(int timeout) { - return timeout == LockOptions.SKIP_LOCKED && supportsSkipLocked() + return timeout == Timeouts.SKIP_LOCKED_MILLI && supportsSkipLocked() ? FOR_SHARE_SKIP_LOCKED_SQL : FOR_SHARE_SQL; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java index e05c8b990ef4..64a4dcdf82cd 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java @@ -4,7 +4,8 @@ */ package org.hibernate.dialect; -import org.hibernate.LockOptions; +import jakarta.persistence.Timeout; +import org.hibernate.Timeouts; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.identity.DB2IdentityColumnSupport; @@ -193,16 +194,30 @@ public String getForUpdateSkipLockedString(String aliases) { return getForUpdateSkipLockedString(); } + @Override + public String getWriteLockString(Timeout timeout) { + return timeout.milliseconds() == Timeouts.SKIP_LOCKED_MILLI && supportsSkipLocked() + ? FOR_UPDATE_SKIP_LOCKED_SQL + : FOR_UPDATE_SQL; + } + + @Override + public String getReadLockString(Timeout timeout) { + return timeout.milliseconds() == Timeouts.SKIP_LOCKED_MILLI && supportsSkipLocked() + ? FOR_UPDATE_SKIP_LOCKED_SQL + : FOR_UPDATE_SQL; + } + @Override public String getWriteLockString(int timeout) { - return timeout == LockOptions.SKIP_LOCKED && supportsSkipLocked() + return timeout == Timeouts.SKIP_LOCKED_MILLI && supportsSkipLocked() ? FOR_UPDATE_SKIP_LOCKED_SQL : FOR_UPDATE_SQL; } @Override public String getReadLockString(int timeout) { - return timeout == LockOptions.SKIP_LOCKED && supportsSkipLocked() + return timeout == Timeouts.SKIP_LOCKED_MILLI && supportsSkipLocked() ? FOR_UPDATE_SKIP_LOCKED_SQL : FOR_UPDATE_SQL; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index a864f910ff31..3da266161914 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -6,6 +6,7 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.TemporalType; +import jakarta.persistence.Timeout; import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.HibernateException; import org.hibernate.Incubating; @@ -13,6 +14,7 @@ import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.ScrollMode; +import org.hibernate.Timeouts; import org.hibernate.boot.TempTableDdlTransactionHandling; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.FunctionContributor; @@ -130,10 +132,10 @@ import org.hibernate.tool.schema.internal.StandardTableMigrator; import org.hibernate.tool.schema.internal.StandardUniqueKeyExporter; import org.hibernate.tool.schema.internal.StandardUserDefinedTypeExporter; -import org.hibernate.tool.schema.spi.TableMigrator; import org.hibernate.tool.schema.spi.Cleaner; import org.hibernate.tool.schema.spi.Exporter; import org.hibernate.tool.schema.spi.SchemaManagementTool; +import org.hibernate.tool.schema.spi.TableMigrator; import org.hibernate.type.BasicType; import org.hibernate.type.BasicTypeRegistry; import org.hibernate.type.SqlTypes; @@ -2200,7 +2202,26 @@ public LockingStrategy getLockingStrategy(EntityPersister lockable, LockMode loc * @return The appropriate {@code for update} fragment. */ public String getForUpdateString(LockOptions lockOptions) { - return getForUpdateString( lockOptions.getLockMode(), lockOptions.getTimeOut() ); + return getForUpdateString( lockOptions.getLockMode(), lockOptions.getTimeout() ); + } + + /** + * Given a {@linkplain LockMode lock level} and timeout, + * determine the appropriate {@code for update} fragment to + * use to obtain the lock. + * + * @param lockMode the lock mode to apply. + * @param timeout the timeout + * @return The appropriate {@code for update} fragment. + */ + public String getForUpdateString(LockMode lockMode, Timeout timeout) { + return switch (lockMode) { + case PESSIMISTIC_READ -> getReadLockString( timeout ); + case PESSIMISTIC_WRITE -> getWriteLockString( timeout ); + case UPGRADE_NOWAIT, PESSIMISTIC_FORCE_INCREMENT -> getForUpdateNowaitString(); + case UPGRADE_SKIPLOCKED -> getForUpdateSkipLockedString(); + default -> ""; + }; } /** @@ -2211,8 +2232,11 @@ public String getForUpdateString(LockOptions lockOptions) { * @param lockMode the lock mode to apply. * @param timeout the timeout * @return The appropriate {@code for update} fragment. + * + * @deprecated Use {@linkplain #getForUpdateString(LockMode,Timeout)} instead */ - private String getForUpdateString(LockMode lockMode, int timeout) { + @Deprecated(since = "7.0") + public String getForUpdateString(LockMode lockMode, int timeout) { return switch (lockMode) { case PESSIMISTIC_READ -> getReadLockString( timeout ); case PESSIMISTIC_WRITE -> getWriteLockString( timeout ); @@ -2230,7 +2254,7 @@ private String getForUpdateString(LockMode lockMode, int timeout) { * @return The appropriate for update fragment. */ public String getForUpdateString(LockMode lockMode) { - return getForUpdateString( lockMode, LockOptions.WAIT_FOREVER ); + return getForUpdateString( lockMode, Timeouts.WAIT_FOREVER ); } /** @@ -2243,6 +2267,27 @@ public String getForUpdateString() { return " for update"; } + /** + * Get the string to append to {@code SELECT} statements to + * acquire pessimistic WRITE locks for this dialect. + * + * @param timeout How long the database should wait to acquire the lock. + * See {@linkplain Timeouts} for some "magic values". + * + * @return The appropriate lock clause. + */ + public String getWriteLockString(Timeout timeout) { + if ( timeout.milliseconds() == Timeouts.SKIP_LOCKED_MILLI && supportsSkipLocked() ) { + return getForUpdateSkipLockedString(); + } + else if ( timeout.milliseconds() == Timeouts.NO_WAIT_MILLI && supportsNoWait() ) { + return getForUpdateNowaitString(); + } + else { + return getForUpdateString(); + } + } + /** * Get the string to append to {@code SELECT} statements to * acquire pessimistic WRITE locks for this dialect. @@ -2250,31 +2295,85 @@ public String getForUpdateString() { * Location of the returned string is treated the same as * {@link #getForUpdateString()}. * - * @param timeout in milliseconds, -1 for indefinite wait and 0 for no wait. + * @param timeout How long, in milliseconds, the database should wait to acquire the lock. + * See {@linkplain Timeouts} for some "magic values". + * * @return The appropriate {@code LOCK} clause string. + * + * @deprecated Use {@linkplain #getWriteLockString(Timeout)} instead. */ + @Deprecated(since = "7.0") public String getWriteLockString(int timeout) { - return getForUpdateString(); + if ( timeout == Timeouts.SKIP_LOCKED_MILLI && supportsSkipLocked() ) { + return getForUpdateSkipLockedString(); + } + else if ( timeout == Timeouts.NO_WAIT_MILLI && supportsNoWait() ) { + return getForUpdateNowaitString(); + } + else { + return getForUpdateString(); + } } /** * Get the string to append to {@code SELECT} statements to * acquire WRITE locks for this dialect, given the aliases of - * the columns to be write locked. + * the columns to be WRITE locked. + * * + * * @param timeout How long the database should wait to acquire the lock. *

    * Location of the returned string is treated the same as * {@link #getForUpdateString()}. * * @param aliases The columns to be read locked. - * @param timeout in milliseconds, -1 for indefinite wait and 0 for no wait. + * @param timeout How long the database should wait to acquire the lock. + * See {@linkplain Timeouts} for some "magic values". + * * @return The appropriate {@code LOCK} clause string. */ + public String getWriteLockString(String aliases, Timeout timeout) { + // by default, we simply return getWriteLockString(timeout), + // since the default is no support for "FOR UPDATE OF ..." + return getWriteLockString( timeout ); + } + + /** + * Get the string to append to {@code SELECT} statements to + * acquire WRITE locks for this dialect, given the aliases of + * the columns to be WRITE locked. + *

    + * Location of the returned string is treated the same as + * {@link #getForUpdateString()}. + * + * @param aliases The columns to be read locked. + * + * @param timeout How long, in milliseconds, the database should wait to acquire the lock. + * See {@linkplain Timeouts} for some "magic values". + * + * @return The appropriate {@code LOCK} clause string. + * + * @deprecated Use {@linkplain #getWriteLockString(String, Timeout)} instead. + */ + @Deprecated(since = "7.0") public String getWriteLockString(String aliases, int timeout) { // by default, we simply return getWriteLockString(timeout), // since the default is no support for "FOR UPDATE OF ..." return getWriteLockString( timeout ); } + /** + * Get the string to append to {@code SELECT} statements to + * acquire READ locks for this dialect. + * + * @param timeout How long the database should wait to acquire the lock. + * See {@linkplain Timeouts} for some "magic values". + * + * @return The appropriate {@code LOCK} clause string. + */ + public String getReadLockString(Timeout timeout) { + return getForUpdateString(); + } + /** * Get the string to append to {@code SELECT} statements to * acquire READ locks for this dialect. @@ -2284,11 +2383,32 @@ public String getWriteLockString(String aliases, int timeout) { * * @param timeout in milliseconds, -1 for indefinite wait and 0 for no wait. * @return The appropriate {@code LOCK} clause string. + * + * @deprecated Use {@linkplain #getReadLockString(Timeout)} instead. */ + @Deprecated(since = "7.0") public String getReadLockString(int timeout) { return getForUpdateString(); } + /** + * Get the string to append to {@code SELECT} statements to + * acquire READ locks for this dialect, given the aliases of + * the columns to be read locked. + * + * @param aliases The columns to be read locked. + * @param timeout How long the database should wait to acquire the lock. + * See {@linkplain Timeouts} for some "magic values". + * + * @return The appropriate {@code LOCK} clause string. + * + * @implNote By default, simply returns the {@linkplain #getReadLockString(Timeout)} + * result since the default is to say no support for "FOR UPDATE OF ...". + */ + public String getReadLockString(String aliases, Timeout timeout) { + return getReadLockString( timeout ); + } + /** * Get the string to append to {@code SELECT} statements to * acquire READ locks for this dialect, given the aliases of @@ -2299,8 +2419,12 @@ public String getReadLockString(int timeout) { * * @param aliases The columns to be read locked. * @param timeout in milliseconds, -1 for indefinite wait and 0 for no wait. + * * @return The appropriate {@code LOCK} clause string. + * + * @deprecated Use {@linkplain #getReadLockString(String, Timeout)} instead. */ + @Deprecated(since = "7.0") public String getReadLockString(String aliases, int timeout) { // by default we simply return the getReadLockString(timeout) result since // the default is to say no support for "FOR UPDATE OF ..." @@ -2439,8 +2563,12 @@ public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map 0 ) { return getForUpdateString() + " wait " + getTimeoutInSeconds( timeout ); } - else if ( timeout == 0 ) { + else if ( timeout == Timeouts.NO_WAIT_MILLI ) { return getForUpdateNowaitString(); } else { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java index 68d244a0c2ab..91f183850941 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java @@ -4,20 +4,11 @@ */ package org.hibernate.dialect; -import java.sql.CallableStatement; -import java.sql.DatabaseMetaData; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Types; -import java.time.ZonedDateTime; -import java.time.temporal.TemporalAccessor; -import java.util.Calendar; -import java.util.Date; -import java.util.TimeZone; - +import jakarta.persistence.TemporalType; +import jakarta.persistence.Timeout; import org.hibernate.Length; -import org.hibernate.LockOptions; import org.hibernate.QueryTimeoutException; +import org.hibernate.Timeouts; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.cfg.AvailableSettings; @@ -54,14 +45,14 @@ import org.hibernate.mapping.CheckConstraint; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; +import org.hibernate.query.common.TemporalUnit; import org.hibernate.query.sqm.CastType; import org.hibernate.query.sqm.IntervalType; -import org.hibernate.query.common.TemporalUnit; import org.hibernate.query.sqm.function.SqmFunctionRegistry; -import org.hibernate.query.sqm.mutation.spi.AfterUseAction; -import org.hibernate.query.sqm.mutation.spi.BeforeUseAction; import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy; import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy; +import org.hibernate.query.sqm.mutation.spi.AfterUseAction; +import org.hibernate.query.sqm.mutation.spi.BeforeUseAction; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.produce.function.FunctionParameterType; @@ -88,7 +79,16 @@ import org.hibernate.type.descriptor.sql.internal.NativeOrdinalEnumDdlTypeImpl; import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; -import jakarta.persistence.TemporalType; +import java.sql.CallableStatement; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.time.ZonedDateTime; +import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; import static java.lang.Integer.parseInt; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; @@ -1433,11 +1433,46 @@ public static Replacer datetimeFormat(String format) { .replace("S", "%f"); } + private String withTimeout(String lockString, Timeout timeout) { + return switch (timeout.milliseconds()) { + case Timeouts.NO_WAIT_MILLI -> supportsNoWait() ? lockString + " nowait" : lockString; + case Timeouts.SKIP_LOCKED_MILLI -> supportsSkipLocked() ? lockString + " skip locked" : lockString; + case Timeouts.WAIT_FOREVER_MILLI -> lockString; + default -> supportsWait() ? lockString + " wait " + Timeouts.getTimeoutInSeconds( timeout ) : lockString; + }; + } + + @Override + public String getWriteLockString(Timeout timeout) { + return withTimeout( getForUpdateString(), timeout ); + } + + @Override + public String getWriteLockString(String aliases, Timeout timeout) { + return withTimeout( getForUpdateString( aliases ), timeout ); + } + + @Override + public String getReadLockString(Timeout timeout) { + return withTimeout( supportsForShare() ? " for share" : " lock in share mode", timeout ); + } + + @Override + public String getReadLockString(String aliases, Timeout timeout) { + if ( supportsAliasLocks() && supportsForShare() ) { + return withTimeout( " for share of " + aliases, timeout ); + } + else { + // fall back to locking all aliases + return getReadLockString( timeout ); + } + } + private String withTimeout(String lockString, int timeout) { return switch (timeout) { - case LockOptions.NO_WAIT -> supportsNoWait() ? lockString + " nowait" : lockString; - case LockOptions.SKIP_LOCKED -> supportsSkipLocked() ? lockString + " skip locked" : lockString; - case LockOptions.WAIT_FOREVER -> lockString; + case Timeouts.NO_WAIT_MILLI -> supportsNoWait() ? lockString + " nowait" : lockString; + case Timeouts.SKIP_LOCKED_MILLI -> supportsSkipLocked() ? lockString + " skip locked" : lockString; + case Timeouts.WAIT_FOREVER_MILLI -> lockString; default -> supportsWait() ? lockString + " wait " + getTimeoutInSeconds( timeout ) : lockString; }; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java index 6463a4bcd85f..6f0e000c5500 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -6,8 +6,10 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.TemporalType; +import jakarta.persistence.Timeout; import org.hibernate.Length; import org.hibernate.QueryTimeoutException; +import org.hibernate.Timeouts; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.dialect.aggregate.AggregateSupport; @@ -115,9 +117,6 @@ import java.util.regex.Pattern; import static java.util.regex.Pattern.CASE_INSENSITIVE; -import static org.hibernate.LockOptions.NO_WAIT; -import static org.hibernate.LockOptions.SKIP_LOCKED; -import static org.hibernate.LockOptions.WAIT_FOREVER; import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_USE_BINARY_FLOATS; import static org.hibernate.dialect.type.OracleJdbcHelper.getArrayJdbcTypeConstructor; import static org.hibernate.dialect.type.OracleJdbcHelper.getNestedTableJdbcTypeConstructor; @@ -1466,12 +1465,36 @@ public String getForUpdateSkipLockedString(String aliases) { return " for update of " + aliases + " skip locked"; } + private String withTimeout(String lockString, Timeout timeout) { + return withTimeout( lockString, timeout.milliseconds() ); + } + + @Override + public String getWriteLockString(Timeout timeout) { + return withTimeout( getForUpdateString(), timeout ); + } + + @Override + public String getWriteLockString(String aliases, Timeout timeout) { + return withTimeout( getForUpdateString(aliases), timeout ); + } + + @Override + public String getReadLockString(Timeout timeout) { + return getWriteLockString( timeout ); + } + + @Override + public String getReadLockString(String aliases, Timeout timeout) { + return getWriteLockString( aliases, timeout ); + } + private String withTimeout(String lockString, int timeout) { return switch (timeout) { - case NO_WAIT -> supportsNoWait() ? lockString + " nowait" : lockString; - case SKIP_LOCKED -> supportsSkipLocked() ? lockString + " skip locked" : lockString; - case WAIT_FOREVER -> lockString; - default -> supportsWait() ? lockString + " wait " + getTimeoutInSeconds( timeout ) : lockString; + case Timeouts.NO_WAIT_MILLI -> supportsNoWait() ? lockString + " nowait" : lockString; + case Timeouts.SKIP_LOCKED_MILLI -> supportsSkipLocked() ? lockString + " skip locked" : lockString; + case Timeouts.WAIT_FOREVER_MILLI -> lockString; + default -> supportsWait() ? lockString + " wait " + Timeouts.getTimeoutInSeconds( timeout ) : lockString; }; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index 485c34805a43..874d4d52f513 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -4,24 +4,15 @@ */ package org.hibernate.dialect; -import java.sql.CallableStatement; -import java.sql.DatabaseMetaData; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Types; -import java.time.temporal.ChronoField; -import java.time.temporal.TemporalAccessor; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.TimeZone; - +import jakarta.persistence.GenerationType; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Timeout; import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.Length; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.QueryTimeoutException; +import org.hibernate.Timeouts; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.dialect.aggregate.AggregateSupport; @@ -70,11 +61,11 @@ import org.hibernate.procedure.internal.PostgreSQLCallableStatementSupport; import org.hibernate.procedure.spi.CallableStatementSupport; import org.hibernate.query.SemanticException; +import org.hibernate.query.common.FetchClauseType; +import org.hibernate.query.common.TemporalUnit; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.sqm.CastType; -import org.hibernate.query.common.FetchClauseType; import org.hibernate.query.sqm.IntervalType; -import org.hibernate.query.common.TemporalUnit; import org.hibernate.query.sqm.mutation.internal.cte.CteInsertStrategy; import org.hibernate.query.sqm.mutation.internal.cte.CteMutationStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; @@ -112,8 +103,18 @@ import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; import org.hibernate.type.spi.TypeConfiguration; -import jakarta.persistence.GenerationType; -import jakarta.persistence.TemporalType; +import java.sql.CallableStatement; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; import static java.lang.Integer.parseInt; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; @@ -1304,10 +1305,38 @@ public void appendDateTimeLiteral( } } + private String withTimeout(String lockString, Timeout timeout) { + return switch (timeout.milliseconds()) { + case Timeouts.NO_WAIT_MILLI -> supportsNoWait() ? lockString + " nowait" : lockString; + case Timeouts.SKIP_LOCKED_MILLI -> supportsSkipLocked() ? lockString + " skip locked" : lockString; + default -> lockString; + }; + } + + @Override + public String getWriteLockString(Timeout timeout) { + return withTimeout( getForUpdateString(), timeout ); + } + + @Override + public String getWriteLockString(String aliases, Timeout timeout) { + return withTimeout( getForUpdateString( aliases ), timeout ); + } + + @Override + public String getReadLockString(Timeout timeout) { + return withTimeout(" for share", timeout ); + } + + @Override + public String getReadLockString(String aliases, Timeout timeout) { + return withTimeout(" for share of " + aliases, timeout ); + } + private String withTimeout(String lockString, int timeout) { return switch (timeout) { - case LockOptions.NO_WAIT -> supportsNoWait() ? lockString + " nowait" : lockString; - case LockOptions.SKIP_LOCKED -> supportsSkipLocked() ? lockString + " skip locked" : lockString; + case Timeouts.NO_WAIT_MILLI -> supportsNoWait() ? lockString + " nowait" : lockString; + case Timeouts.SKIP_LOCKED_MILLI -> supportsSkipLocked() ? lockString + " skip locked" : lockString; default -> lockString; }; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SpannerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SpannerDialect.java index 4685f17c0984..f7be30145b2f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SpannerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SpannerDialect.java @@ -7,6 +7,7 @@ import java.util.Date; import java.util.Map; +import jakarta.persistence.Timeout; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.StaleObjectStateException; @@ -733,6 +734,30 @@ public String getForUpdateString(String aliases, LockOptions lockOptions) { "Cloud Spanner does not support selecting for lock acquisition." ); } + @Override + public String getWriteLockString(Timeout timeout) { + throw new UnsupportedOperationException( + "Cloud Spanner does not support selecting for lock acquisition." ); + } + + @Override + public String getWriteLockString(String aliases, Timeout timeout) { + throw new UnsupportedOperationException( + "Cloud Spanner does not support selecting for lock acquisition." ); + } + + @Override + public String getReadLockString(Timeout timeout) { + throw new UnsupportedOperationException( + "Cloud Spanner does not support selecting for lock acquisition." ); + } + + @Override + public String getReadLockString(String aliases, Timeout timeout) { + throw new UnsupportedOperationException( + "Cloud Spanner does not support selecting for lock acquisition." ); + } + @Override public String getWriteLockString(int timeout) { throw new UnsupportedOperationException( diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java index 578746ce4c83..f04d30246f30 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java @@ -872,16 +872,6 @@ public T find(EntityGraph entityGraph, Object primaryKey, FindOption... o return delegate.find( entityGraph, primaryKey, options ); } - @Override - public T find(Class entityType, Object id, LockMode lockMode) { - return delegate.find( entityType, id, lockMode ); - } - - @Override - public T find(Class entityType, Object id, LockOptions lockOptions) { - return delegate.find( entityType, id, lockOptions ); - } - @Override public T getReference(Class entityClass, Object id) { return delegate.getReference( entityClass, id ); @@ -902,6 +892,11 @@ public void lock(Object object, LockMode lockMode) { delegate.lock( object, lockMode ); } + @Override + public void lock(Object object, LockMode lockMode, LockOption... lockOptions) { + delegate.lock( object, lockMode, lockOptions ); + } + @Override public void lock(String entityName, Object object, LockOptions lockOptions) { delegate.lock( entityName, object, lockOptions ); @@ -937,11 +932,6 @@ public void refresh(Object entity, RefreshOption... options) { delegate.refresh( entity, options ); } - @Override - public void refresh(Object object, LockMode lockMode) { - delegate.refresh( object, lockMode ); - } - @Override public void refresh(Object object, LockOptions lockOptions) { delegate.refresh( object, lockOptions ); @@ -982,11 +972,6 @@ public T get(Class theClass, Object id, LockMode lockMode) { return delegate.get( theClass, id, lockMode ); } - @Override - public T get(Class theClass, Object id, LockOptions lockOptions) { - return delegate.get( theClass, id, lockOptions ); - } - @Override public Object get(String entityName, Object id) { return delegate.get( entityName, id ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java index cdc8d2c8ddd5..dcebd8a82c9c 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java @@ -231,6 +231,11 @@ public void lock(Object object, LockMode lockMode) { this.lazySession.get().lock( object, lockMode ); } + @Override + public void lock(Object object, LockMode lockMode, LockOption... lockOptions) { + this.lazySession.get().lock( object, lockMode, lockOptions ); + } + @Override public void lock(Object object, LockOptions lockOptions) { this.lazySession.get().lock( object, lockOptions ); @@ -241,11 +246,6 @@ public void refresh(Object object) { this.lazySession.get().refresh( object ); } - @Override - public void refresh(Object object, LockMode lockMode) { - this.lazySession.get().refresh( object, lockMode ); - } - @Override public void refresh(Object object, LockOptions lockOptions) { this.lazySession.get().refresh( object, lockOptions ); @@ -286,11 +286,6 @@ public T get(Class entityType, Object id, LockMode lockMode) { return this.lazySession.get().get( entityType, id, lockMode ); } - @Override - public T get(Class entityType, Object id, LockOptions lockOptions) { - return this.lazySession.get().get( entityType, id, lockOptions ); - } - @Override public Object get(String entityName, Object id) { return this.lazySession.get().get( entityName, id ); @@ -804,16 +799,6 @@ public T find(EntityGraph entityGraph, Object primaryKey, FindOption... o return this.lazySession.get().find( entityGraph, primaryKey, options ); } - @Override - public T find(Class entityType, Object id, LockMode lockMode) { - return this.lazySession.get().find( entityType, id, lockMode ); - } - - @Override - public T find(Class entityType, Object id, LockOptions lockOptions) { - return this.lazySession.get().find( entityType, id, lockOptions ); - } - @Override public void lock(Object entity, LockModeType lockMode) { this.lazySession.get().lock( entity, lockMode ); diff --git a/hibernate-core/src/main/java/org/hibernate/exception/LockTimeoutException.java b/hibernate-core/src/main/java/org/hibernate/exception/LockTimeoutException.java index 2763040ca0c4..a1eaa1cb0130 100644 --- a/hibernate-core/src/main/java/org/hibernate/exception/LockTimeoutException.java +++ b/hibernate-core/src/main/java/org/hibernate/exception/LockTimeoutException.java @@ -21,6 +21,7 @@ * * @author Steve Ebersole * + * @see jakarta.persistence.Timeout * @see org.hibernate.LockOptions#getTimeOut * @see org.hibernate.LockOptions#setTimeOut * @see jakarta.persistence.LockTimeoutException diff --git a/hibernate-core/src/main/java/org/hibernate/internal/MultiIdentifierLoadAccessImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/MultiIdentifierLoadAccessImpl.java index bba064dd9b6d..ab3ab06a513d 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/MultiIdentifierLoadAccessImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/MultiIdentifierLoadAccessImpl.java @@ -4,14 +4,11 @@ */ package org.hibernate.internal; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.Supplier; - import jakarta.persistence.EntityGraph; - +import jakarta.persistence.PessimisticLockScope; +import jakarta.persistence.Timeout; import org.hibernate.CacheMode; +import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.MultiIdentifierLoadAccess; import org.hibernate.UnknownProfileException; @@ -21,8 +18,13 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.spi.RootGraphImplementor; -import org.hibernate.persister.entity.EntityPersister; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; +import org.hibernate.persister.entity.EntityPersister; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; import static java.util.Collections.emptyList; @@ -53,6 +55,25 @@ public MultiIdentifierLoadAccessImpl(SharedSessionContractImplementor session, E this.entityPersister = entityPersister; } + @Override + public MultiIdentifierLoadAccess with(LockMode lockMode, PessimisticLockScope lockScope) { + if ( lockOptions == null ) { + lockOptions = new LockOptions(); + } + lockOptions.setLockMode( lockMode ); + lockOptions.setLockScope( lockScope ); + return this; + } + + @Override + public MultiIdentifierLoadAccess with(Timeout timeout) { + if ( lockOptions == null ) { + lockOptions = new LockOptions(); + } + lockOptions.setTimeOut( timeout.milliseconds() ); + return this; + } + @Override public LockOptions getLockOptions() { return lockOptions; diff --git a/hibernate-core/src/main/java/org/hibernate/internal/NaturalIdMultiLoadAccessStandard.java b/hibernate-core/src/main/java/org/hibernate/internal/NaturalIdMultiLoadAccessStandard.java index 5b28a0f480cb..61f010701235 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/NaturalIdMultiLoadAccessStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/NaturalIdMultiLoadAccessStandard.java @@ -8,7 +8,10 @@ import jakarta.persistence.EntityGraph; +import jakarta.persistence.PessimisticLockScope; +import jakarta.persistence.Timeout; import org.hibernate.CacheMode; +import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.NaturalIdMultiLoadAccess; import org.hibernate.engine.spi.EffectiveEntityGraph; @@ -43,6 +46,25 @@ public NaturalIdMultiLoadAccessStandard(EntityPersister entityDescriptor, Shared this.session = session; } + @Override + public NaturalIdMultiLoadAccess with(LockMode lockMode, PessimisticLockScope lockScope) { + if ( lockOptions == null ) { + lockOptions = new LockOptions(); + } + lockOptions.setLockMode( lockMode ); + lockOptions.setLockScope( lockScope ); + return this; + } + + @Override + public NaturalIdMultiLoadAccess with(Timeout timeout) { + if ( lockOptions == null ) { + lockOptions = new LockOptions(); + } + lockOptions.setTimeOut( timeout.milliseconds() ); + return this; + } + @Override public NaturalIdMultiLoadAccess with(LockOptions lockOptions) { this.lockOptions = lockOptions; diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 5f2dabfda8fe..5d18103cf189 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -4,61 +4,23 @@ */ package org.hibernate.internal; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Reader; -import java.io.Serial; -import java.io.Serializable; -import java.sql.Blob; -import java.sql.Clob; -import java.sql.Connection; -import java.sql.NClob; -import java.sql.SQLException; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; -import java.util.function.UnaryOperator; - +import jakarta.persistence.CacheRetrieveMode; +import jakarta.persistence.CacheStoreMode; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.EntityNotFoundException; +import jakarta.persistence.FindOption; +import jakarta.persistence.FlushModeType; +import jakarta.persistence.LockModeType; +import jakarta.persistence.LockOption; +import jakarta.persistence.PersistenceException; import jakarta.persistence.PessimisticLockScope; +import jakarta.persistence.RefreshOption; import jakarta.persistence.Timeout; +import jakarta.persistence.TransactionRequiredException; +import jakarta.persistence.criteria.CriteriaSelect; import jakarta.persistence.metamodel.EntityType; -import org.hibernate.BatchSize; -import org.hibernate.CacheMode; -import org.hibernate.ConnectionAcquisitionMode; -import org.hibernate.ConnectionReleaseMode; -import org.hibernate.EnabledFetchProfile; -import org.hibernate.EntityFilterException; -import org.hibernate.FetchNotFoundException; -import org.hibernate.FlushMode; -import org.hibernate.HibernateException; -import org.hibernate.Interceptor; -import org.hibernate.JDBCException; -import org.hibernate.LobHelper; -import org.hibernate.LockMode; -import org.hibernate.LockOptions; -import org.hibernate.MappingException; -import org.hibernate.MultiIdentifierLoadAccess; -import org.hibernate.NaturalIdLoadAccess; -import org.hibernate.NaturalIdMultiLoadAccess; -import org.hibernate.ObjectDeletedException; -import org.hibernate.ObjectNotFoundException; -import org.hibernate.ReadOnlyMode; -import org.hibernate.ReplicationMode; -import org.hibernate.Session; -import org.hibernate.SessionBuilder; -import org.hibernate.SessionEventListener; -import org.hibernate.SessionException; -import org.hibernate.SharedSessionBuilder; -import org.hibernate.SimpleNaturalIdLoadAccess; -import org.hibernate.Transaction; -import org.hibernate.TypeMismatchException; -import org.hibernate.UnknownProfileException; -import org.hibernate.UnresolvableObjectException; +import jakarta.persistence.metamodel.Metamodel; +import org.hibernate.*; import org.hibernate.action.spi.AfterTransactionCompletionProcess; import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; import org.hibernate.collection.spi.PersistentCollection; @@ -78,46 +40,10 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.event.service.spi.EventListenerGroups; -import org.hibernate.event.spi.AutoFlushEvent; -import org.hibernate.event.spi.AutoFlushEventListener; -import org.hibernate.event.spi.ClearEvent; -import org.hibernate.event.spi.ClearEventListener; -import org.hibernate.event.spi.DeleteContext; -import org.hibernate.event.spi.DeleteEvent; -import org.hibernate.event.spi.DeleteEventListener; -import org.hibernate.event.spi.DirtyCheckEvent; -import org.hibernate.event.spi.DirtyCheckEventListener; -import org.hibernate.event.spi.EventSource; -import org.hibernate.event.spi.EvictEvent; -import org.hibernate.event.spi.EvictEventListener; -import org.hibernate.event.spi.FlushEvent; -import org.hibernate.event.spi.FlushEventListener; -import org.hibernate.event.spi.InitializeCollectionEvent; -import org.hibernate.event.spi.InitializeCollectionEventListener; -import org.hibernate.event.spi.LoadEvent; -import org.hibernate.event.spi.LoadEventListener; -import org.hibernate.event.spi.LockEvent; -import org.hibernate.event.spi.LockEventListener; -import org.hibernate.event.spi.MergeContext; -import org.hibernate.event.spi.MergeEvent; -import org.hibernate.event.spi.MergeEventListener; -import org.hibernate.event.spi.PersistContext; -import org.hibernate.event.spi.PersistEvent; -import org.hibernate.event.spi.PersistEventListener; -import org.hibernate.event.spi.PostLoadEvent; -import org.hibernate.event.spi.PostLoadEventListener; -import org.hibernate.event.spi.RefreshContext; -import org.hibernate.event.spi.RefreshEvent; -import org.hibernate.event.spi.RefreshEventListener; -import org.hibernate.event.spi.ReplicateEvent; -import org.hibernate.event.spi.ReplicateEventListener; -import org.hibernate.loader.internal.CacheLoadHelper; -import org.hibernate.metamodel.model.domain.EntityDomainType; -import org.hibernate.metamodel.model.domain.ManagedDomainType; -import org.hibernate.resource.transaction.spi.TransactionObserver; -import org.hibernate.event.monitor.spi.EventMonitor; import org.hibernate.event.monitor.spi.DiagnosticEvent; +import org.hibernate.event.monitor.spi.EventMonitor; +import org.hibernate.event.service.spi.EventListenerGroups; +import org.hibernate.event.spi.*; import org.hibernate.event.spi.LoadEventListener.LoadType; import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.RootGraph; @@ -127,10 +53,13 @@ import org.hibernate.jpa.internal.util.ConfigurationHelper; import org.hibernate.jpa.internal.util.FlushModeTypeHelper; import org.hibernate.jpa.internal.util.LockModeTypeHelper; +import org.hibernate.loader.internal.CacheLoadHelper; import org.hibernate.loader.internal.IdentifierLoadAccessImpl; import org.hibernate.loader.internal.LoadAccessContext; import org.hibernate.loader.internal.NaturalIdLoadAccessImpl; import org.hibernate.loader.internal.SimpleNaturalIdLoadAccessImpl; +import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.metamodel.model.domain.ManagedDomainType; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.procedure.ProcedureCall; import org.hibernate.procedure.spi.NamedCallableQueryMemento; @@ -148,25 +77,32 @@ import org.hibernate.resource.jdbc.spi.StatementInspector; import org.hibernate.resource.transaction.spi.TransactionCoordinator; import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder; +import org.hibernate.resource.transaction.spi.TransactionObserver; import org.hibernate.resource.transaction.spi.TransactionStatus; import org.hibernate.stat.SessionStatistics; import org.hibernate.stat.internal.SessionStatisticsImpl; import org.hibernate.stat.spi.StatisticsImplementor; import org.hibernate.type.descriptor.WrapperOptions; -import jakarta.persistence.CacheRetrieveMode; -import jakarta.persistence.CacheStoreMode; -import jakarta.persistence.EntityGraph; -import jakarta.persistence.EntityNotFoundException; -import jakarta.persistence.FindOption; -import jakarta.persistence.FlushModeType; -import jakarta.persistence.LockModeType; -import jakarta.persistence.LockOption; -import jakarta.persistence.PersistenceException; -import jakarta.persistence.RefreshOption; -import jakarta.persistence.TransactionRequiredException; -import jakarta.persistence.criteria.CriteriaSelect; -import jakarta.persistence.metamodel.Metamodel; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Reader; +import java.io.Serial; +import java.io.Serializable; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.NClob; +import java.sql.SQLException; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TimeZone; +import java.util.function.UnaryOperator; import static java.lang.Boolean.parseBoolean; import static java.lang.Integer.parseInt; @@ -191,6 +127,7 @@ import static org.hibernate.event.spi.LoadEventListener.INTERNAL_LOAD_EAGER; import static org.hibernate.event.spi.LoadEventListener.INTERNAL_LOAD_LAZY; import static org.hibernate.event.spi.LoadEventListener.INTERNAL_LOAD_NULLABLE; +import static org.hibernate.internal.LockOptionsHelper.applyPropertiesToLockOptions; import static org.hibernate.jpa.HibernateHints.HINT_BATCH_FETCH_SIZE; import static org.hibernate.jpa.HibernateHints.HINT_ENABLE_SUBSELECT_FETCH; import static org.hibernate.jpa.HibernateHints.HINT_FETCH_PROFILE; @@ -202,7 +139,6 @@ import static org.hibernate.jpa.SpecHints.HINT_SPEC_LOCK_TIMEOUT; import static org.hibernate.jpa.SpecHints.HINT_SPEC_QUERY_TIMEOUT; import static org.hibernate.jpa.internal.util.CacheModeHelper.interpretCacheMode; -import static org.hibernate.internal.LockOptionsHelper.applyPropertiesToLockOptions; import static org.hibernate.jpa.internal.util.FlushModeTypeHelper.getFlushModeType; import static org.hibernate.pretty.MessageHelper.infoString; import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; @@ -694,6 +630,11 @@ public void lock(Object object, LockMode lockMode) { fireLock( new LockEvent( object, lockOptions, this ) ); } + @Override + public void lock(Object object, LockMode lockMode, LockOption... lockOptions) { + lock( object, buildLockOptions( lockMode, lockOptions ) ); + } + private void fireLock(LockEvent event) { checkOpen(); checkEntityManaged( event.getEntityName(), event.getObject() ); @@ -1238,11 +1179,6 @@ public T get(Class entityClass, Object id, LockMode lockMode) { return this.byId( entityClass ).with( new LockOptions( lockMode ) ).load( id ); } - @Override - public T get(Class entityClass, Object id, LockOptions lockOptions) { - return this.byId( entityClass ).with( lockOptions ).load( id ); - } - @Override public Object get(String entityName, Object id, LockMode lockMode) { return this.byId( entityName ).with( new LockOptions( lockMode ) ).load( id ); @@ -1359,13 +1295,6 @@ public void refresh(Object object) { fireRefresh( new RefreshEvent( object, this ) ); } - @Override - public void refresh(Object object, LockMode lockMode) { - final LockOptions lockOptions = copySessionLockOptions(); - lockOptions.setLockMode( lockMode ); - fireRefresh( new RefreshEvent( object, lockOptions, this ) ); - } - @Override public void refresh(Object object, LockOptions lockOptions) { fireRefresh( new RefreshEvent( object, lockOptions, this ) ); @@ -2478,20 +2407,6 @@ public T find(Class entityClass, Object primaryKey, LockModeType lockMode return find( entityClass, primaryKey, lockModeType, null ); } - @Override - public T find(Class entityType, Object id, LockMode lockMode) { - checkTransactionNeededForLock( lockMode ); - final LockOptions lockOptions = copySessionLockOptions(); - lockOptions.setLockMode( lockMode ); - return find( entityType, id, lockOptions, null ); - } - - @Override - public T find(Class entityType, Object id, LockOptions lockOptions) { - checkTransactionNeededForLock( lockOptions.getLockMode() ); - return find( entityType, id, lockOptions, null ); - } - @Override public T find(Class entityClass, Object primaryKey, LockModeType lockModeType, Map properties) { checkOpen(); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/BaseNaturalIdLoadAccessImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/BaseNaturalIdLoadAccessImpl.java index cca75ba55e74..7b2e2cf9c3da 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/BaseNaturalIdLoadAccessImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/BaseNaturalIdLoadAccessImpl.java @@ -9,8 +9,11 @@ import jakarta.persistence.EntityGraph; +import jakarta.persistence.PessimisticLockScope; +import jakarta.persistence.Timeout; import org.hibernate.HibernateException; import org.hibernate.IdentifierLoadAccess; +import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.UnknownProfileException; import org.hibernate.engine.spi.EffectiveEntityGraph; @@ -64,6 +67,24 @@ public LockOptions getLockOptions() { return lockOptions; } + protected Object with(LockMode lockMode, PessimisticLockScope lockScope) { + if ( lockOptions == null ) { + lockOptions = new LockOptions(); + } + lockOptions.setLockMode( lockMode ); + lockOptions.setLockScope( lockScope ); + return this; + } + + + protected Object with(Timeout timeout) { + if ( lockOptions == null ) { + lockOptions = new LockOptions(); + } + lockOptions.setTimeOut( timeout.milliseconds() ); + return this; + } + public Object with(EntityGraph graph, GraphSemantic semantic) { this.rootGraph = (RootGraphImplementor) graph; this.graphSemantic = semantic; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/IdentifierLoadAccessImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/IdentifierLoadAccessImpl.java index 81270f40ce68..e2b059274526 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/IdentifierLoadAccessImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/IdentifierLoadAccessImpl.java @@ -11,8 +11,11 @@ import jakarta.persistence.EntityGraph; +import jakarta.persistence.PessimisticLockScope; +import jakarta.persistence.Timeout; import org.hibernate.CacheMode; import org.hibernate.IdentifierLoadAccess; +import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.ObjectNotFoundException; import org.hibernate.UnknownProfileException; @@ -63,6 +66,25 @@ public final IdentifierLoadAccessImpl with(LockOptions lockOptions) { return this; } + @Override + public IdentifierLoadAccess with(LockMode lockMode, PessimisticLockScope lockScope) { + if ( lockOptions == null ) { + lockOptions = new LockOptions(); + } + lockOptions.setLockMode( lockMode ); + lockOptions.setLockScope( lockScope ); + return this; + } + + @Override + public IdentifierLoadAccess with(Timeout timeout) { + if ( lockOptions == null ) { + lockOptions = new LockOptions(); + } + lockOptions.setTimeOut( timeout.milliseconds() ); + return this; + } + @Override public IdentifierLoadAccess with(CacheMode cacheMode) { this.cacheMode = cacheMode; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/NaturalIdLoadAccessImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/NaturalIdLoadAccessImpl.java index af719fb65031..f18fee5dec69 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/NaturalIdLoadAccessImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/NaturalIdLoadAccessImpl.java @@ -4,18 +4,20 @@ */ package org.hibernate.loader.internal; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Optional; - import jakarta.persistence.EntityGraph; +import jakarta.persistence.PessimisticLockScope; +import jakarta.persistence.Timeout; import jakarta.persistence.metamodel.SingularAttribute; - +import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.NaturalIdLoadAccess; import org.hibernate.graph.GraphSemantic; import org.hibernate.metamodel.mapping.EntityMappingType; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; + /** * @author Steve Ebersole */ @@ -26,6 +28,18 @@ public NaturalIdLoadAccessImpl(LoadAccessContext context, EntityMappingType enti super( context, entityDescriptor ); } + @Override + public NaturalIdLoadAccess with(LockMode lockMode, PessimisticLockScope lockScope) { + //noinspection unchecked + return (NaturalIdLoadAccess) super.with( lockMode, lockScope ); + } + + @Override + public NaturalIdLoadAccess with(Timeout timeout) { + //noinspection unchecked + return (NaturalIdLoadAccess) super.with( timeout ); + } + @Override public NaturalIdLoadAccessImpl with(LockOptions lockOptions) { return (NaturalIdLoadAccessImpl) super.with( lockOptions ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/SimpleNaturalIdLoadAccessImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/SimpleNaturalIdLoadAccessImpl.java index 0c3e5b62a078..4f3f3016b5a7 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/SimpleNaturalIdLoadAccessImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/SimpleNaturalIdLoadAccessImpl.java @@ -11,7 +11,10 @@ import jakarta.persistence.EntityGraph; +import jakarta.persistence.PessimisticLockScope; +import jakarta.persistence.Timeout; import org.hibernate.HibernateException; +import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.SimpleNaturalIdLoadAccess; import org.hibernate.graph.GraphSemantic; @@ -52,6 +55,18 @@ public boolean isSynchronizationEnabled() { return super.isSynchronizationEnabled(); } + @Override + public SimpleNaturalIdLoadAccess with(LockMode lockMode, PessimisticLockScope lockScope) { + //noinspection unchecked + return (SimpleNaturalIdLoadAccess) super.with( lockMode, lockScope ); + } + + @Override + public SimpleNaturalIdLoadAccess with(Timeout timeout) { + //noinspection unchecked + return (SimpleNaturalIdLoadAccess) super.with( timeout ); + } + @Override public final SimpleNaturalIdLoadAccessImpl with(LockOptions lockOptions) { return (SimpleNaturalIdLoadAccessImpl) super.with( lockOptions ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/CommonQueryContract.java b/hibernate-core/src/main/java/org/hibernate/query/CommonQueryContract.java index 9955da65281f..47c5e16d516f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/CommonQueryContract.java +++ b/hibernate-core/src/main/java/org/hibernate/query/CommonQueryContract.java @@ -10,6 +10,7 @@ import java.util.Date; import java.util.Map; +import jakarta.persistence.Timeout; import org.hibernate.FlushMode; import org.hibernate.Session; @@ -155,7 +156,9 @@ public interface CommonQueryContract { * Any value set here is eventually passed directly along to the * {@linkplain java.sql.Statement#setQueryTimeout(int) JDBC * statement}, which expressly disallows negative values. So - * negative values should be avoided as a general rule. + * negative values should be avoided as a general rule, + * although certain "magic values" are handled - see + * {@linkplain org.hibernate.Timeouts#NO_WAIT}. *

    * A value of zero indicates no timeout. * @@ -163,10 +166,21 @@ public interface CommonQueryContract { * * @return {@code this}, for method chaining * + * @see org.hibernate.Timeouts + * @see #setTimeout(Timeout) * @see #getTimeout() */ CommonQueryContract setTimeout(int timeout); + /** + * Apply a timeout to the corresponding database query. + * + * @param timeout The timeout to apply + * + * @return {@code this}, for method chaining + */ + CommonQueryContract setTimeout(Timeout timeout); + /** * Get the comment that has been set for this query, if any. */ diff --git a/hibernate-core/src/main/java/org/hibernate/query/NativeQuery.java b/hibernate-core/src/main/java/org/hibernate/query/NativeQuery.java index 2052326502a8..e485354f7228 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/NativeQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/NativeQuery.java @@ -10,7 +10,9 @@ import jakarta.persistence.FlushModeType; import jakarta.persistence.LockModeType; import jakarta.persistence.Parameter; +import jakarta.persistence.PessimisticLockScope; import jakarta.persistence.TemporalType; +import jakarta.persistence.Timeout; import jakarta.persistence.metamodel.SingularAttribute; import org.hibernate.CacheMode; @@ -600,6 +602,21 @@ interface FetchReturn extends ResultNode { @Override NativeQuery setReadOnly(boolean readOnly); + @Override + NativeQuery setComment(String comment); + + @Override + NativeQuery addQueryHint(String hint); + + @Override + NativeQuery setMaxResults(int maxResults); + + @Override + NativeQuery setFirstResult(int startPosition); + + @Override + NativeQuery setHint(String hintName, Object value); + /** * @inheritDoc * @@ -622,29 +639,6 @@ interface FetchReturn extends ResultNode { @Override NativeQuery setLockOptions(LockOptions lockOptions); - /** - * Not applicable to native SQL queries. - * - * @throws IllegalStateException for consistency with JPA - */ - @Override - NativeQuery setLockMode(String alias, LockMode lockMode); - - @Override - NativeQuery setComment(String comment); - - @Override - NativeQuery addQueryHint(String hint); - - @Override - NativeQuery setMaxResults(int maxResults); - - @Override - NativeQuery setFirstResult(int startPosition); - - @Override - NativeQuery setHint(String hintName, Object value); - /** * Not applicable to native SQL queries, due to an unfortunate * requirement of the JPA specification. @@ -691,6 +685,32 @@ interface FetchReturn extends ResultNode { @Override NativeQuery setHibernateLockMode(LockMode lockMode); + /** + * Apply a timeout to the corresponding database query. + * + * @param timeout The timeout to apply + * + * @return {@code this}, for method chaining + */ + NativeQuery setTimeout(Timeout timeout); + + /** + * Apply a scope to any pessimistic locking applied to the query. + * + * @param lockScope The lock scope to apply + * + * @return {@code this}, for method chaining + */ + NativeQuery setLockScope(PessimisticLockScope lockScope); + + /** + * Not applicable to native SQL queries. + * + * @throws IllegalStateException for consistency with JPA + */ + @Override + NativeQuery setLockMode(String alias, LockMode lockMode); + @Override NativeQuery setTupleTransformer(TupleTransformer transformer); diff --git a/hibernate-core/src/main/java/org/hibernate/query/Query.java b/hibernate-core/src/main/java/org/hibernate/query/Query.java index 4d4926ffcb50..f25af16d5ac7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/Query.java +++ b/hibernate-core/src/main/java/org/hibernate/query/Query.java @@ -14,6 +14,8 @@ import java.util.stream.Stream; import jakarta.persistence.EntityGraph; +import jakarta.persistence.PessimisticLockScope; +import jakarta.persistence.Timeout; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.Incubating; @@ -357,7 +359,10 @@ default Query applyLoadGraph(@SuppressWarnings("rawtypes") RootGraph graph) { * @return The {@link LockOptions} currently in effect * * @see LockOptions + * + * @deprecated To be removed with no replacement - this is an SPI/internal concern. */ + @Deprecated(since = "7.0", forRemoval = true) @Override LockOptions getLockOptions(); @@ -375,7 +380,12 @@ default Query applyLoadGraph(@SuppressWarnings("rawtypes") RootGraph graph) { * @return {@code this}, for method chaining * * @see #getLockOptions() + * + * @deprecated Use one of {@linkplain #setLockMode(LockModeType)}, + * {@linkplain #setHibernateLockMode}, {@linkplain #setLockScope} + * and/or {@linkplain #setTimeout} instead. */ + @Deprecated(since = "7.0", forRemoval = true) Query setLockOptions(LockOptions lockOptions); /** @@ -399,6 +409,24 @@ default Query applyLoadGraph(@SuppressWarnings("rawtypes") RootGraph graph) { @Override Query setLockMode(String alias, LockMode lockMode); + /** + * Apply a timeout to the corresponding database query. + * + * @param timeout The timeout to apply + * + * @return {@code this}, for method chaining + */ + Query setTimeout(Timeout timeout); + + /** + * Apply a scope to any pessimistic locking applied to the query. + * + * @param lockScope The lock scope to apply + * + * @return {@code this}, for method chaining + */ + Query setLockScope(PessimisticLockScope lockScope); + /** * Set a {@link TupleTransformer}. */ diff --git a/hibernate-core/src/main/java/org/hibernate/query/SelectionQuery.java b/hibernate-core/src/main/java/org/hibernate/query/SelectionQuery.java index eda176efed59..9c237b58af20 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/SelectionQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/SelectionQuery.java @@ -16,7 +16,7 @@ import jakarta.persistence.CacheRetrieveMode; import jakarta.persistence.CacheStoreMode; import jakarta.persistence.EntityGraph; - +import jakarta.persistence.PessimisticLockScope; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.Incubating; @@ -588,6 +588,15 @@ default Stream stream() { */ SelectionQuery setHibernateLockMode(LockMode lockMode); + /** + * Apply a scope to any pessimistic locking applied to the query. + * + * @param lockScope The lock scope to apply + * + * @return {@code this}, for method chaining + */ + SelectionQuery setLockScope(PessimisticLockScope lockScope); + /** * Specify a {@link LockMode} to apply to a specific alias defined in the query */ diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQuery.java b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQuery.java index 09b8b2b7fb64..5f31ce187120 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQuery.java @@ -21,6 +21,7 @@ import jakarta.persistence.PessimisticLockScope; import jakarta.persistence.TemporalType; +import jakarta.persistence.Timeout; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.query.QueryFlushMode; @@ -262,18 +263,18 @@ public LockOptions getLockOptions() { return getQueryOptions().getLockOptions(); } - @Override - public LockModeType getLockMode() { - getSession().checkOpen( false ); - return super.getLockMode(); - } - @Override public QueryImplementor setLockOptions(LockOptions lockOptions) { getQueryOptions().getLockOptions().overlay( lockOptions ); return this; } + @Override + public LockModeType getLockMode() { + getSession().checkOpen( false ); + return super.getLockMode(); + } + @Override public QueryImplementor setLockMode(String alias, LockMode lockMode) { super.setLockMode( alias, lockMode ); @@ -287,6 +288,20 @@ public QueryImplementor setLockMode(LockModeType lockModeType) { return this; } + @Override + public QueryImplementor setLockScope(PessimisticLockScope lockScope) { + getSession().checkOpen(); + super.setLockScope( lockScope ); + return this; + } + + @Override + public QueryImplementor setTimeout(Timeout timeout) { + getSession().checkOpen(); + super.setTimeout( timeout ); + return this; + } + @Override public String getComment() { return super.getComment(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractSelectionQuery.java b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractSelectionQuery.java index d30723c29bf7..f7f60e8ce133 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractSelectionQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractSelectionQuery.java @@ -16,6 +16,8 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; +import jakarta.persistence.PessimisticLockScope; +import jakarta.persistence.Timeout; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.ScrollableResults; @@ -440,6 +442,18 @@ public SelectionQuery setHibernateLockMode(LockMode lockMode) { return this; } + @Override + public SelectionQuery setTimeout(Timeout timeout) { + getLockOptions().setTimeOut( timeout.milliseconds() ); + return this; + } + + @Override + public SelectionQuery setLockScope(PessimisticLockScope lockScope) { + getLockOptions().setLockScope( lockScope ); + return this; + } + /** * Specifies whether follow-on locking should be applied? */ diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java index 935d4f4e3ccd..1a2f8ddb79d1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java @@ -18,6 +18,8 @@ import java.util.function.Consumer; import java.util.function.Supplier; +import jakarta.persistence.PessimisticLockScope; +import jakarta.persistence.Timeout; import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.CacheMode; import org.hibernate.FlushMode; @@ -582,6 +584,18 @@ public NativeQueryImplementor setHibernateLockMode(LockMode lockMode) { return this; } + @Override + public NativeQueryImplementor setTimeout(Timeout timeout) { + super.setTimeout( timeout ); + return this; + } + + @Override + public NativeQueryImplementor setLockScope(PessimisticLockScope lockScope) { + super.setLockScope( lockScope ); + return this; + } + @Override public NativeQueryImplementor setLockMode(String alias, LockMode lockMode) { // throw IllegalStateException here for consistency with JPA diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java index 772cea5d4298..8ac378f23dbe 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java @@ -11,7 +11,9 @@ import jakarta.persistence.LockModeType; import jakarta.persistence.Parameter; import jakarta.persistence.PersistenceException; +import jakarta.persistence.PessimisticLockScope; import jakarta.persistence.TemporalType; +import jakarta.persistence.Timeout; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.HibernateException; @@ -779,6 +781,20 @@ public LockModeType getLockMode() { return getLockOptions().getLockMode().toJpaLockMode(); } + @Override + public SqmQueryImplementor setLockScope(PessimisticLockScope lockScope) { + getSession().checkOpen( false ); + getQueryOptions().getLockOptions().setLockScope( lockScope ); + return this; + } + + @Override + public SqmQueryImplementor setTimeout(Timeout timeout) { + getSession().checkOpen( false ); + getQueryOptions().getLockOptions().setTimeOut( timeout.milliseconds() ); + return this; + } + @Override public Query setPage(Page page) { super.setPage(page); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java index 65eb65212cae..922bc968a46e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java @@ -18,8 +18,10 @@ import jakarta.persistence.FlushModeType; import jakarta.persistence.LockModeType; import jakarta.persistence.Parameter; +import jakarta.persistence.PessimisticLockScope; import jakarta.persistence.TemporalType; +import jakarta.persistence.Timeout; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.query.QueryFlushMode; @@ -35,6 +37,7 @@ import org.hibernate.query.Page; import org.hibernate.query.QueryParameter; import org.hibernate.query.ResultListTransformer; +import org.hibernate.query.SelectionQuery; import org.hibernate.query.TupleTransformer; import org.hibernate.query.criteria.internal.NamedCriteriaQueryMementoImpl; import org.hibernate.query.hql.internal.NamedHqlQueryMementoImpl; @@ -567,6 +570,18 @@ public SqmSelectionQuery setHibernateLockMode(LockMode lockMode) { return this; } + @Override + public SelectionQuery setTimeout(Timeout timeout) { + super.setTimeout( timeout ); + return this; + } + + @Override + public SelectionQuery setLockScope(PessimisticLockScope lockScope) { + super.setLockScope( lockScope ); + return this; + } + /** * Specify a {@link LockMode} to apply to a specific alias defined in the query */ diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/DelegatingSqmSelectionQueryImplementor.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/DelegatingSqmSelectionQueryImplementor.java index 1c706d3413ee..b123aafeefa0 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/DelegatingSqmSelectionQueryImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/DelegatingSqmSelectionQueryImplementor.java @@ -13,6 +13,8 @@ import java.util.Optional; import java.util.stream.Stream; +import jakarta.persistence.PessimisticLockScope; +import jakarta.persistence.Timeout; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.Incubating; @@ -288,6 +290,18 @@ public SqmSelectionQueryImplementor setHibernateLockMode(LockMode lockMode) { return this; } + @Override + public SqmSelectionQueryImplementor setTimeout(Timeout timeout) { + getDelegate().setTimeout( timeout ); + return this; + } + + @Override + public SqmSelectionQueryImplementor setLockScope(PessimisticLockScope lockScope) { + getDelegate().setLockScope( lockScope ); + return this; + } + @Override public SqmSelectionQueryImplementor setLockMode(String alias, LockMode lockMode) { getDelegate().setLockMode( alias, lockMode ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockExceptionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockExceptionTests.java index 88eae5739f81..87628a73d76d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockExceptionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockExceptionTests.java @@ -4,30 +4,27 @@ */ package org.hibernate.orm.test.jpa.lock; -import java.util.Collections; - -import org.hibernate.LockOptions; +import jakarta.persistence.LockModeType; +import jakarta.persistence.LockTimeoutException; +import jakarta.persistence.PessimisticLockException; +import org.hibernate.Timeouts; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.SQLServerDialect; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.exception.LockAcquisitionException; import org.hibernate.orm.test.jpa.model.AbstractJPATest; import org.hibernate.orm.test.jpa.model.Item; - -import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.jdbc.SQLServerSnapshotIsolationConnectionProvider; import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.testing.orm.junit.SkipForDialect; import org.hibernate.testing.transaction.TransactionUtil2; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; -import jakarta.persistence.LockModeType; -import jakarta.persistence.LockTimeoutException; -import jakarta.persistence.PessimisticLockException; - import static org.junit.jupiter.api.Assertions.fail; /** @@ -76,11 +73,11 @@ public void testLockTimeoutFind() { Item.class, item.getId(), LockModeType.PESSIMISTIC_WRITE, - Collections.singletonMap( AvailableSettings.JAKARTA_LOCK_TIMEOUT, LockOptions.NO_WAIT ) + Timeouts.NO_WAIT ); fail( "Expecting a failure" ); } - catch ( LockTimeoutException | PessimisticLockException expected ) { + catch (LockTimeoutException | PessimisticLockException | LockAcquisitionException expected ) { // expected outcome } } @@ -118,7 +115,7 @@ public void testLockTimeoutRefresh() { secondSession.refresh( item2, LockModeType.PESSIMISTIC_WRITE, - Collections.singletonMap( AvailableSettings.JAKARTA_LOCK_TIMEOUT, LockOptions.NO_WAIT ) + Timeouts.NO_WAIT ); fail( "Expecting a failure" ); } @@ -156,11 +153,11 @@ public void testLockTimeoutLock() { secondSession -> { try { // generally speaking we should be able to read the row - Item item2 = secondSession.get( Item.class, item.getId() ); + Item item2 = secondSession.find( Item.class, item.getId() ); secondSession.lock( item2, LockModeType.PESSIMISTIC_WRITE, - Collections.singletonMap( AvailableSettings.JAKARTA_LOCK_TIMEOUT, LockOptions.NO_WAIT ) + Timeouts.NO_WAIT ); fail( "Expecting a failure" ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockTest.java index 3710d83115e3..b2a978d3c4ad 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockTest.java @@ -16,7 +16,6 @@ import jakarta.persistence.Timeout; import org.hibernate.Hibernate; import org.hibernate.HibernateException; -import org.hibernate.LockOptions; import org.hibernate.Session; import org.hibernate.TransactionException; import org.hibernate.cfg.AvailableSettings; @@ -304,9 +303,7 @@ public void testUpdateWithPessimisticReadLockSkipLocked() { ); doInJPA( this::entityManagerFactory, _entityManagaer -> { - Map properties = new HashMap<>(); - properties.put( AvailableSettings.JAKARTA_LOCK_TIMEOUT, LockOptions.SKIP_LOCKED ); - _entityManagaer.find( Lock.class, lock.getId(), LockModeType.PESSIMISTIC_READ, properties ); + _entityManagaer.find( Lock.class, lock.getId(), LockModeType.PESSIMISTIC_READ, Timeout.milliseconds( 0 ) ); try { doInJPA( this::entityManagerFactory, entityManager -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/loading/multiLoad/MultiLoadLockingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/loading/multiLoad/MultiLoadLockingTest.java index ecc0a16cd8a9..ad5b9a968af3 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/loading/multiLoad/MultiLoadLockingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/loading/multiLoad/MultiLoadLockingTest.java @@ -4,19 +4,13 @@ */ package org.hibernate.orm.test.loading.multiLoad; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.Serializable; -import java.util.List; -import java.util.function.Function; - +import jakarta.persistence.Basic; import jakarta.persistence.Embeddable; import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; import jakarta.persistence.Version; import org.hibernate.LockMode; -import org.hibernate.LockOptions; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.annotations.NaturalId; @@ -37,11 +31,16 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import jakarta.persistence.Basic; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; +import java.io.Serializable; +import java.util.List; +import java.util.function.Function; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +@SuppressWarnings("JUnitMalformedDeclaration") @DomainModel( annotatedClasses = { MultiLoadLockingTest.Customer.class, @@ -146,7 +145,7 @@ public void prepareTestDataAndClearL2C(SessionFactoryScope scope) { @AfterEach public void tearDown(SessionFactoryScope scope) { - scope.inTransaction( session -> scope.getSessionFactory().getSchemaManager().truncate() ); + scope.dropData(); scope.getSessionFactory().getCache().evictAll(); } @@ -154,13 +153,15 @@ public void tearDown(SessionFactoryScope scope) { // (1) simple Id entity w/ pessimistic read lock @Test void testMultiLoadSimpleIdEntityPessimisticReadLock(SessionFactoryScope scope) { - final LockOptions lockOptions = new LockOptions(LockMode.PESSIMISTIC_READ); - final String lockString = scope.getSessionFactory().getJdbcServices().getDialect().getForUpdateString(lockOptions); + final String lockString = scope.getSessionFactory() + .getJdbcServices() + .getDialect() + .getForUpdateString( LockMode.PESSIMISTIC_READ, -1 ); // test byMultipleIds scope.inTransaction( session -> { List customersLoaded = session.byMultipleIds(Customer.class) - .with( lockOptions ) + .with( LockMode.PESSIMISTIC_READ ) .multiLoad(customerIdsAsLongs); assertNotNull(customersLoaded); assertEquals(customerList.size(), customersLoaded.size()); @@ -178,7 +179,7 @@ void testMultiLoadSimpleIdEntityPessimisticReadLock(SessionFactoryScope scope) { // test byMultipleNaturalId scope.inTransaction( session -> { List customersLoaded = session.byMultipleNaturalId(Customer.class) - .with( lockOptions ) + .with( LockMode.PESSIMISTIC_READ ) .multiLoad( customerNaturalIdsAsObjects ); assertNotNull(customersLoaded); assertEquals(customerList.size(), customersLoaded.size()); @@ -190,8 +191,10 @@ void testMultiLoadSimpleIdEntityPessimisticReadLock(SessionFactoryScope scope) { // (2) composite Id entity w/ pessimistic read lock (one of the entities already in L1C) @Test void testMultiLoadCompositeIdEntityPessimisticReadLockAlreadyInSession(SessionFactoryScope scope) { - final LockOptions lockOptions = new LockOptions(LockMode.PESSIMISTIC_READ); - final String lockString = scope.getSessionFactory().getJdbcServices().getDialect().getForUpdateString(lockOptions); + final String lockString = scope.getSessionFactory() + .getJdbcServices() + .getDialect() + .getForUpdateString( LockMode.PESSIMISTIC_READ, -1 ); scope.inTransaction( session -> { EntityWithAggregateId entityInL1C = session @@ -201,7 +204,7 @@ void testMultiLoadCompositeIdEntityPessimisticReadLockAlreadyInSession(SessionFa // test byMultipleIds List entitiesLoaded = session.byMultipleIds(EntityWithAggregateId.class) - .with( lockOptions ) + .with( LockMode.PESSIMISTIC_READ ) .multiLoad(entityWithAggregateIdKeys); assertNotNull(entitiesLoaded); assertEquals(entityWithAggregateIdList.size(), entitiesLoaded.size()); @@ -229,8 +232,9 @@ void testMultiLoadCompositeIdEntityPessimisticReadLockAlreadyInSession(SessionFa assertNotNull(entityInL1C); sqlStatementInspector.clear(); - List entitiesLoaded = session.byMultipleNaturalId(EntityWithAggregateId.class) - .with( lockOptions ) + List entitiesLoaded = session + .byMultipleNaturalId( EntityWithAggregateId.class ) + .with( LockMode.PESSIMISTIC_READ ) .multiLoad( entityWithAggregateIdNaturalIdsAsObjects ); assertNotNull(entitiesLoaded); assertEquals(entityWithAggregateIdList.size(), entitiesLoaded.size()); @@ -245,7 +249,6 @@ void testMultiLoadCompositeIdEntityPessimisticReadLockAlreadyInSession(SessionFa public void testMultiLoadSimpleIdEntityPessimisticWriteLockSomeInL1CAndSomeInL2C(SessionFactoryScope scope) { final Integer userInL2CId = userIds.get(0); final Integer userInL1CId = userIds.get(1); - final LockOptions lockOptions = new LockOptions(LockMode.PESSIMISTIC_WRITE); Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect(); String lockString; if ( PostgreSQLDialect.class.isAssignableFrom( dialect.getClass() ) ) { @@ -253,7 +256,7 @@ public void testMultiLoadSimpleIdEntityPessimisticWriteLockSomeInL1CAndSomeInL2C lockString = translator.getForUpdate(); } else { - lockString = scope.getSessionFactory().getJdbcServices().getDialect().getForUpdateString(lockOptions); + lockString = dialect.getForUpdateString( LockMode.PESSIMISTIC_WRITE, -1 ); } scope.inTransaction( session -> { @@ -268,7 +271,7 @@ public void testMultiLoadSimpleIdEntityPessimisticWriteLockSomeInL1CAndSomeInL2C sqlStatementInspector.clear(); List usersLoaded = session.byMultipleIds(User.class) - .with( lockOptions ) + .with( LockMode.PESSIMISTIC_WRITE ) .multiLoad(userIds); assertNotNull(usersLoaded); assertEquals(userList.size(), usersLoaded.size()); @@ -294,7 +297,7 @@ public void testMultiLoadSimpleIdEntityPessimisticWriteLockSomeInL1CAndSomeInL2C sqlStatementInspector.clear(); List usersLoaded = session.byMultipleNaturalId(User.class) - .with( lockOptions ) + .with( LockMode.PESSIMISTIC_WRITE ) .multiLoad( userNaturalIdsAsObjects ); assertNotNull(usersLoaded); assertEquals(userList.size(), usersLoaded.size()); @@ -310,7 +313,7 @@ void testMultiLoadSimpleIdEntityOptimisticReadLock(SessionFactoryScope scope) { // test byMultipleIds scope.inTransaction( session -> { List usersLoaded = session.byMultipleIds(User.class) - .with(new LockOptions(LockMode.OPTIMISTIC)) + .with( LockMode.OPTIMISTIC ) .multiLoad(userIds); assertNotNull(usersLoaded); assertEquals(userList.size(), usersLoaded.size()); @@ -326,7 +329,7 @@ void testMultiLoadSimpleIdEntityOptimisticReadLock(SessionFactoryScope scope) { // test byMultipleNaturalId scope.inTransaction( session -> { List usersLoaded = session.byMultipleNaturalId(User.class) - .with( new LockOptions(LockMode.OPTIMISTIC) ) + .with( LockMode.OPTIMISTIC ) .multiLoad( userNaturalIdsAsObjects ); assertNotNull(usersLoaded); assertEquals(userList.size(), usersLoaded.size()); @@ -341,7 +344,7 @@ void testMultiLoadSimpleIdEntityOptimisticForceIncrementLock(SessionFactoryScope // test byMultipleIds scope.inTransaction( session -> { List usersLoaded = session.byMultipleIds(User.class) - .with(new LockOptions(LockMode.OPTIMISTIC_FORCE_INCREMENT)) + .with( LockMode.OPTIMISTIC_FORCE_INCREMENT ) .multiLoad(userIds); assertNotNull(usersLoaded); assertEquals(userList.size(), usersLoaded.size()); @@ -357,7 +360,7 @@ void testMultiLoadSimpleIdEntityOptimisticForceIncrementLock(SessionFactoryScope // test byMultipleNaturalId scope.inTransaction( session -> { List usersLoaded = session.byMultipleNaturalId(User.class) - .with( new LockOptions(LockMode.OPTIMISTIC_FORCE_INCREMENT) ) + .with( LockMode.OPTIMISTIC_FORCE_INCREMENT ) .multiLoad( userNaturalIdsAsObjects ); assertNotNull(usersLoaded); assertEquals(userList.size(), usersLoaded.size()); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/loading/multiLoad/MultiLoadSecondLlvCacheTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/loading/multiLoad/MultiLoadSecondLlvCacheTest.java index 67f5e06ba080..ea1ce12916e6 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/loading/multiLoad/MultiLoadSecondLlvCacheTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/loading/multiLoad/MultiLoadSecondLlvCacheTest.java @@ -4,21 +4,19 @@ */ package org.hibernate.orm.test.loading.multiLoad; -import java.util.List; - +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.SharedCacheMode; import org.hibernate.CacheMode; -import org.hibernate.LockOptions; +import org.hibernate.LockMode; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; import org.hibernate.stat.Statistics; - import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; -import jakarta.persistence.Basic; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.SharedCacheMode; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -56,7 +54,7 @@ public void test() { inSession( session -> { List events = session.byMultipleIds( Event.class ) .with( CacheMode.NORMAL ) - .with( LockOptions.NONE ) + .with( LockMode.NONE ) .multiLoad( 1, 2, 3 ); // check all elements are not null diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/lob/BlobLocatorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/BlobLocatorTest.java index dbef3655eaa2..3e8f7cea13fa 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/lob/BlobLocatorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/BlobLocatorTest.java @@ -4,19 +4,18 @@ */ package org.hibernate.orm.test.lob; -import java.sql.Blob; -import java.util.Arrays; - -import org.hibernate.LockOptions; +import junit.framework.AssertionFailedError; +import org.hibernate.LockMode; import org.hibernate.Session; import org.hibernate.dialect.SybaseDialect; - import org.hibernate.testing.DialectChecks; import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Assert; import org.junit.Test; -import junit.framework.AssertionFailedError; + +import java.sql.Blob; +import java.util.Arrays; import static org.junit.Assert.assertNotNull; @@ -70,7 +69,7 @@ public void testBoundedBlobLocatorAccess() throws Throwable { if ( getDialect().supportsLobValueChangePropagation() ) { s = openSession(); s.beginTransaction(); - entity = ( LobHolder ) s.byId( LobHolder.class ).with( LockOptions.UPGRADE ).load( entity.getId() ); + entity = s.byId( LobHolder.class ).with( LockMode.PESSIMISTIC_WRITE ).load( entity.getId() ); entity.getBlobLocator().truncate( 1 ); entity.getBlobLocator().setBytes( 1, changed ); s.getTransaction().commit(); @@ -78,7 +77,7 @@ public void testBoundedBlobLocatorAccess() throws Throwable { s = openSession(); s.beginTransaction(); - entity = ( LobHolder ) s.byId( LobHolder.class ).with( LockOptions.UPGRADE ).load( entity.getId() ); + entity = s.byId( LobHolder.class ).with( LockMode.PESSIMISTIC_WRITE ).load( entity.getId() ); assertNotNull( entity.getBlobLocator() ); Assert.assertEquals( BLOB_SIZE, entity.getBlobLocator().length() ); assertEquals( changed, extractData( entity.getBlobLocator() ) ); @@ -91,7 +90,7 @@ public void testBoundedBlobLocatorAccess() throws Throwable { // test mutation via supplying a new clob locator instance... s = openSession(); s.beginTransaction(); - entity = ( LobHolder ) s.byId( LobHolder.class ).with( LockOptions.UPGRADE ).load( entity.getId() ); + entity = s.byId( LobHolder.class ).with( LockMode.PESSIMISTIC_WRITE ).load( entity.getId() ); assertNotNull( entity.getBlobLocator() ); Assert.assertEquals( BLOB_SIZE, entity.getBlobLocator().length() ); assertEquals( original, extractData( entity.getBlobLocator() ) ); @@ -102,7 +101,7 @@ public void testBoundedBlobLocatorAccess() throws Throwable { // test empty blob s = openSession(); s.beginTransaction(); - entity = s.get( LobHolder.class, entity.getId() ); + entity = s.find( LobHolder.class, entity.getId() ); Assert.assertEquals( BLOB_SIZE, entity.getBlobLocator().length() ); assertEquals( changed, extractData( entity.getBlobLocator() ) ); entity.setBlobLocator( s.getLobHelper().createBlob( empty ) ); @@ -111,7 +110,7 @@ public void testBoundedBlobLocatorAccess() throws Throwable { s = openSession(); s.beginTransaction(); - entity = s.get( LobHolder.class, entity.getId() ); + entity = s.find( LobHolder.class, entity.getId() ); // Seems ASE does not support empty BLOBs if ( entity.getBlobLocator() != null && !(sessionFactory().getJdbcServices().getDialect() instanceof SybaseDialect) ) { Assert.assertEquals( empty.length, entity.getBlobLocator().length() ); @@ -147,7 +146,7 @@ public void testUnboundedBlobLocatorAccess() throws Throwable { // at that point it is unbounded... s = openSession(); s.beginTransaction(); - entity = s.get( LobHolder.class, entity.getId() ); + entity = s.find( LobHolder.class, entity.getId() ); s.getTransaction().commit(); s.close(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/lob/ClobLocatorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/ClobLocatorTest.java index 21f609c40233..8b1a1ab6911c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/lob/ClobLocatorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/ClobLocatorTest.java @@ -4,19 +4,18 @@ */ package org.hibernate.orm.test.lob; -import java.sql.Clob; - -import org.hibernate.LockOptions; +import org.hibernate.LockMode; import org.hibernate.dialect.SybaseASEDialect; -import org.hibernate.type.descriptor.java.DataHelper; - import org.hibernate.testing.orm.junit.DialectFeatureChecks; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.type.descriptor.java.DataHelper; import org.junit.jupiter.api.Test; +import java.sql.Clob; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; @@ -82,7 +81,7 @@ public void testBoundedClobLocatorAccess(SessionFactoryScope scope) throws Throw session -> { try { LobHolder entity = session.byId( LobHolder.class ) - .with( LockOptions.UPGRADE ) + .with( LockMode.PESSIMISTIC_WRITE ) .load( id ); entity.getClobLocator().truncate( 1 ); entity.getClobLocator().setString( 1, changed ); @@ -97,7 +96,7 @@ public void testBoundedClobLocatorAccess(SessionFactoryScope scope) throws Throw session -> { try { LobHolder entity = session.byId( LobHolder.class ) - .with( LockOptions.UPGRADE ) + .with( LockMode.PESSIMISTIC_WRITE ) .load( id ); assertNotNull( entity.getClobLocator() ); @@ -118,7 +117,7 @@ public void testBoundedClobLocatorAccess(SessionFactoryScope scope) throws Throw scope.inTransaction( session -> { try { - LobHolder entity = session.byId( LobHolder.class ).with( LockOptions.UPGRADE ).load( id ); + LobHolder entity = session.find( LobHolder.class, id, LockMode.PESSIMISTIC_WRITE ); assertNotNull( entity.getClobLocator() ); assertEquals( CLOB_SIZE, entity.getClobLocator().length() ); assertEquals( original, extractData( entity.getClobLocator() ) ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/locking/AbstractSkipLockedTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/AbstractSkipLockedTest.java index e61e73a1761e..06fa88237fe2 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/locking/AbstractSkipLockedTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/AbstractSkipLockedTest.java @@ -4,27 +4,26 @@ */ package org.hibernate.orm.test.locking; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; import jakarta.persistence.Entity; import jakarta.persistence.Id; - +import jakarta.persistence.Timeout; import org.hibernate.LockMode; -import org.hibernate.LockOptions; import org.hibernate.Session; import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.SQLServerDialect; import org.hibernate.query.Query; - import org.hibernate.testing.DialectChecks; import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; import org.junit.Test; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -208,10 +207,7 @@ private List nextFiveBatchJobs(Session session, Integer maxResult) { } protected void applySkipLocked(Query query) { - query.setLockOptions( - new LockOptions( lockMode() ) - .setTimeOut( LockOptions.SKIP_LOCKED ) - ); + query.setHibernateLockMode( lockMode() ).setTimeout( Timeout.milliseconds( -2 ) ); } protected abstract LockMode lockMode(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/locking/ExplicitLockingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/ExplicitLockingTest.java index 73e42b6f15b8..305a556fb702 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/locking/ExplicitLockingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/ExplicitLockingTest.java @@ -4,9 +4,6 @@ */ package org.hibernate.orm.test.locking; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import jakarta.persistence.Column; import jakarta.persistence.ElementCollection; import jakarta.persistence.Embeddable; @@ -17,21 +14,22 @@ import jakarta.persistence.JoinTable; import jakarta.persistence.LockModeType; import jakarta.persistence.PessimisticLockScope; - import org.hibernate.LockMode; -import org.hibernate.LockOptions; import org.hibernate.Session; +import org.hibernate.Timeouts; import org.hibernate.dialect.OracleDialect; import org.hibernate.query.Query; - import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.Jpa; import org.hibernate.testing.orm.junit.RequiresDialect; +import org.jboss.logging.Logger; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import org.jboss.logging.Logger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -122,8 +120,7 @@ public void testSessionLock(EntityManagerFactoryScope scope) { //tag::locking-session-lock-example[] Person person = entityManager.find(Person.class, id); Session session = entityManager.unwrap(Session.class); - LockOptions lockOptions = new LockOptions(LockMode.PESSIMISTIC_READ, LockOptions.NO_WAIT); - session.lock(person, lockOptions); + session.lock( person, LockMode.PESSIMISTIC_READ, Timeouts.NO_WAIT ); //end::locking-session-lock-example[] }); @@ -132,8 +129,7 @@ public void testSessionLock(EntityManagerFactoryScope scope) { //tag::locking-session-lock-scope-example[] Person person = entityManager.find(Person.class, id); Session session = entityManager.unwrap(Session.class); - LockOptions lockOptions = new LockOptions(LockMode.PESSIMISTIC_READ, LockOptions.NO_WAIT, PessimisticLockScope.EXTENDED); - session.lock(person, lockOptions); + session.lock(person, LockMode.PESSIMISTIC_READ, Timeouts.NO_WAIT, PessimisticLockScope.EXTENDED); //end::locking-session-lock-scope-example[] }); @@ -177,14 +173,12 @@ public void testFollowOnLocking(EntityManagerFactoryScope scope) { scope.inTransaction( entityManager -> { //tag::locking-follow-on-explicit-example[] - List persons = entityManager.createQuery( - "select p from Person p", Person.class) - .setMaxResults(10) - .unwrap(Query.class) - .setLockOptions( - new LockOptions(LockMode.PESSIMISTIC_WRITE) - .setFollowOnLocking(false)) - .getResultList(); + List persons = entityManager.createQuery( "select p from Person p", Person.class ) + .setMaxResults( 10 ) + .setLockMode( LockModeType.PESSIMISTIC_WRITE ) + .unwrap( Query.class ) + .setFollowOnLocking( false ) + .getResultList(); //end::locking-follow-on-explicit-example[] }); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockModeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockModeTest.java index a0c6d4718f6a..6f74fa7fcdad 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockModeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockModeTest.java @@ -7,11 +7,11 @@ import java.util.Collections; import jakarta.persistence.LockModeType; +import jakarta.persistence.Timeout; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; import org.hibernate.LockMode; -import org.hibernate.LockOptions; import org.hibernate.Session; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.community.dialect.AltibaseDialect; @@ -80,11 +80,10 @@ public void prepareTest() throws Exception { @RequiresDialectFeature( feature = DialectFeatureChecks.SupportsLockTimeouts.class ) @SkipForDialect(dialectClass = CockroachDialect.class, reason = "for update clause does not imply locking. See https://github.com/cockroachdb/cockroach/issues/88995") @SkipForDialect(dialectClass = AltibaseDialect.class, reason = "Can't commit transaction because Altibase closes socket after lock timeout") - @SuppressWarnings( {"deprecation"}) public void testLoading() { // open a session, begin a transaction and lock row doInHibernate( this::sessionFactory, session -> { - A it = session.byId( A.class ).with( LockOptions.UPGRADE ).load( id ); + A it = session.find( A.class, id, LockMode.PESSIMISTIC_WRITE ); // make sure we got it assertNotNull( it ); @@ -168,10 +167,10 @@ public void testQueryUsingLockOptions() { // todo : need an association here to make sure the alias-specific lock modes are applied correctly doInHibernate( this::sessionFactory, session -> { session.createQuery( "from A a" ) - .setLockOptions( new LockOptions( LockMode.PESSIMISTIC_WRITE ) ) + .setLockMode( LockModeType.PESSIMISTIC_WRITE ) .uniqueResult(); session.createQuery( "from A a" ) - .setLockOptions( new LockOptions().setAliasSpecificLockMode( "a", LockMode.PESSIMISTIC_WRITE ) ) + .setLockMode( "a", LockMode.PESSIMISTIC_WRITE ) .uniqueResult(); } ); } @@ -204,7 +203,7 @@ public void testQueryLockModePessimisticWriteWithAlias() { @JiraKey(value = "HHH-12257") public void testRefreshLockedEntity() { doInHibernate( this::sessionFactory, session -> { - A a = session.get( A.class, id, LockMode.PESSIMISTIC_READ ); + A a = session.find( A.class, id, LockMode.PESSIMISTIC_READ ); checkLockMode( a, LockMode.PESSIMISTIC_READ, session ); session.refresh( a ); checkLockMode( a, LockMode.PESSIMISTIC_READ, session ); @@ -219,7 +218,7 @@ public void testRefreshLockedEntity() { @JiraKey(value = "HHH-12257") public void testRefreshWithExplicitLowerLevelLockMode() { doInHibernate( this::sessionFactory, session -> { - A a = session.get( A.class, id, LockMode.PESSIMISTIC_READ ); + A a = session.find( A.class, id, LockMode.PESSIMISTIC_READ ); checkLockMode( a, LockMode.PESSIMISTIC_READ, session ); session.refresh( a, LockMode.READ ); checkLockMode( a, LockMode.PESSIMISTIC_READ, session ); @@ -236,7 +235,7 @@ public void testRefreshWithExplicitLowerLevelLockMode() { @SkipForDialect( dialectClass = CockroachDialect.class ) public void testRefreshWithExplicitHigherLevelLockMode1() { doInHibernate( this::sessionFactory, session -> { - A a = session.get( A.class, id ); + A a = session.find( A.class, id ); checkLockMode( a, LockMode.READ, session ); session.refresh( a, LockMode.UPGRADE_NOWAIT ); checkLockMode( a, LockMode.UPGRADE_NOWAIT, session ); @@ -250,7 +249,7 @@ public void testRefreshWithExplicitHigherLevelLockMode1() { @SkipForDialect( dialectClass = CockroachDialect.class ) public void testRefreshWithExplicitHigherLevelLockMode2() { doInHibernate( this::sessionFactory, session -> { - A a = session.get( A.class, id ); + A a = session.find( A.class, id ); checkLockMode( a, LockMode.READ, session ); session.refresh( a, LockModeType.PESSIMISTIC_READ ); checkLockMode( a, LockMode.PESSIMISTIC_READ, session ); @@ -263,7 +262,7 @@ public void testRefreshWithExplicitHigherLevelLockMode2() { @JiraKey(value = "HHH-12257") public void testRefreshAfterUpdate() { doInHibernate( this::sessionFactory, session -> { - A a = session.get( A.class, id ); + A a = session.find( A.class, id ); checkLockMode( a, LockMode.READ, session ); a.setValue( "new value" ); session.flush(); @@ -294,10 +293,11 @@ private void nowAttemptToUpdateRow() { try { // load with write lock to deal with databases that block (wait indefinitely) direct attempts // to write a locked row - A it = _session.get( + A it = _session.find( A.class, id, - new LockOptions( LockMode.PESSIMISTIC_WRITE ).setTimeOut( LockOptions.NO_WAIT ) + LockMode.PESSIMISTIC_WRITE, + Timeout.milliseconds( 0 ) ); _session.createNativeQuery( updateStatement() ) .setParameter( "value", "changed" ) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/locking/PessimisticWriteLockTimeoutTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/PessimisticWriteLockTimeoutTest.java index 9d4a2aac6c34..ab1086838fc1 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/locking/PessimisticWriteLockTimeoutTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/PessimisticWriteLockTimeoutTest.java @@ -4,15 +4,14 @@ */ package org.hibernate.orm.test.locking; -import org.hibernate.LockMode; -import org.hibernate.LockOptions; -import org.hibernate.query.Query; +import jakarta.persistence.LockModeType; import org.hibernate.Session; +import org.hibernate.Timeouts; import org.hibernate.boot.SessionFactoryBuilder; import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.SQLServerDialect; - +import org.hibernate.query.Query; import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.jdbc.SQLStatementInterceptor; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; @@ -59,19 +58,16 @@ public void createTestData() { @RequiresDialect(OracleDialect.class) @RequiresDialect(PostgreSQLDialect.class) @RequiresDialect(SQLServerDialect.class) - public void testNoWait() - throws NoSuchFieldException, IllegalAccessException { + public void testNoWait() { Session session = sessionFactory().openSession(); session.beginTransaction(); try { - session.createQuery( - "select a from A a", A.class ) - .unwrap( Query.class ) - .setLockOptions( - new LockOptions( LockMode.PESSIMISTIC_WRITE ) - .setTimeOut( LockOptions.NO_WAIT ) ) - .list(); + session.createQuery( "select a from A a", A.class ) + .unwrap( Query.class ) + .setLockMode( LockModeType.PESSIMISTIC_WRITE ) + .setTimeout( Timeouts.NO_WAIT ) + .list(); String lockingQuery = sqlStatementInterceptor.getSqlQueries().getLast(); assertTrue( lockingQuery.toLowerCase().contains( "nowait") ); @@ -85,19 +81,15 @@ public void testNoWait() @Test @RequiresDialect(OracleDialect.class) @RequiresDialect(PostgreSQLDialect.class) - public void testSkipLocked() - throws NoSuchFieldException, IllegalAccessException { + public void testSkipLocked() { Session session = sessionFactory().openSession(); session.beginTransaction(); try { - session.createQuery( - "select a from A a", A.class ) - .unwrap( Query.class ) - .setLockOptions( - new LockOptions( LockMode.PESSIMISTIC_WRITE ) - .setTimeOut( LockOptions.SKIP_LOCKED ) ) - .list(); + session.createQuery("select a from A a", A.class ) + .setLockMode( LockModeType.PESSIMISTIC_WRITE ) + .setTimeout( Timeouts.SKIP_LOCKED ) + .list(); String lockingQuery = sqlStatementInterceptor.getSqlQueries().getLast(); assertTrue( lockingQuery.toLowerCase().contains( "skip locked") ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/locking/UpgradeSkipLockedTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/UpgradeSkipLockedTest.java index 2568fc88ab73..eaa66802f964 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/locking/UpgradeSkipLockedTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/UpgradeSkipLockedTest.java @@ -5,7 +5,6 @@ package org.hibernate.orm.test.locking; import org.hibernate.LockMode; -import org.hibernate.LockOptions; import org.hibernate.query.Query; /** @@ -17,9 +16,7 @@ public class UpgradeSkipLockedTest @Override protected void applySkipLocked(Query query) { - query.setLockOptions( - new LockOptions( lockMode() ).setFollowOnLocking( false ) - ); + query.setHibernateLockMode( lockMode() ).setFollowOnLocking( false ); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/MutableNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/MutableNaturalIdTest.java index ca587972a2c6..b44a33168a02 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/MutableNaturalIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/MutableNaturalIdTest.java @@ -4,25 +4,24 @@ */ package org.hibernate.orm.test.mapping.naturalid.mutable; -import java.lang.reflect.Field; - import org.hibernate.HibernateException; -import org.hibernate.LockOptions; +import org.hibernate.LockMode; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.stat.spi.StatisticsImplementor; -import org.hibernate.tuple.entity.EntityMetamodel; - -import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.ServiceRegistry; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.Setting; +import org.hibernate.tuple.entity.EntityMetamodel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import java.lang.reflect.Field; + import static org.hibernate.cfg.AvailableSettings.GENERATE_STATISTICS; import static org.hibernate.cfg.AvailableSettings.USE_QUERY_CACHE; import static org.hibernate.cfg.AvailableSettings.USE_SECOND_LEVEL_CACHE; @@ -146,7 +145,7 @@ public void testReattachmentUnmodifiedNaturalIdCheck(SessionFactoryScope scope) scope.inTransaction( (session) -> { try { - session.lock( created, LockOptions.NONE ); + session.lock( created, LockMode.NONE ); name.set( created, "Gavin" ); final User loaded = session diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/ops/genericApi/BasicGetLoadAccessTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/ops/genericApi/BasicGetLoadAccessTest.java index 43f91a8c5424..5e5620a8c126 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/ops/genericApi/BasicGetLoadAccessTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/ops/genericApi/BasicGetLoadAccessTest.java @@ -4,23 +4,21 @@ */ package org.hibernate.orm.test.ops.genericApi; -import java.util.NoSuchElementException; -import java.util.Optional; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; +import jakarta.persistence.LockModeType; import jakarta.persistence.Table; - import org.hibernate.LockMode; -import org.hibernate.LockOptions; -import org.hibernate.annotations.GenericGenerator; - import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import java.util.NoSuchElementException; +import java.util.Optional; + import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -30,6 +28,7 @@ /** * @author Steve Ebersole */ +@SuppressWarnings("JUnitMalformedDeclaration") @DomainModel( annotatedClasses = BasicGetLoadAccessTest.User.class ) @@ -37,11 +36,8 @@ public class BasicGetLoadAccessTest { @AfterEach - public void tearDown(SessionFactoryScope scope){ - scope.inTransaction( - session -> - session.createQuery( "delete from User" ).executeUpdate() - ); + public void tearDown(SessionFactoryScope scope) { + scope.dropData(); } @Test @@ -54,21 +50,20 @@ public void testIt(SessionFactoryScope scope) { ); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // test `get` access + // test `find` access scope.inTransaction( session -> - session.get( User.class, 1 ) + session.find( User.class, 1 ) ); - scope.inTransaction( session -> - session.get( User.class, 1, LockMode.PESSIMISTIC_WRITE ) + session.find( User.class, 1, LockMode.PESSIMISTIC_WRITE ) ); scope.inTransaction( session -> - session.get( User.class, 1, LockOptions.UPGRADE ) + session.find( User.class, 1, LockModeType.PESSIMISTIC_WRITE ) ); scope.inTransaction( @@ -78,11 +73,11 @@ public void testIt(SessionFactoryScope scope) { scope.inTransaction( session -> - session.byId( User.class ).with( LockOptions.UPGRADE ).load( 1 ) + session.byId( User.class ).with( LockMode.PESSIMISTIC_WRITE ).load( 1 ) ); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // test `load` access + // test `getReference` access scope.inTransaction( session -> session.getReference( User.class, 1 ) @@ -90,12 +85,7 @@ public void testIt(SessionFactoryScope scope) { scope.inTransaction( session -> - session.get( User.class, 1, LockMode.PESSIMISTIC_WRITE ) - ); - - scope.inTransaction( - session -> - session.get( User.class, 1, LockOptions.UPGRADE ) + session.find( User.class, 1, LockMode.PESSIMISTIC_WRITE ) ); scope.inTransaction( @@ -105,7 +95,7 @@ public void testIt(SessionFactoryScope scope) { scope.inTransaction( session -> - session.byId( User.class ).with( LockOptions.UPGRADE ).getReference( 1 ) + session.byId( User.class ).with( LockMode.PESSIMISTIC_WRITE ).getReference( 1 ) ); } @@ -164,7 +154,6 @@ public User(String name) { @Id @GeneratedValue(generator = "increment") - @GenericGenerator(name = "increment", strategy = "increment") public Integer getId() { return id; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/pagination/OraclePaginationWithLocksTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/pagination/OraclePaginationWithLocksTest.java index b1cf79fb749d..5224b7bd07fe 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/pagination/OraclePaginationWithLocksTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/pagination/OraclePaginationWithLocksTest.java @@ -4,22 +4,18 @@ */ package org.hibernate.orm.test.pagination; -import java.util.List; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; +import jakarta.persistence.LockModeType; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Root; - -import org.hibernate.LockMode; -import org.hibernate.LockOptions; import org.hibernate.dialect.OracleDialect; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.query.common.FetchClauseType; import org.hibernate.resource.jdbc.spi.StatementInspector; - -import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; @@ -27,6 +23,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.List; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -133,7 +131,8 @@ public void testCriteriaQuery(SessionFactoryScope scope) { query.select( root ); final List people = session.createQuery( query ) .setMaxResults( 10 ) - .setLockOptions( new LockOptions( LockMode.PESSIMISTIC_WRITE ).setFollowOnLocking( false ) ) + .setLockMode( LockModeType.PESSIMISTIC_WRITE ) + .setFollowOnLocking( false ) .getResultList(); assertEquals( 10, people.size() ); assertSqlContainsFetch( session ); @@ -176,7 +175,8 @@ public void testHqlQuery(SessionFactoryScope scope) { List people = session.createQuery( "select p from Person p", Person.class ) .setMaxResults( 10 ) - .setLockOptions( new LockOptions( LockMode.PESSIMISTIC_WRITE ).setFollowOnLocking( false ) ) + .setLockMode( LockModeType.PESSIMISTIC_WRITE ) + .setFollowOnLocking( false ) .getResultList(); assertEquals( 10, people.size() ); assertSqlContainsFetch( session ); @@ -212,7 +212,8 @@ public void testHqlQuery(SessionFactoryScope scope) { List people = session.createQuery( "select p from Person p where p.name = 'for update'", Person.class ) .setMaxResults( 10 ) - .setLockOptions( new LockOptions( LockMode.PESSIMISTIC_WRITE ).setFollowOnLocking( false ) ) + .setLockMode( LockModeType.PESSIMISTIC_WRITE ) + .setFollowOnLocking( false ) .getResultList(); assertEquals( 1, people.size() ); assertSqlContainsFetch( session ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyTest.java index e640db1978b9..479474e945af 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyTest.java @@ -4,14 +4,10 @@ */ package org.hibernate.orm.test.proxy; -import java.math.BigDecimal; -import java.util.List; - import org.hibernate.FlushMode; import org.hibernate.Hibernate; import org.hibernate.LazyInitializationException; import org.hibernate.LockMode; -import org.hibernate.LockOptions; import org.hibernate.ObjectNotFoundException; import org.hibernate.Session; import org.hibernate.Transaction; @@ -21,11 +17,13 @@ import org.hibernate.internal.SessionImpl; import org.hibernate.internal.util.SerializationHelper; import org.hibernate.proxy.HibernateProxy; - import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.testing.orm.junit.JiraKey; import org.junit.Test; +import java.math.BigDecimal; +import java.util.List; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; @@ -296,7 +294,7 @@ public void testProxy() { dp = s.getReference( DataPoint.class, dp.getId()); assertFalse( Hibernate.isInitialized(dp) ); - dp2 = s.byId( DataPoint.class ).with( LockOptions.READ ).getReference( dp.getId() ); + dp2 = s.find( DataPoint.class, dp.getId(), LockMode.READ ); assertSame(dp, dp2); assertTrue( Hibernate.isInitialized(dp) ); s.clear(); @@ -346,7 +344,7 @@ public void testProxyWithGetReference() { dp = s.getReference( DataPoint.class, dp.getId() ); assertFalse( Hibernate.isInitialized(dp) ); - dp2 = s.byId( DataPoint.class ).with( LockOptions.READ ).getReference( dp.getId() ); + dp2 = s.find( DataPoint.class, dp.getId(), LockMode.READ ); assertSame(dp, dp2); assertTrue( Hibernate.isInitialized(dp) ); s.clear(); @@ -528,8 +526,8 @@ public void testRefreshLockInitializedProxy() { dp.getX(); assertTrue( Hibernate.isInitialized( dp ) ); - s.refresh( dp, LockOptions.UPGRADE ); - assertSame( LockOptions.UPGRADE.getLockMode(), s.getCurrentLockMode( dp ) ); + s.refresh( dp, LockMode.PESSIMISTIC_WRITE ); + assertSame( LockMode.PESSIMISTIC_WRITE, s.getCurrentLockMode( dp ) ); s.remove( dp ); t.commit(); @@ -546,8 +544,8 @@ public void testRefreshLockUninitializedProxy() { dp = s.getReference( DataPoint.class, dp.getId()); assertFalse( Hibernate.isInitialized( dp ) ); - s.refresh( dp, LockOptions.UPGRADE ); - assertSame( LockOptions.UPGRADE.getLockMode(), s.getCurrentLockMode( dp ) ); + s.refresh( dp, LockMode.PESSIMISTIC_WRITE ); + assertSame( LockMode.PESSIMISTIC_WRITE, s.getCurrentLockMode( dp ) ); s.remove( dp ); t.commit(); @@ -574,9 +572,9 @@ public void testRefreshLockUninitializedProxyThenRead() { dp = s.getReference( DataPoint.class, dp.getId()); assertFalse( Hibernate.isInitialized( dp ) ); - s.refresh( dp, LockOptions.UPGRADE ); + s.refresh( dp, LockMode.PESSIMISTIC_WRITE ); dp.getX(); - assertSame( LockOptions.UPGRADE.getLockMode(), s.getCurrentLockMode( dp ) ); + assertSame( LockMode.PESSIMISTIC_WRITE, s.getCurrentLockMode( dp ) ); s.remove( dp ); t.commit(); @@ -591,8 +589,8 @@ public void testLockUninitializedProxy() { dp = s.getReference( DataPoint.class, dp.getId()); assertFalse( Hibernate.isInitialized( dp ) ); - s.lock( dp, LockOptions.UPGRADE ); - assertSame( LockOptions.UPGRADE.getLockMode(), s.getCurrentLockMode( dp ) ); + s.lock( dp, LockMode.PESSIMISTIC_WRITE ); + assertSame( LockMode.PESSIMISTIC_WRITE, s.getCurrentLockMode( dp ) ); s.remove( dp ); t.commit(); diff --git a/migration-guide.adoc b/migration-guide.adoc index 58a20359eb2a..3abc842d6cde 100644 --- a/migration-guide.adoc +++ b/migration-guide.adoc @@ -729,11 +729,47 @@ Now, `varchar(1)` is used by default. [[settings]] -=== Changes Related to Settings +== Changes Related to Settings * Removed `hibernate.mapping.precedence` and friends * Removed `hibernate.allow_refresh_detached_entity` +[[lock-options]] +== LockOptions + +The exposure of `LockOptions` as an API has been deprecated. Applications should instead prefer the use of `LockMode` (or `LockModeType`), `Timeout` and `PessimisticLockScope`. + +Use this + +==== +[source, java, indent=0] +---- +Book loaded = session.find( + Book.class, + 1, + LockMode.PESSIMISTIC_WRITE, + Timeouts.NO_WAIT +); +---- +==== + +instead of this + +==== +[source, java, indent=0] +---- +Book loaded = session.find( + Book.class, + 1, + new LockOptions( + LockMode.PESSIMISTIC_WRITE, + 0 + ) +); +---- +==== + +See the link:{whatsNewBase}#operation-options[What's New] guide for more details. [[pools]] diff --git a/whats-new.adoc b/whats-new.adoc index 713941a44c2b..ee9583919926 100644 --- a/whats-new.adoc +++ b/whats-new.adoc @@ -126,6 +126,53 @@ The new operations `Session.findMultiple()` and `StatelessSession.getMultiple()` Combined with the `BatchSize` option, allows breaking up the JDBC calls into "batches". +[[operation-options]] +== FindOption, LockOption, RefreshOption + +As a more typesafe replacement for hints, Jakarta Persistence now allows for "option objects" which indicate these hints. Not only is it more typesafe, but also more concise - a double win! + +These "option objects" come in 3 flavors depending on what operation is being performed - + +FindOption:: Used to indicate options for find operations : + * `Session.find` + * `Session.findMultiple` + * etc +LockOption:: Used to indicate options for `Session.lock` +RefreshOption:: Used to indicate options for `Session.refresh` + +Each of these are interfaces. Jakarta Persistence does provide some built-in options: + +CacheRetrieveMode:: Is now a `FindOption`. +CacheStoreMode:: Is now a `FindOption` and a `RefreshOption`. +LockModeType:: Is now a `FindOption` and a `RefreshOption`. +Timeout (new):: Is a `FindOption`, a `RefreshOption` and a `LockOption`. See also `org.hibernate.Timeouts` for some magic values. +PessimisticLockScope:: Is now a `FindOption`, a `RefreshOption` and a `LockOption` + +Hibernate provides a few additional options: + +ReadOnlyMode (new):: Is a `FindOption` which indicates whether the entities and collections being loaded should be considered read-only. +EnabledFetchProfile (new):: Is a `FindOption` indicating a fetch-profile to be enabled for the find operation. +LockMode:: Is now a `FindOption` and a `RefreshOption` (expanded version of JPA's `LockModeType`). +CacheMode:: Is now a `FindOption`. +BatchSize (new):: Is now a `FindOption`. + + +All operations which accept options (which previously accepted hints) accept a varargs grouping of them. E.g. + +==== +[source, java, indent=0] +---- +Book loaded = session.find( + Book.class, + 1, + LockMode.PESSIMISTIC_WRITE, + Timeouts.NO_WAIT, + new EnabledFetchProfile("with-authors") +); +---- +==== + + [[QuerySpecification]] == QuerySpecification, Restriction, and Range