mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-17 08:35:13 +00:00
HHH-16782 Allow query plan caching of criteria queries
This commit is contained in:
parent
97a699a3e1
commit
d859f43748
@ -61,6 +61,11 @@ public Boolean isResultCachingEnabled() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getQueryPlanCachingEnabled() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheRetrieveMode getCacheRetrieveMode() {
|
||||
return null;
|
||||
|
@ -201,4 +201,26 @@ public interface HibernateHints {
|
||||
* @see jakarta.persistence.EntityManager#setProperty(String, Object)
|
||||
*/
|
||||
String HINT_JDBC_BATCH_SIZE = "org.hibernate.jdbcBatchSize";
|
||||
|
||||
/**
|
||||
* Hint to enable or disable the query plan caching.
|
||||
* <p>
|
||||
* By default, query plan caching is enabled for HQL queries
|
||||
* and immutable criteria queries i.e. created with {@link org.hibernate.cfg.AvailableSettings#CRITERIA_COPY_TREE}.
|
||||
* Query plan caching can be disabled for any query by setting this property to {@code false}.
|
||||
* Query plan caching can be enabled for mutable criteria queries by setting this property to {@code true}.
|
||||
* <p>
|
||||
* Setting this property to {@code true} for mutable criteria queries can lead to cache trashing,
|
||||
* because the query plan is cached based on a copy of the criteria query.
|
||||
* This is mostly useful when the same {@link org.hibernate.query.Query} should be executed multiple times,
|
||||
* but with different parameter values to avoid re-translation of the criteria query.
|
||||
* <p>
|
||||
* Note that setting this property to {@code true} does not override the basic safety measures of Hibernate.
|
||||
* Hibernate will never cache query plans that are not safe to cache, regardless of the value of this property.
|
||||
*
|
||||
* @see org.hibernate.query.SelectionQuery#setQueryPlanCacheable
|
||||
*
|
||||
* @since 6.3
|
||||
*/
|
||||
String HINT_QUERY_PLAN_CACHEABLE = "hibernate.query.plan.cacheable";
|
||||
}
|
||||
|
@ -257,6 +257,11 @@ public Boolean isResultCachingEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getQueryPlanCachingEnabled() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheRetrieveMode getCacheRetrieveMode() {
|
||||
return CacheRetrieveMode.BYPASS;
|
||||
|
@ -403,6 +403,18 @@ default Stream<R> stream() {
|
||||
*/
|
||||
SelectionQuery<R> setCacheable(boolean cacheable);
|
||||
|
||||
/**
|
||||
* Should the query plan of the query be stored in the query plan cache?
|
||||
*/
|
||||
boolean isQueryPlanCacheable();
|
||||
|
||||
/**
|
||||
* Enable/disable query plan caching for this query.
|
||||
*
|
||||
* @see #isQueryPlanCacheable
|
||||
*/
|
||||
SelectionQuery<R> setQueryPlanCacheable(boolean queryPlanCacheable);
|
||||
|
||||
/**
|
||||
* Obtain the name of the second level query cache region in which query
|
||||
* results will be stored (if they are cached, see the discussion on
|
||||
|
@ -40,6 +40,7 @@ public class QueryOptionsImpl implements MutableQueryOptions, AppliedGraph {
|
||||
private Boolean resultCachingEnabled;
|
||||
private String resultCacheRegionName;
|
||||
private Boolean readOnlyEnabled;
|
||||
private Boolean queryPlanCachingEnabled;
|
||||
|
||||
private TupleTransformer tupleTransformer;
|
||||
private ResultListTransformer resultListTransformer;
|
||||
@ -61,6 +62,7 @@ public FlushMode getFlushMode() {
|
||||
return flushMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlushMode(FlushMode flushMode) {
|
||||
this.flushMode = flushMode;
|
||||
}
|
||||
@ -70,6 +72,7 @@ public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setComment(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
@ -141,6 +144,7 @@ public Boolean isResultCachingEnabled() {
|
||||
return resultCachingEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResultCachingEnabled(boolean resultCachingEnabled) {
|
||||
this.resultCachingEnabled = resultCachingEnabled;
|
||||
}
|
||||
@ -150,6 +154,16 @@ public String getResultCacheRegionName() {
|
||||
return resultCacheRegionName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getQueryPlanCachingEnabled() {
|
||||
return queryPlanCachingEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQueryPlanCachingEnabled(Boolean queryPlanCachingEnabled) {
|
||||
this.queryPlanCachingEnabled = queryPlanCachingEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TupleTransformer getTupleTransformer() {
|
||||
return tupleTransformer;
|
||||
@ -160,6 +174,7 @@ public ResultListTransformer getResultListTransformer() {
|
||||
return resultListTransformer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResultCacheRegionName(String resultCacheRegionName) {
|
||||
this.resultCacheRegionName = resultCacheRegionName;
|
||||
}
|
||||
|
@ -71,6 +71,7 @@
|
||||
import static org.hibernate.jpa.HibernateHints.HINT_FLUSH_MODE;
|
||||
import static org.hibernate.jpa.HibernateHints.HINT_FOLLOW_ON_LOCKING;
|
||||
import static org.hibernate.jpa.HibernateHints.HINT_NATIVE_SPACES;
|
||||
import static org.hibernate.jpa.HibernateHints.HINT_QUERY_PLAN_CACHEABLE;
|
||||
import static org.hibernate.jpa.HibernateHints.HINT_TIMEOUT;
|
||||
import static org.hibernate.jpa.LegacySpecHints.HINT_JAVAEE_CACHE_RETRIEVE_MODE;
|
||||
import static org.hibernate.jpa.LegacySpecHints.HINT_JAVAEE_CACHE_STORE_MODE;
|
||||
@ -182,6 +183,7 @@ protected void collectHints(Map<String, Object> hints) {
|
||||
putIfNotNull( hints, HINT_CACHEABLE, getQueryOptions().isResultCachingEnabled() );
|
||||
putIfNotNull( hints, HINT_CACHE_REGION, getQueryOptions().getResultCacheRegionName() );
|
||||
putIfNotNull( hints, HINT_CACHE_MODE, getQueryOptions().getCacheMode() );
|
||||
putIfNotNull( hints, HINT_QUERY_PLAN_CACHEABLE, getQueryOptions().getQueryPlanCachingEnabled() );
|
||||
|
||||
putIfNotNull( hints, HINT_SPEC_CACHE_RETRIEVE_MODE, getQueryOptions().getCacheRetrieveMode() );
|
||||
putIfNotNull( hints, HINT_JAVAEE_CACHE_RETRIEVE_MODE, getQueryOptions().getCacheRetrieveMode() );
|
||||
@ -299,6 +301,9 @@ protected final boolean applySelectionHint(String hintName, Object value) {
|
||||
case HINT_FETCH_SIZE:
|
||||
applyFetchSizeHint( getInteger( value ) );
|
||||
return true;
|
||||
case HINT_QUERY_PLAN_CACHEABLE:
|
||||
applyQueryPlanCacheableHint( getBoolean( value ) );
|
||||
return true;
|
||||
case HINT_CACHEABLE:
|
||||
applyCacheableHint( getBoolean( value ) );
|
||||
return true;
|
||||
@ -343,6 +348,10 @@ protected void applyFetchSizeHint(int fetchSize) {
|
||||
getQueryOptions().setFetchSize( fetchSize );
|
||||
}
|
||||
|
||||
protected void applyQueryPlanCacheableHint(boolean isCacheable) {
|
||||
getQueryOptions().setQueryPlanCachingEnabled( isCacheable );
|
||||
}
|
||||
|
||||
protected void applyCacheModeHint(CacheMode cacheMode) {
|
||||
getQueryOptions().setCacheMode( cacheMode );
|
||||
}
|
||||
|
@ -37,6 +37,7 @@
|
||||
import org.hibernate.query.IllegalQueryOperationException;
|
||||
import org.hibernate.query.QueryParameter;
|
||||
import org.hibernate.query.ResultListTransformer;
|
||||
import org.hibernate.query.SelectionQuery;
|
||||
import org.hibernate.query.TupleTransformer;
|
||||
import org.hibernate.query.named.NamedQueryMemento;
|
||||
import org.hibernate.query.sqm.SqmExpressible;
|
||||
@ -221,6 +222,12 @@ public QueryImplementor<R> setCacheRegion(String cacheRegion) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryImplementor<R> setQueryPlanCacheable(boolean queryPlanCacheable) {
|
||||
super.setQueryPlanCacheable( queryPlanCacheable );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryImplementor<R> setTimeout(int timeout) {
|
||||
super.setTimeout( timeout );
|
||||
|
@ -804,6 +804,18 @@ public SelectionQuery<R> setCacheable(boolean cacheable) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isQueryPlanCacheable() {
|
||||
// By default, we assume query plan caching is enabled unless explicitly disabled
|
||||
return getQueryOptions().getQueryPlanCachingEnabled() != Boolean.FALSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelectionQuery<R> setQueryPlanCacheable(boolean queryPlanCacheable) {
|
||||
getQueryOptions().setQueryPlanCachingEnabled( queryPlanCacheable );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCacheRegion() {
|
||||
return getQueryOptions().getResultCacheRegionName();
|
||||
|
@ -74,6 +74,11 @@ public CacheStoreMode getCacheStoreMode() {
|
||||
return queryOptions.getCacheStoreMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getQueryPlanCachingEnabled() {
|
||||
return queryOptions.getQueryPlanCachingEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheMode getCacheMode() {
|
||||
return queryOptions.getCacheMode();
|
||||
|
@ -59,6 +59,11 @@ default void setCacheMode(CacheMode cacheMode) {
|
||||
*/
|
||||
void setResultCacheRegionName(String cacheRegion);
|
||||
|
||||
/**
|
||||
* Corollary to {@link #getQueryPlanCachingEnabled()}
|
||||
*/
|
||||
void setQueryPlanCachingEnabled(Boolean queryPlanCachingEnabled);
|
||||
|
||||
/**
|
||||
* Corollary to {@link #getTimeout()}
|
||||
*/
|
||||
|
@ -99,6 +99,11 @@ default CacheMode getCacheMode() {
|
||||
*/
|
||||
String getResultCacheRegionName();
|
||||
|
||||
/**
|
||||
* Should the query plan of the query be cached?
|
||||
*/
|
||||
Boolean getQueryPlanCachingEnabled();
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// JDBC / SQL options
|
||||
|
@ -80,6 +80,11 @@ public Boolean isResultCachingEnabled() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getQueryPlanCachingEnabled() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResultCacheRegionName() {
|
||||
return null;
|
||||
|
@ -1149,6 +1149,12 @@ public NativeQueryImplementor<R> setCacheRegion(String cacheRegion) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeQueryImplementor<R> setQueryPlanCacheable(boolean queryPlanCacheable) {
|
||||
super.setQueryPlanCacheable( queryPlanCacheable );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeQueryImplementor<R> setTimeout(int timeout) {
|
||||
super.setTimeout( timeout );
|
||||
|
@ -227,6 +227,8 @@ public QuerySqmImpl(
|
||||
}
|
||||
else {
|
||||
this.sqm = criteria;
|
||||
// Cache immutable query plans by default
|
||||
setQueryPlanCacheable( true );
|
||||
}
|
||||
|
||||
setComment( hql );
|
||||
@ -436,7 +438,8 @@ public String getQueryString() {
|
||||
return hql;
|
||||
}
|
||||
|
||||
public SqmStatement getSqmStatement() {
|
||||
@Override
|
||||
public SqmStatement<R> getSqmStatement() {
|
||||
return sqm;
|
||||
}
|
||||
|
||||
@ -633,6 +636,14 @@ protected ScrollableResultsImplementor doScroll(ScrollMode scrollMode) {
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Select query plan
|
||||
|
||||
@Override
|
||||
public boolean isQueryPlanCacheable() {
|
||||
return CRITERIA_HQL_STRING.equals( hql )
|
||||
// For criteria queries, query plan caching requires an explicit opt-in
|
||||
? getQueryOptions().getQueryPlanCachingEnabled() == Boolean.TRUE
|
||||
: super.isQueryPlanCacheable();
|
||||
}
|
||||
|
||||
private SelectQueryPlan<R> resolveSelectQueryPlan() {
|
||||
final QueryInterpretationCache.Key cacheKey = SqmInterpretationsKey.createInterpretationsKey( this );
|
||||
if ( cacheKey != null ) {
|
||||
@ -1202,6 +1213,12 @@ public SqmQueryImplementor<R> setCacheRegion(String cacheRegion) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmQueryImplementor<R> setQueryPlanCacheable(boolean queryPlanCacheable) {
|
||||
super.setQueryPlanCacheable( queryPlanCacheable );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmQueryImplementor<R> setTimeout(int timeout) {
|
||||
super.setTimeout( timeout );
|
||||
|
@ -16,6 +16,7 @@
|
||||
import org.hibernate.query.TupleTransformer;
|
||||
import org.hibernate.query.spi.QueryInterpretationCache;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.query.sqm.tree.SqmStatement;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static org.hibernate.query.spi.AbstractSelectionQuery.CRITERIA_HQL_STRING;
|
||||
@ -25,7 +26,9 @@
|
||||
*/
|
||||
public class SqmInterpretationsKey implements QueryInterpretationCache.Key {
|
||||
public interface CacheabilityInfluencers {
|
||||
boolean isQueryPlanCacheable();
|
||||
String getQueryString();
|
||||
SqmStatement<?> getSqmStatement();
|
||||
QueryOptions getQueryOptions();
|
||||
LoadQueryInfluencers getLoadQueryInfluencers();
|
||||
Supplier<Boolean> hasMultiValuedParameterBindingsChecker();
|
||||
@ -36,9 +39,11 @@ public interface InterpretationsKeySource extends CacheabilityInfluencers {
|
||||
}
|
||||
|
||||
public static SqmInterpretationsKey createInterpretationsKey(InterpretationsKeySource keySource) {
|
||||
if ( isCacheable (keySource ) ) {
|
||||
if ( isCacheable ( keySource ) ) {
|
||||
return new SqmInterpretationsKey(
|
||||
keySource.getQueryString(),
|
||||
CRITERIA_HQL_STRING.equals( keySource.getQueryString() )
|
||||
? keySource.getSqmStatement()
|
||||
: keySource.getQueryString(),
|
||||
keySource.getResultType(),
|
||||
keySource.getQueryOptions().getLockOptions(),
|
||||
keySource.getQueryOptions().getTupleTransformer(),
|
||||
@ -57,7 +62,7 @@ private static boolean isCacheable(InterpretationsKeySource keySource) {
|
||||
// for now at least, skip caching Criteria-based plans
|
||||
// - especially wrt parameters atm; this works with HQL because the
|
||||
// parameters are part of the query string; with Criteria, they're not.
|
||||
return ! CRITERIA_HQL_STRING.equals( keySource.getQueryString() )
|
||||
return keySource.isQueryPlanCacheable()
|
||||
// At the moment we cannot cache query plan if there is filter enabled.
|
||||
&& ! keySource.getLoadQueryInfluencers().hasEnabledFilters()
|
||||
// At the moment we cannot cache query plan if it has an entity graph
|
||||
@ -79,7 +84,7 @@ public static QueryInterpretationCache.Key generateNonSelectKey(InterpretationsK
|
||||
return null;
|
||||
}
|
||||
|
||||
private final String query;
|
||||
private final Object query;
|
||||
private final Class<?> resultType;
|
||||
private final LockOptions lockOptions;
|
||||
private final TupleTransformer<?> tupleTransformer;
|
||||
@ -87,7 +92,7 @@ public static QueryInterpretationCache.Key generateNonSelectKey(InterpretationsK
|
||||
private final Collection<String> enabledFetchProfiles;
|
||||
|
||||
private SqmInterpretationsKey(
|
||||
String query,
|
||||
Object query,
|
||||
Class<?> resultType,
|
||||
LockOptions lockOptions,
|
||||
TupleTransformer<?> tupleTransformer,
|
||||
@ -116,7 +121,7 @@ public QueryInterpretationCache.Key prepareForStore() {
|
||||
|
||||
@Override
|
||||
public String getQueryString() {
|
||||
return query;
|
||||
return query instanceof String ? (String) query : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,6 +54,7 @@
|
||||
import org.hibernate.query.spi.SelectQueryPlan;
|
||||
import org.hibernate.query.sqm.SqmSelectionQuery;
|
||||
import org.hibernate.query.sqm.internal.SqmInterpretationsKey.InterpretationsKeySource;
|
||||
import org.hibernate.query.sqm.tree.SqmCopyContext;
|
||||
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||
@ -195,7 +196,14 @@ public SqmSelectionQueryImpl(
|
||||
SharedSessionContractImplementor session) {
|
||||
super( session );
|
||||
this.hql = CRITERIA_HQL_STRING;
|
||||
this.sqm = session.isCriteriaCopyTreeEnabled() ? criteria.copy( simpleContext() ) : criteria;
|
||||
if ( session.isCriteriaCopyTreeEnabled() ) {
|
||||
this.sqm = criteria.copy( SqmCopyContext.simpleContext() );
|
||||
}
|
||||
else {
|
||||
this.sqm = criteria;
|
||||
// Cache immutable query plans by default
|
||||
setQueryPlanCacheable( true );
|
||||
}
|
||||
|
||||
this.domainParameterXref = DomainParameterXref.from( sqm );
|
||||
this.parameterMetadata = domainParameterXref.hasParameters()
|
||||
@ -233,8 +241,8 @@ public TupleMetadata getTupleMetadata() {
|
||||
return tupleMetadata;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public SqmSelectStatement getSqmStatement() {
|
||||
@Override
|
||||
public SqmSelectStatement<R> getSqmStatement() {
|
||||
return sqm;
|
||||
}
|
||||
|
||||
@ -524,6 +532,20 @@ public SqmSelectionQuery<R> setCacheRegion(String regionName) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmSelectionQuery<R> setQueryPlanCacheable(boolean queryPlanCacheable) {
|
||||
super.setQueryPlanCacheable( queryPlanCacheable );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isQueryPlanCacheable() {
|
||||
return CRITERIA_HQL_STRING.equals( hql )
|
||||
// For criteria queries, query plan caching requires an explicit opt-in
|
||||
? getQueryOptions().getQueryPlanCachingEnabled() == Boolean.TRUE
|
||||
: super.isQueryPlanCacheable();
|
||||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// hints
|
||||
|
@ -743,6 +743,11 @@ public Boolean isResultCachingEnabled() {
|
||||
return resultCachingEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getQueryPlanCachingEnabled() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheRetrieveMode getCacheRetrieveMode() {
|
||||
return cacheRetrieveMode;
|
||||
|
@ -9,6 +9,7 @@
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.internal.util.ExceptionHelper;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
@ -32,8 +33,10 @@
|
||||
|
||||
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.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -58,6 +61,7 @@
|
||||
}
|
||||
)
|
||||
@SessionFactory
|
||||
@ServiceRegistry(settings = @Setting(name = AvailableSettings.CRITERIA_COPY_TREE, value = "true"))
|
||||
public class TransientOverrideAsPersistentJoined {
|
||||
|
||||
@Test
|
||||
|
@ -7,6 +7,9 @@
|
||||
package org.hibernate.orm.test.inheritance;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.ConstraintMode;
|
||||
import jakarta.persistence.DiscriminatorColumn;
|
||||
@ -29,8 +32,10 @@
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
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.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -53,6 +58,7 @@
|
||||
}
|
||||
)
|
||||
@SessionFactory
|
||||
@ServiceRegistry(settings = @Setting(name = AvailableSettings.CRITERIA_COPY_TREE, value = "true"))
|
||||
public class TransientOverrideAsPersistentMappedSuperclass {
|
||||
|
||||
@Test
|
||||
|
@ -7,6 +7,9 @@
|
||||
package org.hibernate.orm.test.inheritance;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.ConstraintMode;
|
||||
import jakarta.persistence.DiscriminatorColumn;
|
||||
@ -28,8 +31,10 @@
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
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.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -51,6 +56,7 @@
|
||||
}
|
||||
)
|
||||
@SessionFactory
|
||||
@ServiceRegistry(settings = @Setting(name = AvailableSettings.CRITERIA_COPY_TREE, value = "true"))
|
||||
public class TransientOverrideAsPersistentSingleTable {
|
||||
|
||||
@Test
|
||||
|
@ -8,6 +8,9 @@
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.ConstraintMode;
|
||||
import jakarta.persistence.DiscriminatorColumn;
|
||||
@ -29,8 +32,10 @@
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
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.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -52,6 +57,7 @@
|
||||
}
|
||||
)
|
||||
@SessionFactory
|
||||
@ServiceRegistry(settings = @Setting(name = AvailableSettings.CRITERIA_COPY_TREE, value = "true"))
|
||||
public class TransientOverrideAsPersistentTablePerClass {
|
||||
|
||||
@Test
|
||||
|
@ -8,10 +8,14 @@
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
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.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -54,6 +58,7 @@
|
||||
}
|
||||
)
|
||||
@SessionFactory
|
||||
@ServiceRegistry(settings = @Setting(name = AvailableSettings.CRITERIA_COPY_TREE, value = "true"))
|
||||
public class TransientOverrideAsPersistentWithEmbeddable {
|
||||
|
||||
@Test
|
||||
|
@ -10,6 +10,7 @@
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.query.Query;
|
||||
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
|
||||
|
||||
@ -19,6 +20,7 @@
|
||||
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.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -28,11 +30,11 @@
|
||||
/**
|
||||
* @author Jan Schatteman
|
||||
*/
|
||||
@ServiceRegistry
|
||||
@DomainModel(
|
||||
standardModels = StandardDomainModel.GAMBIT
|
||||
)
|
||||
@SessionFactory
|
||||
@ServiceRegistry(settings = @Setting(name = AvailableSettings.CRITERIA_COPY_TREE, value = "true"))
|
||||
public class ILikeCriteriaTest {
|
||||
|
||||
@BeforeEach
|
||||
|
@ -16,15 +16,20 @@
|
||||
import jakarta.persistence.Tuple;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.jpa.AvailableHints;
|
||||
import org.hibernate.query.Query;
|
||||
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
|
||||
import org.hibernate.query.criteria.JpaCriteriaQuery;
|
||||
import org.hibernate.query.spi.QueryImplementor;
|
||||
import org.hibernate.stat.QueryStatistics;
|
||||
import org.hibernate.stat.Statistics;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
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;
|
||||
@ -47,7 +52,7 @@
|
||||
@Setting( name = Environment.GENERATE_STATISTICS, value = "true")
|
||||
})
|
||||
@SessionFactory
|
||||
@TestForIssue(jiraKey = "HHH-12855")
|
||||
@JiraKey("HHH-12855")
|
||||
public class QueryPlanCacheStatisticsTest {
|
||||
|
||||
private Statistics statistics;
|
||||
@ -126,7 +131,7 @@ public void test(SessionFactoryScope scope) {
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-13077" )
|
||||
@JiraKey("HHH-13077")
|
||||
public void testCreateQueryHitCount(SessionFactoryScope scope) {
|
||||
scope.inTransaction( entityManager -> {
|
||||
|
||||
@ -176,7 +181,7 @@ public void testCreateQueryHitCount(SessionFactoryScope scope) {
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-14632" )
|
||||
@JiraKey("HHH-14632")
|
||||
public void testCreateNativeQueryHitCount(SessionFactoryScope scope) {
|
||||
statistics.clear();
|
||||
|
||||
@ -224,7 +229,7 @@ public void testCreateNativeQueryHitCount(SessionFactoryScope scope) {
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-13077" )
|
||||
@JiraKey("HHH-13077")
|
||||
public void testCreateNamedQueryHitCount(SessionFactoryScope scope) {
|
||||
// Compile the named queries
|
||||
scope.getSessionFactory().getQueryEngine().getNamedObjectRepository().checkNamedQueries( scope.getSessionFactory().getQueryEngine() );
|
||||
@ -263,7 +268,7 @@ public void testCreateNamedQueryHitCount(SessionFactoryScope scope) {
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-13077" )
|
||||
@JiraKey("HHH-13077")
|
||||
public void testCreateQueryTupleHitCount(SessionFactoryScope scope) {
|
||||
scope.inTransaction( entityManager -> {
|
||||
|
||||
@ -314,7 +319,7 @@ public void testCreateQueryTupleHitCount(SessionFactoryScope scope) {
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-13077")
|
||||
@JiraKey("HHH-13077")
|
||||
public void testLockModeHitCount(SessionFactoryScope scope) {
|
||||
scope.inTransaction( entityManager -> {
|
||||
TypedQuery<Employee> typedQuery = entityManager.createQuery( "select e from Employee e", Employee.class );
|
||||
@ -344,6 +349,132 @@ public void testLockModeHitCount(SessionFactoryScope scope) {
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@JiraKey("HHH-16782")
|
||||
public void testCriteriaQuery(SessionFactoryScope scope) {
|
||||
scope.inTransaction( entityManager -> {
|
||||
HibernateCriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
JpaCriteriaQuery<Employee> cq = cb.createQuery( Employee.class );
|
||||
cq.from( Employee.class );
|
||||
entityManager.setProperty( AvailableSettings.CRITERIA_COPY_TREE, true );
|
||||
TypedQuery<Employee> typedQuery = entityManager.createQuery( cq );
|
||||
|
||||
// Criteria query does not need parsing, so no miss or hit at this point
|
||||
assertEquals( 0, statistics.getQueryPlanCacheMissCount() );
|
||||
assertEquals( 0, statistics.getQueryPlanCacheHitCount() );
|
||||
assertEquals( 0, statistics.getQueryExecutionCount() );
|
||||
|
||||
List<Employee> employees = typedQuery.getResultList();
|
||||
assertEquals( 5, employees.size() );
|
||||
|
||||
// The miss count is 0 because the query plan is not even considered for query plan caching
|
||||
assertEquals( 0, statistics.getQueryPlanCacheMissCount() );
|
||||
assertEquals( 0, statistics.getQueryPlanCacheHitCount() );
|
||||
assertEquals( 1, statistics.getQueryExecutionCount() );
|
||||
|
||||
typedQuery.getResultList();
|
||||
|
||||
// The miss count is 0 because the query plan is not even considered for query plan caching
|
||||
assertEquals( 0, statistics.getQueryPlanCacheMissCount() );
|
||||
assertEquals( 0, statistics.getQueryPlanCacheHitCount() );
|
||||
assertEquals( 2, statistics.getQueryExecutionCount() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@JiraKey("HHH-16782")
|
||||
public void testCriteriaQueryCache(SessionFactoryScope scope) {
|
||||
scope.inTransaction( entityManager -> {
|
||||
HibernateCriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
JpaCriteriaQuery<Employee> cq = cb.createQuery( Employee.class );
|
||||
cq.from( Employee.class );
|
||||
TypedQuery<Employee> typedQuery = entityManager.createQuery( cq );
|
||||
typedQuery.setHint( AvailableHints.HINT_QUERY_PLAN_CACHEABLE, true );
|
||||
|
||||
// Criteria query does not need parsing, so no miss or hit at this point
|
||||
assertEquals( 0, statistics.getQueryPlanCacheMissCount() );
|
||||
assertEquals( 0, statistics.getQueryPlanCacheHitCount() );
|
||||
assertEquals( 0, statistics.getQueryExecutionCount() );
|
||||
|
||||
List<Employee> employees = typedQuery.getResultList();
|
||||
assertEquals( 5, employees.size() );
|
||||
|
||||
// The miss count is 1 because the query plan is resolved once
|
||||
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
|
||||
assertEquals( 0, statistics.getQueryPlanCacheHitCount() );
|
||||
assertEquals( 1, statistics.getQueryExecutionCount() );
|
||||
|
||||
typedQuery.getResultList();
|
||||
|
||||
// The hit count should increase on second access though
|
||||
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
|
||||
assertEquals( 1, statistics.getQueryPlanCacheHitCount() );
|
||||
assertEquals( 2, statistics.getQueryExecutionCount() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@JiraKey("HHH-16782")
|
||||
public void testCriteriaQueryNoCopyTree(SessionFactoryScope scope) {
|
||||
scope.inTransaction( entityManager -> {
|
||||
HibernateCriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
JpaCriteriaQuery<Employee> cq = cb.createQuery( Employee.class );
|
||||
cq.from( Employee.class );
|
||||
entityManager.setProperty( AvailableSettings.CRITERIA_COPY_TREE, false );
|
||||
TypedQuery<Employee> typedQuery = entityManager.createQuery( cq );
|
||||
|
||||
// Criteria query does not need parsing, so no miss or hit at this point
|
||||
assertEquals( 0, statistics.getQueryPlanCacheMissCount() );
|
||||
assertEquals( 0, statistics.getQueryPlanCacheHitCount() );
|
||||
assertEquals( 0, statistics.getQueryExecutionCount() );
|
||||
|
||||
List<Employee> employees = typedQuery.getResultList();
|
||||
assertEquals( 5, employees.size() );
|
||||
|
||||
// The miss count is 1 because the query plan is resolved once
|
||||
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
|
||||
assertEquals( 0, statistics.getQueryPlanCacheHitCount() );
|
||||
assertEquals( 1, statistics.getQueryExecutionCount() );
|
||||
|
||||
typedQuery.getResultList();
|
||||
|
||||
// The hit count should increase on second access though
|
||||
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
|
||||
assertEquals( 1, statistics.getQueryPlanCacheHitCount() );
|
||||
assertEquals( 2, statistics.getQueryExecutionCount() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@JiraKey("HHH-16782")
|
||||
public void testDisableQueryPlanCache(SessionFactoryScope scope) {
|
||||
scope.inTransaction( entityManager -> {
|
||||
TypedQuery<Employee> typedQuery = entityManager.createQuery( "select e from Employee e", Employee.class );
|
||||
typedQuery.setHint( AvailableHints.HINT_QUERY_PLAN_CACHEABLE, false );
|
||||
|
||||
//First time, we get a cache miss, so the query is compiled
|
||||
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
|
||||
//The hit count should be 0 as we don't need to go to the cache after we already compiled the query
|
||||
assertEquals( 0, statistics.getQueryPlanCacheHitCount() );
|
||||
|
||||
List<Employee> employees = typedQuery.getResultList();
|
||||
|
||||
assertEquals( 5, employees.size() );
|
||||
|
||||
//The miss count is still 1 and hit count still 0 because plan is not even considered for caching
|
||||
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
|
||||
assertEquals( 0, statistics.getQueryPlanCacheHitCount() );
|
||||
assertEquals( 1, statistics.getQueryExecutionCount() );
|
||||
|
||||
typedQuery.getResultList();
|
||||
|
||||
//The miss count is still 1 and hit count still 0 because plan is not even considered for caching
|
||||
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
|
||||
assertEquals( 0, statistics.getQueryPlanCacheHitCount() );
|
||||
assertEquals( 2, statistics.getQueryExecutionCount() );
|
||||
} );
|
||||
}
|
||||
|
||||
private void assertQueryStatistics(String hql, int hitCount) {
|
||||
QueryStatistics queryStatistics = statistics.getQueryStatistics( hql );
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user