From 52e185b1948dd341150eb1fe204a81d5f3253870 Mon Sep 17 00:00:00 2001 From: Gavin Date: Wed, 28 Dec 2022 14:54:37 +0100 Subject: [PATCH] HHH-15942 introduce QueryFlushMode for specifying whether a query flushes or not - replaces FlushModeType in the annotation package - much less confusing when applied to a Query * what do MANUAL and COMMIT mean for a Query? * how is AUTO useful for a Query? - also make Query.getHibernateFlushMode() obey its documented semantics by returning the session flush mode instead of null when unset --- .../main/java/org/hibernate/FlushMode.java | 9 +- .../src/main/java/org/hibernate/Session.java | 2 +- .../hibernate/annotations/FlushModeType.java | 9 +- .../annotations/NamedNativeQuery.java | 14 + .../org/hibernate/annotations/NamedQuery.java | 14 + .../boot/model/internal/QueryBinder.java | 12 +- .../internal/NamedNativeQueryAnnotation.java | 14 + .../internal/NamedQueryAnnotation.java | 14 + .../internal/util/FlushModeTypeHelper.java | 66 ++-- .../hibernate/procedure/ProcedureCall.java | 2 +- .../procedure/internal/ProcedureCallImpl.java | 2 +- .../hibernate/query/CommonQueryContract.java | 60 +++- .../org/hibernate/query/MutationQuery.java | 8 +- .../java/org/hibernate/query/NativeQuery.java | 5 +- .../main/java/org/hibernate/query/Query.java | 7 +- .../org/hibernate/query/QueryFlushMode.java | 42 +++ .../org/hibernate/query/SelectionQuery.java | 24 +- .../query/hql/spi/SqmQueryImplementor.java | 8 +- .../spi/AbstractCommonQueryContract.java | 17 +- .../hibernate/query/spi/AbstractQuery.java | 18 +- .../query/spi/AbstractSelectionQuery.java | 15 +- .../org/hibernate/query/spi/SqmQuery.java | 6 +- .../query/sql/internal/NativeQueryImpl.java | 12 +- .../query/sql/spi/NativeQueryImplementor.java | 6 +- .../query/sqm/SqmSelectionQuery.java | 6 +- .../query/sqm/internal/QuerySqmImpl.java | 14 +- .../sqm/internal/SqmSelectionQueryImpl.java | 7 + ...elegatingSqmSelectionQueryImplementor.java | 12 + .../jpa/query/NamedQueryFlushModeTest.java | 17 +- .../query/NamedQueryQueryFlushModeTest.java | 293 ++++++++++++++++++ 30 files changed, 641 insertions(+), 94 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/query/QueryFlushMode.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NamedQueryQueryFlushModeTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/FlushMode.java b/hibernate-core/src/main/java/org/hibernate/FlushMode.java index 79a0ca205d..29caaf86a0 100644 --- a/hibernate-core/src/main/java/org/hibernate/FlushMode.java +++ b/hibernate-core/src/main/java/org/hibernate/FlushMode.java @@ -18,9 +18,16 @@ import jakarta.persistence.FlushModeType; *

* For example, {@link #COMMIT} specifies that the session flushes * automatically when the transaction is about to commit. + *

+ * This enumeration represents options which may be + * {@linkplain Session#setHibernateFlushMode set at the session + * level}, and competes with the JPA-defined enumeration + * {@link jakarta.persistence.FlushModeType}. Alternatively, a + * {@link org.hibernate.query.QueryFlushMode QueryFlushMode} may + * be specified for a given query. * * @see Session#setHibernateFlushMode - * @see org.hibernate.query.CommonQueryContract#setHibernateFlushMode + * @see org.hibernate.query.QueryFlushMode * * @author Gavin King */ diff --git a/hibernate-core/src/main/java/org/hibernate/Session.java b/hibernate-core/src/main/java/org/hibernate/Session.java index e5b8713c8a..64c0a3358f 100644 --- a/hibernate-core/src/main/java/org/hibernate/Session.java +++ b/hibernate-core/src/main/java/org/hibernate/Session.java @@ -199,7 +199,7 @@ public interface Session extends SharedSessionContract, EntityManager { void flush(); /** - * Set the current {@link FlushModeType JPA flush mode} for this session. + * Set the current {@linkplain FlushModeType JPA flush mode} for this session. *

* Flushing is the process of synchronizing the underlying persistent * store with persistable state held in memory. The current flush mode determines diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/FlushModeType.java b/hibernate-core/src/main/java/org/hibernate/annotations/FlushModeType.java index 24818577ec..73c1462c1a 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/FlushModeType.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/FlushModeType.java @@ -6,6 +6,8 @@ */ package org.hibernate.annotations; +import org.hibernate.query.QueryFlushMode; + /** * Enumeration extending the {@linkplain jakarta.persistence.FlushModeType JPA flush modes} * with flush modes specific to Hibernate, and a "null" mode, {@link #PERSISTENCE_CONTEXT}, @@ -14,9 +16,12 @@ package org.hibernate.annotations; * * @author Carlos Gonzalez-Cadenas * - * @see NamedQuery - * @see NamedNativeQuery + * @see NamedQuery#flushMode + * @see NamedNativeQuery#flushMode + * + * @deprecated use {@link QueryFlushMode} */ +@Deprecated(since="7") public enum FlushModeType { /** * Corresponds to {@link org.hibernate.FlushMode#ALWAYS}. diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/NamedNativeQuery.java b/hibernate-core/src/main/java/org/hibernate/annotations/NamedNativeQuery.java index 79b204bdcc..7a028ee81d 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/NamedNativeQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/NamedNativeQuery.java @@ -13,6 +13,7 @@ import java.lang.annotation.Target; import jakarta.persistence.CacheRetrieveMode; import jakarta.persistence.CacheStoreMode; import org.hibernate.CacheMode; +import org.hibernate.query.QueryFlushMode; import static java.lang.annotation.ElementType.PACKAGE; import static java.lang.annotation.ElementType.TYPE; @@ -63,12 +64,25 @@ public @interface NamedNativeQuery { */ String resultSetMapping() default ""; + /** + * Determines whether the session should be flushed before + * executing the query. + * + * @see org.hibernate.query.CommonQueryContract#setQueryFlushMode(QueryFlushMode) + * + * @since 7.0 + */ + QueryFlushMode flush() default QueryFlushMode.DEFAULT; + /** * The flush mode for the query. * * @see org.hibernate.query.CommonQueryContract#setFlushMode(jakarta.persistence.FlushModeType) * @see org.hibernate.jpa.HibernateHints#HINT_FLUSH_MODE + * + * @deprecated use {@link #flush()} */ + @Deprecated(since = "7") FlushModeType flushMode() default FlushModeType.PERSISTENCE_CONTEXT; /** diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/NamedQuery.java b/hibernate-core/src/main/java/org/hibernate/annotations/NamedQuery.java index 61c8bba6f3..8d2adf5472 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/NamedQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/NamedQuery.java @@ -15,6 +15,7 @@ import jakarta.persistence.CacheStoreMode; import jakarta.persistence.EntityManager; import org.hibernate.CacheMode; +import org.hibernate.query.QueryFlushMode; import static java.lang.annotation.ElementType.PACKAGE; import static java.lang.annotation.ElementType.TYPE; @@ -58,12 +59,25 @@ public @interface NamedQuery { */ Class resultClass() default void.class; + /** + * Determines whether the session should be flushed before + * executing the query. + * + * @see org.hibernate.query.CommonQueryContract#setQueryFlushMode(QueryFlushMode) + * + * @since 7.0 + */ + QueryFlushMode flush() default QueryFlushMode.DEFAULT; + /** * The flush mode for this query. * * @see org.hibernate.query.CommonQueryContract#setFlushMode(jakarta.persistence.FlushModeType) * @see org.hibernate.jpa.HibernateHints#HINT_FLUSH_MODE + * + * @deprecated use {@link #flush()} */ + @Deprecated(since = "7") FlushModeType flushMode() default FlushModeType.PERSISTENCE_CONTEXT; /** diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/QueryBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/QueryBinder.java index 1151318f39..999f8ea04a 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/QueryBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/QueryBinder.java @@ -16,6 +16,7 @@ import java.util.function.Supplier; import org.hibernate.AnnotationException; import org.hibernate.CacheMode; import org.hibernate.FlushMode; +import org.hibernate.query.QueryFlushMode; import org.hibernate.LockOptions; import org.hibernate.annotations.FlushModeType; import org.hibernate.annotations.HQLSelect; @@ -35,6 +36,7 @@ import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.log.DeprecationLogger; import org.hibernate.jpa.HibernateHints; +import org.hibernate.jpa.internal.util.FlushModeTypeHelper; import org.hibernate.models.internal.util.StringHelper; import org.hibernate.models.spi.ClassDetails; import org.hibernate.models.spi.SourceModelBuildingContext; @@ -271,7 +273,7 @@ public abstract class QueryBinder { .setCacheMode(getCacheMode(namedNativeQuery.cacheRetrieveMode(), namedNativeQuery.cacheStoreMode())) .setTimeout(timeout < 0 ? null : timeout) .setFetchSize(fetchSize < 0 ? null : fetchSize) - .setFlushMode(getFlushMode(namedNativeQuery.flushMode())) + .setFlushMode(getFlushMode(namedNativeQuery.flush(), namedNativeQuery.flushMode())) .setReadOnly(namedNativeQuery.readOnly()) .setQuerySpaces(querySpaces) .setComment(nullIfEmpty(namedNativeQuery.comment())); @@ -412,7 +414,7 @@ public abstract class QueryBinder { .setCacheMode(getCacheMode(namedQuery.cacheRetrieveMode(), namedQuery.cacheStoreMode())) .setTimeout(timeout < 0 ? null : timeout) .setFetchSize(fetchSize < 0 ? null : fetchSize) - .setFlushMode(getFlushMode(namedQuery.flushMode())) + .setFlushMode(getFlushMode(namedQuery.flush(), namedQuery.flushMode())) .setReadOnly(namedQuery.readOnly()) .setComment(nullIfEmpty(namedQuery.comment())); } @@ -422,6 +424,12 @@ public abstract class QueryBinder { return cacheMode == null ? CacheMode.NORMAL : cacheMode; } + private static FlushMode getFlushMode(QueryFlushMode queryFlushMode, FlushModeType flushModeType) { + return queryFlushMode == QueryFlushMode.DEFAULT + ? getFlushMode( flushModeType ) + : FlushModeTypeHelper.getFlushMode(queryFlushMode); + } + private static FlushMode getFlushMode(FlushModeType flushModeType) { return switch ( flushModeType ) { case ALWAYS -> FlushMode.ALWAYS; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/annotations/internal/NamedNativeQueryAnnotation.java b/hibernate-core/src/main/java/org/hibernate/boot/models/annotations/internal/NamedNativeQueryAnnotation.java index 45441f7229..2b4c0898cb 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/annotations/internal/NamedNativeQueryAnnotation.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/annotations/internal/NamedNativeQueryAnnotation.java @@ -24,6 +24,7 @@ import org.hibernate.models.spi.SourceModelBuildingContext; import jakarta.persistence.CacheRetrieveMode; import jakarta.persistence.CacheStoreMode; +import org.hibernate.query.QueryFlushMode; @SuppressWarnings({ "ClassExplicitlyAnnotation", "unused" }) @jakarta.annotation.Generated("org.hibernate.orm.build.annotations.ClassGeneratorProcessor") @@ -33,6 +34,7 @@ public class NamedNativeQueryAnnotation implements NamedNativeQuery { private Class resultClass; private String resultSetMapping; private FlushModeType flushMode; + private QueryFlushMode flush; boolean cacheable; String cacheRegion; int fetchSize; @@ -51,6 +53,7 @@ public class NamedNativeQueryAnnotation implements NamedNativeQuery { resultClass = void.class; resultSetMapping = ""; flushMode = FlushModeType.PERSISTENCE_CONTEXT; + flush = QueryFlushMode.DEFAULT; cacheable = false; cacheRegion = ""; fetchSize = -1; @@ -72,6 +75,7 @@ public class NamedNativeQueryAnnotation implements NamedNativeQuery { this.resultClass = annotation.resultClass(); this.resultSetMapping = annotation.resultSetMapping(); this.flushMode = annotation.flushMode(); + this.flush = annotation.flush(); this.cacheable = annotation.cacheable(); this.cacheRegion = annotation.cacheRegion(); this.fetchSize = annotation.fetchSize(); @@ -97,6 +101,7 @@ public class NamedNativeQueryAnnotation implements NamedNativeQuery { this.resultClass = (Class) attributeValues.get( "resultClass" ); this.resultSetMapping = (String) attributeValues.get( "resultSetMapping" ); this.flushMode = (FlushModeType) attributeValues.get( "flushMode" ); + this.flush = (QueryFlushMode) attributeValues.get( "flush" ); this.cacheable = (boolean) attributeValues.get( "cacheable" ); this.cacheRegion = (String) attributeValues.get( "cacheRegion" ); this.fetchSize = (int) attributeValues.get( "fetchSize" ); @@ -152,6 +157,15 @@ public class NamedNativeQueryAnnotation implements NamedNativeQuery { this.resultSetMapping = value; } + @Override + public QueryFlushMode flush() { + return flush; + } + + public void flush(QueryFlushMode value) { + this.flush = value; + } + @Override public FlushModeType flushMode() { return flushMode; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/annotations/internal/NamedQueryAnnotation.java b/hibernate-core/src/main/java/org/hibernate/boot/models/annotations/internal/NamedQueryAnnotation.java index 01eb1aa921..d884bac43c 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/annotations/internal/NamedQueryAnnotation.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/annotations/internal/NamedQueryAnnotation.java @@ -19,6 +19,7 @@ import org.hibernate.models.spi.SourceModelBuildingContext; import jakarta.persistence.CacheRetrieveMode; import jakarta.persistence.CacheStoreMode; +import org.hibernate.query.QueryFlushMode; import static org.hibernate.boot.models.xml.internal.QueryProcessing.interpretFlushMode; @@ -29,6 +30,7 @@ public class NamedQueryAnnotation implements NamedQuery { private String query; private Class resultClass; private FlushModeType flushMode; + private QueryFlushMode flush; boolean cacheable; String cacheRegion; int fetchSize; @@ -44,6 +46,7 @@ public class NamedQueryAnnotation implements NamedQuery { public NamedQueryAnnotation(SourceModelBuildingContext modelContext) { resultClass = void.class; flushMode = FlushModeType.PERSISTENCE_CONTEXT; + flush = QueryFlushMode.DEFAULT; cacheable = false; cacheRegion = ""; fetchSize = -1; @@ -62,6 +65,7 @@ public class NamedQueryAnnotation implements NamedQuery { this.query = annotation.query(); this.resultClass = annotation.resultClass(); this.flushMode = annotation.flushMode(); + this.flush = annotation.flush(); this.cacheable = annotation.cacheable(); this.cacheRegion = annotation.cacheRegion(); this.fetchSize = annotation.fetchSize(); @@ -84,6 +88,7 @@ public class NamedQueryAnnotation implements NamedQuery { this.query = (String) attributeValues.get( "query" ); this.resultClass = (Class) attributeValues.get( "resultClass" ); this.flushMode = (FlushModeType) attributeValues.get( "flushMode" ); + this.flush = (QueryFlushMode) attributeValues.get( "flush" ); this.cacheable = (boolean) attributeValues.get( "cacheable" ); this.cacheRegion = (String) attributeValues.get( "cacheRegion" ); this.fetchSize = (int) attributeValues.get( "fetchSize" ); @@ -128,6 +133,15 @@ public class NamedQueryAnnotation implements NamedQuery { this.resultClass = value; } + @Override + public QueryFlushMode flush() { + return flush; + } + + public void flush(QueryFlushMode value) { + this.flush = value; + } + @Override public FlushModeType flushMode() { return flushMode; diff --git a/hibernate-core/src/main/java/org/hibernate/jpa/internal/util/FlushModeTypeHelper.java b/hibernate-core/src/main/java/org/hibernate/jpa/internal/util/FlushModeTypeHelper.java index c07ba9b4d7..55e438baa8 100644 --- a/hibernate-core/src/main/java/org/hibernate/jpa/internal/util/FlushModeTypeHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/jpa/internal/util/FlushModeTypeHelper.java @@ -10,8 +10,8 @@ package org.hibernate.jpa.internal.util; import java.util.Locale; import jakarta.persistence.FlushModeType; -import org.hibernate.AssertionFailure; import org.hibernate.FlushMode; +import org.hibernate.query.QueryFlushMode; import org.hibernate.MappingException; import org.jboss.logging.Logger; @@ -28,35 +28,55 @@ public class FlushModeTypeHelper { } public static FlushModeType getFlushModeType(FlushMode flushMode) { - if ( flushMode == FlushMode.ALWAYS ) { - log.debug( "Interpreting Hibernate FlushMode#ALWAYS to JPA FlushModeType#AUTO; may cause problems if relying on FlushMode#ALWAYS-specific behavior" ); - return FlushModeType.AUTO; + if ( flushMode == null ) { + return null; } - else if ( flushMode == FlushMode.MANUAL ) { - log.debug( "Interpreting Hibernate FlushMode#MANUAL to JPA FlushModeType#COMMIT; may cause problems if relying on FlushMode#MANUAL-specific behavior" ); - return FlushModeType.COMMIT; - } - else if ( flushMode == FlushMode.COMMIT ) { - return FlushModeType.COMMIT; - } - else if ( flushMode == FlushMode.AUTO ) { - return FlushModeType.AUTO; - } - else { - throw new AssertionFailure( "unhandled FlushMode " + flushMode ); + return switch (flushMode) { + case ALWAYS -> { + log.debug("Interpreting Hibernate FlushMode.ALWAYS as JPA FlushModeType.AUTO (may cause problems if relying on FlushMode.ALWAYS-specific behavior)"); + yield FlushModeType.AUTO; + } + case MANUAL -> { + log.debug("Interpreting Hibernate FlushMode.MANUAL as JPA FlushModeType.COMMIT (may cause problems if relying on FlushMode.MANUAL-specific behavior)"); + yield FlushModeType.COMMIT; + } + case COMMIT -> FlushModeType.COMMIT; + case AUTO -> FlushModeType.AUTO; + }; + } + + public static QueryFlushMode getForcedFlushMode(FlushMode flushMode) { + if ( flushMode == null ) { + return QueryFlushMode.DEFAULT; } + return switch (flushMode) { + case ALWAYS -> QueryFlushMode.FLUSH; + case COMMIT, MANUAL -> QueryFlushMode.NO_FLUSH; + case AUTO -> + // this is not precisely correctly correct, but good enough + QueryFlushMode.DEFAULT; + }; } public static FlushMode getFlushMode(FlushModeType flushModeType) { - if ( flushModeType == FlushModeType.AUTO ) { - return FlushMode.AUTO; + if ( flushModeType == null ) { + return null; } - else if ( flushModeType == FlushModeType.COMMIT ) { - return FlushMode.COMMIT; - } - else { - throw new AssertionFailure( "unhandled FlushModeType " + flushModeType ); + return switch (flushModeType) { + case AUTO -> FlushMode.AUTO; + case COMMIT -> FlushMode.COMMIT; + }; + } + + public static FlushMode getFlushMode(QueryFlushMode queryFlushMode) { + if ( queryFlushMode == null ) { + return null; } + return switch (queryFlushMode) { + case FLUSH -> FlushMode.ALWAYS; + case NO_FLUSH -> FlushMode.MANUAL; + default -> null; + }; } public static FlushMode interpretFlushMode(Object value) { diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCall.java b/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCall.java index 7418ac6546..a6db5f5fbc 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCall.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCall.java @@ -281,7 +281,7 @@ public interface ProcedureCall @Override ProcedureCall setParameter(int position, Date value, TemporalType temporalType); - @Override + @Override @Deprecated(since = "7") ProcedureCall setFlushMode(FlushModeType flushMode); @Override diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java index d61b28d410..4904cce092 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java @@ -809,7 +809,7 @@ public class ProcedureCallImpl isCacheable(), getCacheRegion(), getCacheMode(), - getHibernateFlushMode(), + getQueryOptions().getFlushMode(), isReadOnly(), getTimeout(), getFetchSize(), 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 1acbeccdf6..a6919298aa 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/CommonQueryContract.java +++ b/hibernate-core/src/main/java/org/hibernate/query/CommonQueryContract.java @@ -51,48 +51,86 @@ import jakarta.persistence.TemporalType; */ public interface CommonQueryContract { + /** + * The {@link QueryFlushMode} in effect for this query. + *

+ * By default, this is {@link QueryFlushMode#DEFAULT}, and the + * {@link FlushMode} of the owning {@link Session} determines whether + * it is flushed. + * + * @see Session#getHibernateFlushMode() + * + * @since 7.0 + */ + QueryFlushMode getQueryFlushMode(); + + /** + * Set the {@link QueryFlushMode} to use for this query. + * + * @see Session#getHibernateFlushMode() + * + * @since 7.0 + */ + CommonQueryContract setQueryFlushMode(QueryFlushMode queryFlushMode); + /** * The JPA {@link FlushModeType} in effect for this query. By default, the * query inherits the {@link FlushMode} of the {@link Session} from which * it originates. * - * @see #getHibernateFlushMode - * @see Session#getHibernateFlushMode + * @see #getQueryFlushMode() + * @see #getHibernateFlushMode() + * @see Session#getHibernateFlushMode() + * + * @deprecated use {@link #getQueryFlushMode()} */ + @Deprecated(since = "7") FlushModeType getFlushMode(); /** - * Set the {@link FlushMode} in to use for this query. - * - * @implNote Setting to {@code null} ultimately indicates to use the - * FlushMode of the Session. Use {@link #setHibernateFlushMode} passing - * {@link FlushMode#MANUAL} instead to indicate that no automatic flushing - * should occur + * Set the {@link FlushMode} to use for this query. + *

+ * Setting this to {@code null} ultimately indicates to use the + * {@link FlushMode} of the session. Use {@link #setHibernateFlushMode} + * passing {@link FlushMode#MANUAL} instead to indicate that no automatic + * flushing should occur. * + * @see #getQueryFlushMode() * @see #getHibernateFlushMode() * @see Session#getHibernateFlushMode() + * + * @deprecated use {@link #setQueryFlushMode(QueryFlushMode)} */ + @Deprecated(since = "7") CommonQueryContract setFlushMode(FlushModeType flushMode); /** - * The {@link FlushMode} in effect for this query. By default, the query + * The {@link FlushMode} in effect for this query. By default, the query * inherits the {@code FlushMode} of the {@link Session} from which it * originates. * - * @see Session#getHibernateFlushMode + * @see #getQueryFlushMode() + * @see Session#getHibernateFlushMode() + * + * @deprecated use {@link #getQueryFlushMode()} */ + @Deprecated(since = "7") FlushMode getHibernateFlushMode(); /** * Set the current {@link FlushMode} in effect for this query. * * @implNote Setting to {@code null} ultimately indicates to use the - * {@link FlushMode} of the Session. Use {@link FlushMode#MANUAL} + * {@link FlushMode} of the session. Use {@link FlushMode#MANUAL} * instead to indicate that no automatic flushing should occur. * + * @see #getQueryFlushMode() * @see #getHibernateFlushMode() * @see Session#getHibernateFlushMode() + * + * @deprecated use {@link #setQueryFlushMode(QueryFlushMode)} */ + @Deprecated(since = "7") CommonQueryContract setHibernateFlushMode(FlushMode flushMode); /** diff --git a/hibernate-core/src/main/java/org/hibernate/query/MutationQuery.java b/hibernate-core/src/main/java/org/hibernate/query/MutationQuery.java index 2f13e5178d..9ce0ef89c4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/MutationQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/MutationQuery.java @@ -14,7 +14,6 @@ import java.util.Map; import org.hibernate.FlushMode; import org.hibernate.Incubating; -import org.hibernate.Session; import jakarta.persistence.FlushModeType; import jakarta.persistence.Parameter; @@ -81,10 +80,10 @@ public interface MutationQuery extends CommonQueryContract { // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Covariant returns - @Override + @Override @Deprecated(since = "7") MutationQuery setFlushMode(FlushModeType flushMode); - @Override + @Override @Deprecated(since = "7") MutationQuery setHibernateFlushMode(FlushMode flushMode); @Override @@ -209,4 +208,7 @@ public interface MutationQuery extends CommonQueryContract { @Override MutationQuery setProperties(@SuppressWarnings("rawtypes") Map bean); + + @Override + MutationQuery setQueryFlushMode(QueryFlushMode queryFlushMode); } 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 6e2ac20e5c..54aab000a9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/NativeQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/NativeQuery.java @@ -554,10 +554,13 @@ public interface NativeQuery extends Query, SynchronizeableQuery { // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // covariant overrides - Query - @Override + @Override @Deprecated(since = "7") NativeQuery setHibernateFlushMode(FlushMode flushMode); @Override + NativeQuery setQueryFlushMode(QueryFlushMode queryFlushMode); + + @Override @Deprecated(since = "7") NativeQuery setFlushMode(FlushModeType flushMode); @Override 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 ba82fa6d2b..6cf35124cb 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/Query.java +++ b/hibernate-core/src/main/java/org/hibernate/query/Query.java @@ -851,9 +851,12 @@ public interface Query extends SelectionQuery, MutationQuery, TypedQuery setHibernateFlushMode(FlushMode flushMode); + @Override + Query setQueryFlushMode(QueryFlushMode queryFlushMode); + @Override Query setCacheable(boolean cacheable); @@ -914,7 +917,7 @@ public interface Query extends SelectionQuery, MutationQuery, TypedQuery disableFetchProfile(String profileName); - @Override + @Override @Deprecated(since = "7") Query setFlushMode(FlushModeType flushMode); @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/QueryFlushMode.java b/hibernate-core/src/main/java/org/hibernate/query/QueryFlushMode.java new file mode 100644 index 0000000000..46b919b7db --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/QueryFlushMode.java @@ -0,0 +1,42 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.query; + +/** + * Enumerates the possible flush modes for execution of a + * {@link org.hibernate.query.Query}. An explicitly-specified + * {@linkplain Query#setQueryFlushMode(QueryFlushMode) + * query-level flush mode} overrides the current + * {@linkplain org.hibernate.Session#getHibernateFlushMode() + * flush mode of the session}. + * + * @since 7.0 + * + * @see CommonQueryContract#setQueryFlushMode(QueryFlushMode) + * @see org.hibernate.annotations.NamedQuery#flush + * @see org.hibernate.annotations.NamedNativeQuery#flush + * + * @author Gavin King + */ +public enum QueryFlushMode { + /** + * Flush before executing the query. + */ + FLUSH, + /** + * Do not flush before executing the query. + */ + NO_FLUSH, + /** + * Let the owning {@link org.hibernate.Session session} + * decide whether to flush, depending on its current + * {@link org.hibernate.FlushMode}. + * + * @see org.hibernate.Session#getFlushMode() + */ + DEFAULT +} 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 5bdd7eff2d..b79789a453 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/SelectionQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/SelectionQuery.java @@ -295,12 +295,15 @@ public interface SelectionQuery extends CommonQueryContract { */ SelectionQuery disableFetchProfile(String profileName); - @Override + @Override @Deprecated(since = "7") SelectionQuery setFlushMode(FlushModeType flushMode); - @Override + @Override @Deprecated(since = "7") SelectionQuery setHibernateFlushMode(FlushMode flushMode); + @Override + SelectionQuery setQueryFlushMode(QueryFlushMode queryFlushMode); + @Override SelectionQuery setTimeout(int timeout); @@ -421,14 +424,13 @@ public interface SelectionQuery extends CommonQueryContract { * the query inherits the {@link CacheMode} of the session from which * it originates. *

- * The {@link CacheMode} here describes reading-from/writing-to the - * entity/collection caches as we process query results. For caching - * of the actual query results, see {@link #isCacheable()} and - * {@link #getCacheRegion()} + * The {@link CacheMode} here affects the use of entity and collection + * caches as the query result set is processed. For caching of the actual + * query results, use {@link #isCacheable()} and {@link #getCacheRegion()}. *

* In order for this setting to have any affect, second-level caching - * would have to be enabled and the entities/collections in question - * configured for caching. + * must be enabled and the entities and collections must be eligible + * for storage in the second-level cache. * * @see Session#getCacheMode() */ @@ -450,9 +452,9 @@ public interface SelectionQuery extends CommonQueryContract { /** * Set the current {@link CacheMode} in effect for this query. - * - * @implNote Setting it to {@code null} ultimately indicates to use the - * {@code CacheMode} of the session. + *

+ * Set it to {@code null} to indicate that the {@code CacheMode} + * of the {@link Session#getCacheMode() session} should be used. * * @see #getCacheMode() * @see Session#setCacheMode(CacheMode) diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/spi/SqmQueryImplementor.java b/hibernate-core/src/main/java/org/hibernate/query/hql/spi/SqmQueryImplementor.java index 48ccfd244d..e2229853bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/spi/SqmQueryImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/spi/SqmQueryImplementor.java @@ -14,6 +14,7 @@ import java.util.Map; import org.hibernate.CacheMode; import org.hibernate.FlushMode; +import org.hibernate.query.QueryFlushMode; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.graph.GraphSemantic; @@ -104,9 +105,12 @@ public interface SqmQueryImplementor extends QueryImplementor, SqmQuery, N return setTupleTransformer( transformer ).setResultListTransformer( transformer ); } - @Override + @Override @Deprecated(since = "7") SqmQueryImplementor setHibernateFlushMode(FlushMode flushMode); + @Override + SqmQueryImplementor setQueryFlushMode(QueryFlushMode queryFlushMode); + @Override SqmQueryImplementor setMaxResults(int maxResult); @@ -116,7 +120,7 @@ public interface SqmQueryImplementor extends QueryImplementor, SqmQuery, N @Override SqmQueryImplementor setHint(String hintName, Object value); - @Override + @Override @Deprecated(since = "7") SqmQueryImplementor setFlushMode(FlushModeType flushMode); @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java index 4ad3c56f15..2071d2b31e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java @@ -17,6 +17,7 @@ import java.util.Set; import org.hibernate.CacheMode; import org.hibernate.FlushMode; +import org.hibernate.query.QueryFlushMode; import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; @@ -178,7 +179,7 @@ public abstract class AbstractCommonQueryContract implements CommonQueryContract } putIfNotNull( hints, HINT_COMMENT, getComment() ); - putIfNotNull( hints, HINT_FLUSH_MODE, getHibernateFlushMode() ); + putIfNotNull( hints, HINT_FLUSH_MODE, getQueryOptions().getFlushMode() ); putIfNotNull( hints, HINT_READONLY, getQueryOptions().isReadOnly() ); putIfNotNull( hints, HINT_FETCH_SIZE, getQueryOptions().getFetchSize() ); @@ -521,7 +522,8 @@ public abstract class AbstractCommonQueryContract implements CommonQueryContract @Override public FlushMode getHibernateFlushMode() { - return getQueryOptions().getFlushMode(); + final FlushMode flushMode = getQueryOptions().getFlushMode(); + return flushMode == null ? getSession().getHibernateFlushMode() : flushMode; } @Override @@ -530,6 +532,17 @@ public abstract class AbstractCommonQueryContract implements CommonQueryContract return this; } + @Override + public QueryFlushMode getQueryFlushMode() { + return FlushModeTypeHelper.getForcedFlushMode( getQueryOptions().getFlushMode() ); + } + + @Override + public CommonQueryContract setQueryFlushMode(QueryFlushMode queryFlushMode) { + getQueryOptions().setFlushMode( FlushModeTypeHelper.getFlushMode(queryFlushMode) ); + return this; + } + protected boolean applyJpaCacheRetrieveModeHint(CacheRetrieveMode retrieveMode) { getQueryOptions().setCacheRetrieveMode( retrieveMode ); return true; 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 5bf155f2ac..ab954dbfa8 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 @@ -27,6 +27,7 @@ import jakarta.persistence.TemporalType; import org.hibernate.CacheMode; import org.hibernate.FlushMode; +import org.hibernate.query.QueryFlushMode; import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; @@ -194,18 +195,21 @@ public abstract class AbstractQuery return this; } + @Override + public QueryImplementor setQueryFlushMode(QueryFlushMode queryFlushMode) { + super.setQueryFlushMode(queryFlushMode); + return this; + } + @Override public FlushModeType getFlushMode() { - getSession().checkOpen(); - final FlushMode flushMode = getQueryOptions().getFlushMode() == null - ? getSession().getHibernateFlushMode() - : getQueryOptions().getFlushMode(); - return FlushModeTypeHelper.getFlushModeType( flushMode ); +// getSession().checkOpen(); + return FlushModeTypeHelper.getFlushModeType( getHibernateFlushMode() ); } @Override public QueryImplementor setFlushMode(FlushModeType flushModeType) { - getSession().checkOpen(); +// getSession().checkOpen(); setHibernateFlushMode( FlushModeTypeHelper.getFlushMode( flushModeType ) ); return this; } @@ -366,7 +370,7 @@ public abstract class AbstractQuery putIfNotNull( hints, HINT_COMMENT, getComment() ); putIfNotNull( hints, HINT_FETCH_SIZE, getQueryOptions().getFetchSize() ); - putIfNotNull( hints, HINT_FLUSH_MODE, getHibernateFlushMode() ); + putIfNotNull( hints, HINT_FLUSH_MODE, getQueryOptions().getFlushMode() ); if ( getCacheMode() != null ) { putIfNotNull( hints, HINT_CACHE_MODE, getCacheMode() ); 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 ebf4653eed..dddfbb81e5 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 @@ -20,6 +20,7 @@ import java.util.stream.StreamSupport; import org.hibernate.CacheMode; import org.hibernate.FlushMode; +import org.hibernate.query.QueryFlushMode; import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; @@ -31,6 +32,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.spi.AppliedGraph; import org.hibernate.graph.spi.RootGraphImplementor; +import org.hibernate.jpa.internal.util.FlushModeTypeHelper; import org.hibernate.jpa.internal.util.LockModeTypeHelper; import org.hibernate.query.BindableType; import org.hibernate.query.IllegalQueryOperationException; @@ -52,7 +54,6 @@ import jakarta.persistence.TemporalType; import static java.util.Spliterators.spliteratorUnknownSize; import static org.hibernate.CacheMode.fromJpaModes; -import static org.hibernate.FlushMode.fromJpaFlushMode; import static org.hibernate.cfg.AvailableSettings.JAKARTA_SHARED_CACHE_RETRIEVE_MODE; import static org.hibernate.cfg.AvailableSettings.JAKARTA_SHARED_CACHE_STORE_MODE; import static org.hibernate.cfg.AvailableSettings.JPA_SHARED_CACHE_RETRIEVE_MODE; @@ -167,7 +168,7 @@ public abstract class AbstractSelectionQuery assert sessionFlushMode == null; assert sessionCacheMode == null; - final FlushMode effectiveFlushMode = getHibernateFlushMode(); + final FlushMode effectiveFlushMode = getQueryOptions().getFlushMode(); if ( effectiveFlushMode != null ) { sessionFlushMode = session.getHibernateFlushMode(); session.setHibernateFlushMode( effectiveFlushMode ); @@ -333,12 +334,12 @@ public abstract class AbstractSelectionQuery @Override public FlushModeType getFlushMode() { - return getQueryOptions().getFlushMode().toJpaFlushMode(); + return FlushModeTypeHelper.getFlushModeType( getHibernateFlushMode() ); } @Override public SelectionQuery setFlushMode(FlushModeType flushMode) { - getQueryOptions().setFlushMode( fromJpaFlushMode( flushMode ) ); + getQueryOptions().setFlushMode( FlushModeTypeHelper.getFlushMode( flushMode ) ); return this; } @@ -566,6 +567,12 @@ public abstract class AbstractSelectionQuery return this; } + @Override + public SelectionQuery setQueryFlushMode(QueryFlushMode queryFlushMode) { + super.setQueryFlushMode(queryFlushMode); + return this; + } + @Override public SelectionQuery setTimeout(int timeout) { super.setTimeout( timeout ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/SqmQuery.java b/hibernate-core/src/main/java/org/hibernate/query/spi/SqmQuery.java index 4c3a09850d..580f953c63 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/SqmQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/SqmQuery.java @@ -13,6 +13,7 @@ import java.util.Date; import java.util.Map; import org.hibernate.FlushMode; +import org.hibernate.query.QueryFlushMode; import org.hibernate.query.BindableType; import org.hibernate.query.CommonQueryContract; import org.hibernate.query.ParameterMetadata; @@ -151,6 +152,9 @@ public interface SqmQuery extends CommonQueryContract { @Override SqmQuery setProperties(@SuppressWarnings("rawtypes") Map bean); - @Override + @Override @Deprecated(since = "7") SqmQuery setHibernateFlushMode(FlushMode flushMode); + + @Override + SqmQuery setQueryFlushMode(QueryFlushMode queryFlushMode); } 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 80fde787f0..6eb5bad550 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 @@ -22,6 +22,7 @@ import java.util.function.Supplier; import org.hibernate.CacheMode; import org.hibernate.FlushMode; +import org.hibernate.query.QueryFlushMode; import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; @@ -55,7 +56,6 @@ import org.hibernate.query.TupleTransformer; import org.hibernate.query.internal.DelegatingDomainQueryExecutionContext; import org.hibernate.query.internal.ParameterMetadataImpl; import org.hibernate.query.internal.QueryOptionsImpl; -import org.hibernate.query.internal.QueryParameterBindingsImpl; import org.hibernate.query.internal.ResultSetMappingResolutionContext; import org.hibernate.query.named.NamedResultSetMappingMemento; import org.hibernate.query.results.Builders; @@ -469,7 +469,7 @@ public class NativeQueryImpl isCacheable(), getCacheRegion(), getCacheMode(), - getHibernateFlushMode(), + getQueryOptions().getFlushMode(), isReadOnly(), getTimeout(), getFetchSize(), @@ -607,7 +607,7 @@ public class NativeQueryImpl private boolean shouldFlush() { if ( getSession().isTransactionInProgress() ) { - FlushMode effectiveFlushMode = getHibernateFlushMode(); + FlushMode effectiveFlushMode = getQueryOptions().getFlushMode(); if ( effectiveFlushMode == null ) { effectiveFlushMode = getSession().getHibernateFlushMode(); } @@ -1180,6 +1180,12 @@ public class NativeQueryImpl return this; } + @Override + public NativeQueryImplementor setQueryFlushMode(QueryFlushMode queryFlushMode) { + super.setQueryFlushMode(queryFlushMode); + return this; + } + @Override public NativeQueryImplementor setFlushMode(FlushModeType flushModeType) { super.setFlushMode( flushModeType ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NativeQueryImplementor.java b/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NativeQueryImplementor.java index 5f2150e040..73f166f48b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NativeQueryImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NativeQueryImplementor.java @@ -14,6 +14,7 @@ import java.util.Map; import org.hibernate.CacheMode; import org.hibernate.FlushMode; +import org.hibernate.query.QueryFlushMode; import org.hibernate.Incubating; import org.hibernate.LockMode; import org.hibernate.LockOptions; @@ -148,10 +149,13 @@ public interface NativeQueryImplementor extends QueryImplementor, NativeQu @Override NativeQueryImplementor setHint(String hintName, Object value); - @Override + @Override @Deprecated(since = "7") NativeQueryImplementor setHibernateFlushMode(FlushMode flushMode); @Override + NativeQueryImplementor setQueryFlushMode(QueryFlushMode queryFlushMode); + + @Override @Deprecated(since = "7") NativeQueryImplementor setFlushMode(FlushModeType flushMode); @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmSelectionQuery.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmSelectionQuery.java index 0b5c20144e..677d569f98 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmSelectionQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmSelectionQuery.java @@ -14,6 +14,7 @@ import java.util.Map; import org.hibernate.CacheMode; import org.hibernate.FlushMode; +import org.hibernate.query.QueryFlushMode; import org.hibernate.query.BindableType; import org.hibernate.query.QueryParameter; import org.hibernate.query.SelectionQuery; @@ -141,9 +142,12 @@ public interface SqmSelectionQuery extends SqmQuery, SelectionQuery { @Override SqmSelectionQuery setProperties(@SuppressWarnings("rawtypes") Map bean); - @Override + @Override @Deprecated(since = "7") SqmSelectionQuery setHibernateFlushMode(FlushMode flushMode); + @Override + SqmSelectionQuery setQueryFlushMode(QueryFlushMode queryFlushMode); + @Override SqmSelectionQuery setCacheMode(CacheMode cacheMode); 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 0851508cdf..037b89a31a 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 @@ -21,6 +21,7 @@ import java.util.function.Supplier; import jakarta.persistence.EntityGraph; import org.hibernate.CacheMode; import org.hibernate.FlushMode; +import org.hibernate.query.QueryFlushMode; import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; @@ -35,8 +36,6 @@ import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.id.BulkInsertionCapableIdentifierGenerator; import org.hibernate.id.OptimizableGenerator; import org.hibernate.id.enhanced.Optimizer; -import org.hibernate.internal.CoreLogging; -import org.hibernate.internal.CoreMessageLogger; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping; import org.hibernate.metamodel.model.domain.EntityDomainType; @@ -123,7 +122,6 @@ import static org.hibernate.query.sqm.internal.SqmUtil.verifyIsNonSelectStatemen public class QuerySqmImpl extends AbstractSqmSelectionQuery implements SqmQueryImplementor, InterpretationsKeySource, DomainQueryExecutionContext { - private static final CoreMessageLogger LOG = CoreLogging.messageLogger( QuerySqmImpl.class ); private final String hql; private SqmStatement sqm; @@ -733,6 +731,12 @@ public class QuerySqmImpl return this; } + @Override + public SqmQueryImplementor setQueryFlushMode(QueryFlushMode queryFlushMode) { + super.setQueryFlushMode(queryFlushMode); + return this; + } + @Override public SqmQueryImplementor setFlushMode(FlushModeType flushMode) { applyJpaFlushMode( flushMode ); @@ -909,7 +913,7 @@ public class QuerySqmImpl isCacheable(), getCacheRegion(), getCacheMode(), - getHibernateFlushMode(), + getQueryOptions().getFlushMode(), isReadOnly(), getLockOptions(), getTimeout(), @@ -929,7 +933,7 @@ public class QuerySqmImpl isCacheable(), getCacheRegion(), getCacheMode(), - getHibernateFlushMode(), + getQueryOptions().getFlushMode(), isReadOnly(), getLockOptions(), getTimeout(), 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 67921591cd..a493fd62df 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 @@ -24,6 +24,7 @@ import jakarta.persistence.TemporalType; import org.hibernate.CacheMode; import org.hibernate.FlushMode; +import org.hibernate.query.QueryFlushMode; import org.hibernate.LockMode; import org.hibernate.ScrollMode; import org.hibernate.engine.spi.LoadQueryInfluencers; @@ -551,6 +552,12 @@ public class SqmSelectionQueryImpl extends AbstractSqmSelectionQuery return this; } + @Override + public SqmSelectionQuery setQueryFlushMode(QueryFlushMode queryFlushMode) { + super.setQueryFlushMode(queryFlushMode); + return this; + } + @Override public SqmSelectionQuery setTimeout(int timeout) { super.setTimeout( timeout ); 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 bbd39da456..1c5ba392e9 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 @@ -29,8 +29,10 @@ import org.hibernate.query.KeyedResultList; import org.hibernate.query.Order; import org.hibernate.query.Page; import org.hibernate.query.ParameterMetadata; +import org.hibernate.query.QueryFlushMode; import org.hibernate.query.QueryParameter; import org.hibernate.query.spi.QueryOptions; +import org.hibernate.query.sqm.SqmSelectionQuery; import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.sql.results.spi.ResultsConsumer; @@ -63,6 +65,16 @@ public abstract class DelegatingSqmSelectionQueryImplementor implements SqmSe return getDelegate().getHibernateFlushMode(); } + @Override + public QueryFlushMode getQueryFlushMode() { + return getDelegate().getQueryFlushMode(); + } + + @Override + public SqmSelectionQuery setQueryFlushMode(QueryFlushMode queryFlushMode) { + return getDelegate().setQueryFlushMode( queryFlushMode ); + } + @Override public Integer getTimeout() { return getDelegate().getTimeout(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NamedQueryFlushModeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NamedQueryFlushModeTest.java index 8eb01a25c4..ded24d9660 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NamedQueryFlushModeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NamedQueryFlushModeTest.java @@ -25,7 +25,6 @@ import org.hibernate.testing.orm.junit.Jpa; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; /** * @author Yoann Rodiere @@ -103,22 +102,22 @@ public class NamedQueryFlushModeTest { s.setHibernateFlushMode( FlushMode.MANUAL ); query = s.getNamedQuery( queryName ); - assertNull( query.getHibernateFlushMode() ); + assertEquals( FlushMode.MANUAL, query.getHibernateFlushMode() ); assertEquals( jakarta.persistence.FlushModeType.COMMIT, query.getFlushMode() ); s.setHibernateFlushMode( FlushMode.COMMIT ); query = s.getNamedQuery( queryName ); - assertNull( query.getHibernateFlushMode() ); + assertEquals( FlushMode.COMMIT, query.getHibernateFlushMode() ); assertEquals( jakarta.persistence.FlushModeType.COMMIT, query.getFlushMode() ); s.setHibernateFlushMode( FlushMode.AUTO ); query = s.getNamedQuery( queryName ); - assertNull( query.getHibernateFlushMode() ); + assertEquals( FlushMode.AUTO, query.getHibernateFlushMode() ); assertEquals( jakarta.persistence.FlushModeType.AUTO, query.getFlushMode() ); s.setHibernateFlushMode( FlushMode.ALWAYS ); query = s.getNamedQuery( queryName ); - assertNull( query.getHibernateFlushMode() ); + assertEquals( FlushMode.ALWAYS, query.getHibernateFlushMode() ); assertEquals( jakarta.persistence.FlushModeType.AUTO, query.getFlushMode() ); } ); @@ -185,22 +184,22 @@ public class NamedQueryFlushModeTest { s.setHibernateFlushMode( FlushMode.MANUAL ); query = s.getNamedNativeQuery( queryName ); - assertNull( query.getHibernateFlushMode() ); + assertEquals( FlushMode.MANUAL, query.getHibernateFlushMode() ); assertEquals( jakarta.persistence.FlushModeType.COMMIT, query.getFlushMode() ); s.setHibernateFlushMode( FlushMode.COMMIT ); query = s.getNamedNativeQuery( queryName ); - assertNull( query.getHibernateFlushMode() ); + assertEquals( FlushMode.COMMIT, query.getHibernateFlushMode() ); assertEquals( jakarta.persistence.FlushModeType.COMMIT, query.getFlushMode() ); s.setHibernateFlushMode( FlushMode.AUTO ); query = s.getNamedNativeQuery( queryName ); - assertNull( query.getHibernateFlushMode() ); + assertEquals( FlushMode.AUTO, query.getHibernateFlushMode() ); assertEquals( jakarta.persistence.FlushModeType.AUTO, query.getFlushMode() ); s.setHibernateFlushMode( FlushMode.ALWAYS ); query = s.getNamedNativeQuery( queryName ); - assertNull( query.getHibernateFlushMode() ); + assertEquals( FlushMode.ALWAYS, query.getHibernateFlushMode() ); assertEquals( jakarta.persistence.FlushModeType.AUTO, query.getFlushMode() ); } ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NamedQueryQueryFlushModeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NamedQueryQueryFlushModeTest.java new file mode 100644 index 0000000000..a6e1644bd5 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NamedQueryQueryFlushModeTest.java @@ -0,0 +1,293 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.orm.test.jpa.query; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import org.hibernate.FlushMode; +import org.hibernate.query.QueryFlushMode; +import org.hibernate.Session; +import org.hibernate.annotations.NamedNativeQuery; +import org.hibernate.annotations.NamedQuery; +import org.hibernate.query.NativeQuery; +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.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Yoann Rodiere + */ +@JiraKey("HHH-12795") +@Jpa(annotatedClasses = { + NamedQueryQueryFlushModeTest.TestEntity.class +}) +public class NamedQueryQueryFlushModeTest { + + @Test + public void testNamedQueryWithFlushModeManual(EntityManagerFactoryScope scope) { + String queryName = "NamedQueryFlushModeManual"; + scope.inEntityManager( + entityManager -> { + Session s = entityManager.unwrap( Session.class ); + Query query = s.getNamedQuery( queryName ); + assertEquals( FlushMode.MANUAL, query.getHibernateFlushMode() ); + // JPA flush mode is an approximation + assertEquals( jakarta.persistence.FlushModeType.COMMIT, query.getFlushMode() ); + } + ); + } + + @Test + public void testNamedQueryWithFlushModeCommit(EntityManagerFactoryScope scope) { + String queryName = "NamedQueryFlushModeCommit"; + scope.inEntityManager( + entityManager -> { + Session s = entityManager.unwrap( Session.class ); + Query query = s.getNamedQuery( queryName ); + assertEquals( FlushMode.MANUAL, query.getHibernateFlushMode() ); + assertEquals( jakarta.persistence.FlushModeType.COMMIT, query.getFlushMode() ); + } + ); + } + + @Test + public void testNamedQueryWithFlushModeAuto(EntityManagerFactoryScope scope) { + String queryName = "NamedQueryFlushModeAuto"; + scope.inEntityManager( + entityManager -> { + Session s = entityManager.unwrap( Session.class ); + Query query = s.getNamedQuery( queryName ); + assertEquals( FlushMode.AUTO, query.getHibernateFlushMode() ); + assertEquals( jakarta.persistence.FlushModeType.AUTO, query.getFlushMode() ); + } + ); + } + + @Test + public void testNamedQueryWithFlushModeAlways(EntityManagerFactoryScope scope) { + String queryName = "NamedQueryFlushModeAlways"; + scope.inEntityManager( + entityManager -> { + Session s = entityManager.unwrap( Session.class ); + Query query = s.getNamedQuery( queryName ); + assertEquals( FlushMode.ALWAYS, query.getHibernateFlushMode() ); + // JPA flush mode is an approximation + assertEquals( jakarta.persistence.FlushModeType.AUTO, query.getFlushMode() ); + } + ); + } + + @Test + public void testNamedQueryWithFlushModePersistenceContext(EntityManagerFactoryScope scope) { + String queryName = "NamedQueryFlushModePersistenceContext"; + scope.inEntityManager( + entityManager -> { + Session s = entityManager.unwrap( Session.class ); + Query query; + + // A null Hibernate flush mode means we will use whatever mode is set on the session + // JPA doesn't allow null flush modes, so we expect some approximation of the flush mode to be returned + + s.setHibernateFlushMode( FlushMode.MANUAL ); + query = s.getNamedQuery( queryName ); + assertEquals( FlushMode.MANUAL, query.getHibernateFlushMode() ); + assertEquals( jakarta.persistence.FlushModeType.COMMIT, query.getFlushMode() ); + assertEquals( QueryFlushMode.DEFAULT, query.getQueryFlushMode() ); + + s.setHibernateFlushMode( FlushMode.COMMIT ); + query = s.getNamedQuery( queryName ); + assertEquals( FlushMode.COMMIT, query.getHibernateFlushMode() ); + assertEquals( jakarta.persistence.FlushModeType.COMMIT, query.getFlushMode() ); + assertEquals( QueryFlushMode.DEFAULT, query.getQueryFlushMode() ); + + s.setHibernateFlushMode( FlushMode.AUTO ); + query = s.getNamedQuery( queryName ); + assertEquals( FlushMode.AUTO, query.getHibernateFlushMode() ); + assertEquals( jakarta.persistence.FlushModeType.AUTO, query.getFlushMode() ); + assertEquals( QueryFlushMode.DEFAULT, query.getQueryFlushMode() ); + + s.setHibernateFlushMode( FlushMode.ALWAYS ); + query = s.getNamedQuery( queryName ); + assertEquals( FlushMode.ALWAYS, query.getHibernateFlushMode() ); + assertEquals( jakarta.persistence.FlushModeType.AUTO, query.getFlushMode() ); + assertEquals( QueryFlushMode.DEFAULT, query.getQueryFlushMode() ); + } + ); + } + + @Test + public void testNamedNativeQueryWithFlushModeManual(EntityManagerFactoryScope scope) { + String queryName = "NamedNativeQueryFlushModeManual"; + scope.inEntityManager( + entityManager -> { + Session s = entityManager.unwrap( Session.class ); + NativeQuery query = s.getNamedNativeQuery( queryName ); + assertEquals( FlushMode.MANUAL, query.getHibernateFlushMode() ); + assertEquals( QueryFlushMode.NO_FLUSH, query.getQueryFlushMode() ); + } + ); + } + + @Test + public void testNamedNativeQueryWithFlushModeCommit(EntityManagerFactoryScope scope) { + String queryName = "NamedNativeQueryFlushModeCommit"; + scope.inEntityManager( + entityManager -> { + Session s = entityManager.unwrap( Session.class ); + NativeQuery query = s.getNamedNativeQuery( queryName ); + assertEquals( FlushMode.MANUAL, query.getHibernateFlushMode() ); + assertEquals( QueryFlushMode.NO_FLUSH, query.getQueryFlushMode() ); + } + ); + } + + @Test + public void testNamedNativeQueryWithFlushModeAuto(EntityManagerFactoryScope scope) { + String queryName = "NamedNativeQueryFlushModeAuto"; + scope.inEntityManager( + entityManager -> { + Session s = entityManager.unwrap( Session.class ); + NativeQuery query = s.getNamedNativeQuery( queryName ); + assertEquals( FlushMode.AUTO, query.getHibernateFlushMode() ); + assertEquals( QueryFlushMode.DEFAULT, query.getQueryFlushMode() ); + } + ); + } + + @Test + public void testNamedNativeQueryWithFlushModeAlways(EntityManagerFactoryScope scope) { + String queryName = "NamedNativeQueryFlushModeAlways"; + scope.inEntityManager( + entityManager -> { + Session s = entityManager.unwrap( Session.class ); + NativeQuery query = s.getNamedNativeQuery( queryName ); + assertEquals( FlushMode.ALWAYS, query.getHibernateFlushMode() ); + assertEquals( QueryFlushMode.FLUSH, query.getQueryFlushMode() ); + } + ); + } + + @Test + public void testNamedNativeQueryWithFlushModePersistenceContext(EntityManagerFactoryScope scope) { + String queryName = "NamedNativeQueryFlushModePersistenceContext"; + scope.inEntityManager( + entityManager -> { + Session s = entityManager.unwrap( Session.class ); + NativeQuery query; + + // A null Hibernate flush mode means we will use whatever mode is set on the session + // JPA doesn't allow null flush modes, so we expect some approximation of the flush mode to be returned + + s.setHibernateFlushMode( FlushMode.MANUAL ); + query = s.getNamedNativeQuery( queryName ); + assertEquals( FlushMode.MANUAL, query.getHibernateFlushMode() ); + assertEquals( jakarta.persistence.FlushModeType.COMMIT, query.getFlushMode() ); + + s.setHibernateFlushMode( FlushMode.COMMIT ); + query = s.getNamedNativeQuery( queryName ); + assertEquals( FlushMode.COMMIT, query.getHibernateFlushMode() ); + assertEquals( jakarta.persistence.FlushModeType.COMMIT, query.getFlushMode() ); + + s.setHibernateFlushMode( FlushMode.AUTO ); + query = s.getNamedNativeQuery( queryName ); + assertEquals( FlushMode.AUTO, query.getHibernateFlushMode() ); + assertEquals( jakarta.persistence.FlushModeType.AUTO, query.getFlushMode() ); + + s.setHibernateFlushMode( FlushMode.ALWAYS ); + query = s.getNamedNativeQuery( queryName ); + assertEquals( FlushMode.ALWAYS, query.getHibernateFlushMode() ); + assertEquals( jakarta.persistence.FlushModeType.AUTO, query.getFlushMode() ); + } + ); + } + + @Entity(name = "TestEntity") + @NamedQuery( + name = "NamedQueryFlushModeManual", + query = "select e from TestEntity e where e.text = :text", + flush = QueryFlushMode.NO_FLUSH + ) + @NamedQuery( + name = "NamedQueryFlushModeCommit", + query = "select e from TestEntity e where e.text = :text", + flush = QueryFlushMode.NO_FLUSH + ) + @NamedQuery( + name = "NamedQueryFlushModeAuto", + query = "select e from TestEntity e where e.text = :text", + flush = QueryFlushMode.DEFAULT + ) + @NamedQuery( + name = "NamedQueryFlushModeAlways", + query = "select e from TestEntity e where e.text = :text", + flush = QueryFlushMode.FLUSH + ) + @NamedQuery( + name = "NamedQueryFlushModePersistenceContext", + query = "select e from TestEntity e where e.text = :text", + flush = QueryFlushMode.DEFAULT + ) + @NamedNativeQuery( + name = "NamedNativeQueryFlushModeManual", + query = "select * from TestEntity e where e.text = :text", + resultClass = TestEntity.class, + flush = QueryFlushMode.NO_FLUSH + ) + @NamedNativeQuery( + name = "NamedNativeQueryFlushModeCommit", + query = "select * from TestEntity e where e.text = :text", + resultClass = TestEntity.class, + flush = QueryFlushMode.NO_FLUSH + ) + @NamedNativeQuery( + name = "NamedNativeQueryFlushModeAuto", + query = "select * from TestEntity e where e.text = :text", + resultClass = TestEntity.class, + flush = QueryFlushMode.DEFAULT + ) + @NamedNativeQuery( + name = "NamedNativeQueryFlushModeAlways", + query = "select * from TestEntity e where e.text = :text", + resultClass = TestEntity.class, + flush = QueryFlushMode.FLUSH + ) + @NamedNativeQuery( + name = "NamedNativeQueryFlushModePersistenceContext", + query = "select * from TestEntity e where e.text = :text", + resultClass = TestEntity.class, + flush = QueryFlushMode.DEFAULT + ) + + public static class TestEntity { + @Id + @GeneratedValue + private Long id; + + private String text; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + } +}