Add query plan cache statistics for native queries and implement proper caching

This commit is contained in:
Christian Beikov 2021-12-23 00:30:04 +01:00
parent 1184a5963b
commit 3ea5a066ed
49 changed files with 1504 additions and 265 deletions

View File

@ -113,4 +113,36 @@ public class JpaAttributeConverterImpl<O,R> implements JpaAttributeConverter<O,R
public JavaType<R> getRelationalJavaTypeDescriptor() {
return jdbcJtd;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
JpaAttributeConverterImpl<?, ?> that = (JpaAttributeConverterImpl<?, ?>) o;
if ( !attributeConverterBean.equals( that.attributeConverterBean ) ) {
return false;
}
if ( !converterJtd.equals( that.converterJtd ) ) {
return false;
}
if ( !domainJtd.equals( that.domainJtd ) ) {
return false;
}
return jdbcJtd.equals( that.jdbcJtd );
}
@Override
public int hashCode() {
int result = attributeConverterBean.hashCode();
result = 31 * result + converterJtd.hashCode();
result = 31 * result + domainJtd.hashCode();
result = 31 * result + jdbcJtd.hashCode();
return result;
}
}

View File

@ -60,6 +60,7 @@ import org.hibernate.persister.entity.Queryable;
import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.spi.DynamicModelJtd;
import org.hibernate.type.descriptor.java.spi.EntityJavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
@ -195,7 +196,7 @@ public class JpaMetamodelImpl implements JpaMetamodel, Serializable {
@Override
public <X> EmbeddableDomainType<X> embeddable(Class<X> cls) {
final ManagedType<?> type = jpaManagedTypeMap.get( cls );
if ( !( type instanceof EntityDomainType<?> ) ) {
if ( !( type instanceof EmbeddableDomainType<?> ) ) {
throw new IllegalArgumentException( "Not an embeddable: " + cls.getName() );
}
//noinspection unchecked
@ -503,13 +504,16 @@ public class JpaMetamodelImpl implements JpaMetamodel, Serializable {
if ( embeddable.getJavaType() != null && embeddable.getJavaType() != Map.class ) {
this.jpaEmbeddables.add( embeddable );
this.jpaManagedTypes.add( embeddable );
this.jpaManagedTypeMap.put( embeddable.getJavaType(), embeddable );
if ( !( embeddable.getExpressableJavaTypeDescriptor() instanceof EntityJavaTypeDescriptor<?> ) ) {
this.jpaManagedTypeMap.put( embeddable.getJavaType(), embeddable );
}
}
break;
case ENABLED:
this.jpaEmbeddables.add( embeddable );
this.jpaManagedTypes.add( embeddable );
if ( embeddable.getJavaType() != null ) {
if ( embeddable.getJavaType() != null
&& !( embeddable.getExpressableJavaTypeDescriptor() instanceof EntityJavaTypeDescriptor<?> ) ) {
this.jpaManagedTypeMap.put( embeddable.getJavaType(), embeddable );
}
break;
@ -517,7 +521,9 @@ public class JpaMetamodelImpl implements JpaMetamodel, Serializable {
if ( embeddable.getJavaType() == null ) {
throw new UnsupportedOperationException( "ANY not supported" );
}
this.jpaManagedTypeMap.put( embeddable.getJavaType(), embeddable );
if ( !( embeddable.getExpressableJavaTypeDescriptor() instanceof EntityJavaTypeDescriptor<?> ) ) {
this.jpaManagedTypeMap.put( embeddable.getJavaType(), embeddable );
}
break;
}
}

View File

@ -50,6 +50,11 @@ public class EntityDomainResultBuilder implements ResultBuilder {
return entityDescriptor.getJavaTypeDescriptor().getJavaTypeClass();
}
@Override
public ResultBuilder cacheKeyInstance() {
return this;
}
@Override
public EntityResult buildResult(
JdbcValuesMetadata jdbcResultsMetadata,
@ -77,4 +82,22 @@ public class EntityDomainResultBuilder implements ResultBuilder {
domainResultCreationState
);
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final EntityDomainResultBuilder that = (EntityDomainResultBuilder) o;
return entityDescriptor.equals( that.entityDescriptor );
}
@Override
public int hashCode() {
return entityDescriptor.hashCode();
}
}

View File

@ -39,4 +39,28 @@ public class ScalarDomainResultBuilder<T> implements ResultBuilder {
DomainResultCreationState domainResultCreationState) {
return new BasicResult<>( resultPosition, null, typeDescriptor );
}
@Override
public ResultBuilder cacheKeyInstance() {
return this;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
ScalarDomainResultBuilder<?> that = (ScalarDomainResultBuilder<?>) o;
return typeDescriptor.equals( that.typeDescriptor );
}
@Override
public int hashCode() {
return typeDescriptor.hashCode();
}
}

View File

@ -47,7 +47,11 @@ public class QueryInterpretationCacheDisabledImpl implements QueryInterpretation
@Override
public <R> SelectQueryPlan<R> resolveSelectQueryPlan(Key key, Supplier<SelectQueryPlan<R>> creator) {
return null;
final StatisticsImplementor statistics = statisticsSupplier.get();
if ( statistics.isStatisticsEnabled() ) {
statistics.queryPlanCacheMiss( key.getQueryString() );
}
return creator.get();
}
@Override
@ -61,7 +65,7 @@ public class QueryInterpretationCacheDisabledImpl implements QueryInterpretation
@Override
public HqlInterpretation resolveHqlInterpretation(String queryString, Function<String, SqmStatement<?>> creator) {
StatisticsImplementor statistics = statisticsSupplier.get();
final StatisticsImplementor statistics = statisticsSupplier.get();
final boolean stats = statistics.isStatisticsEnabled();
final long startTime = ( stats ) ? System.nanoTime() : 0L;
final SqmStatement<?> sqmStatement = creator.apply( queryString );
@ -76,7 +80,6 @@ public class QueryInterpretationCacheDisabledImpl implements QueryInterpretation
else {
domainParameterXref = DomainParameterXref.from( sqmStatement );
parameterMetadata = new ParameterMetadataImpl( domainParameterXref.getQueryParameters() );
}
if ( stats ) {

View File

@ -80,15 +80,23 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation
Key key,
Supplier<SelectQueryPlan<R>> creator) {
log.tracef( "QueryPlan#getSelectQueryPlan(%s)", key );
final StatisticsImplementor statistics = statisticsSupplier.get();
final boolean stats = statistics.isStatisticsEnabled();
@SuppressWarnings("unchecked")
final SelectQueryPlan<R> cached = (SelectQueryPlan<R>) queryPlanCache.get( key );
if ( cached != null ) {
if ( stats ) {
statistics.queryPlanCacheHit( key.getQueryString() );
}
return cached;
}
final SelectQueryPlan<R> plan = creator.get();
queryPlanCache.put( key.prepareForStore(), plan );
if ( stats ) {
statistics.queryPlanCacheMiss( key.getQueryString() );
}
return plan;
}

View File

@ -6,6 +6,8 @@
*/
package org.hibernate.query.results;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.metamodel.EntityType;
@ -13,7 +15,6 @@ import jakarta.persistence.metamodel.SingularAttribute;
import org.hibernate.LockMode;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.RuntimeMetamodels;
import org.hibernate.metamodel.mapping.AttributeMapping;
@ -38,6 +39,7 @@ import org.hibernate.query.results.implicit.ImplicitFetchBuilder;
import org.hibernate.query.results.implicit.ImplicitFetchBuilderBasic;
import org.hibernate.query.results.implicit.ImplicitFetchBuilderEmbeddable;
import org.hibernate.query.results.implicit.ImplicitFetchBuilderEntity;
import org.hibernate.query.results.implicit.ImplicitFetchBuilderEntityPart;
import org.hibernate.query.results.implicit.ImplicitFetchBuilderPlural;
import org.hibernate.query.results.implicit.ImplicitModelPartResultBuilderEntity;
import org.hibernate.sql.results.graph.DomainResultCreationState;
@ -236,7 +238,7 @@ public class Builders {
}
public static DynamicFetchBuilderLegacy fetch(String tableAlias, String ownerTableAlias, String joinPropertyName) {
return new DynamicFetchBuilderLegacy( tableAlias, ownerTableAlias, joinPropertyName, null, null );
return new DynamicFetchBuilderLegacy( tableAlias, ownerTableAlias, joinPropertyName, new ArrayList<>(), new HashMap<>() );
}
public static ResultBuilder implicitEntityResultBuilder(
@ -273,15 +275,7 @@ public class Builders {
}
if ( fetchable instanceof EntityCollectionPart ) {
final EntityCollectionPart entityCollectionPart = (EntityCollectionPart) fetchable;
return (parent, fetchablePath, jdbcResultsMetadata, legacyFetchResolver, domainResultCreationState) -> parent.generateFetchableFetch(
entityCollectionPart,
fetchablePath,
FetchTiming.IMMEDIATE,
true,
null,
domainResultCreationState
);
return new ImplicitFetchBuilderEntityPart( fetchPath, (EntityCollectionPart) fetchable );
}
throw new UnsupportedOperationException();

View File

@ -35,4 +35,6 @@ public interface FetchBuilder {
default void visitFetchBuilders(BiConsumer<String, FetchBuilder> consumer) {
}
FetchBuilder cacheKeyInstance();
}

View File

@ -32,6 +32,11 @@ public class ImplicitAttributeFetchBuilder implements FetchBuilder, ImplicitFetc
this.attributeMapping = attributeMapping;
}
@Override
public FetchBuilder cacheKeyInstance() {
return this;
}
@Override
public Fetch buildFetch(
FetchParent parent,
@ -50,4 +55,25 @@ public class ImplicitAttributeFetchBuilder implements FetchBuilder, ImplicitFetc
domainResultCreationState
);
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final ImplicitAttributeFetchBuilder that = (ImplicitAttributeFetchBuilder) o;
return navigablePath.equals( that.navigablePath )
&& attributeMapping.equals( that.attributeMapping );
}
@Override
public int hashCode() {
int result = navigablePath.hashCode();
result = 31 * result + attributeMapping.hashCode();
return result;
}
}

View File

@ -31,6 +31,8 @@ public interface ResultBuilder {
Class<?> getJavaType();
ResultBuilder cacheKeyInstance();
default void visitFetchBuilders(BiConsumer<String, FetchBuilder> consumer) {
}
}

View File

@ -11,6 +11,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.BiConsumer;
@ -37,6 +38,7 @@ import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
import org.hibernate.sql.results.graph.entity.EntityResult;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMapping;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
import org.hibernate.type.BasicType;
@ -47,12 +49,46 @@ import org.hibernate.type.BasicType;
@Internal
public class ResultSetMappingImpl implements ResultSetMapping {
private final String mappingIdentifier;
private final boolean isDynamic;
private List<ResultBuilder> resultBuilders;
private Map<String, Map<String, DynamicFetchBuilderLegacy>> legacyFetchBuilders;
public ResultSetMappingImpl(String mappingIdentifier) {
this( mappingIdentifier, false );
}
public ResultSetMappingImpl(String mappingIdentifier, boolean isDynamic) {
this.mappingIdentifier = mappingIdentifier;
this.isDynamic = isDynamic;
}
private ResultSetMappingImpl(ResultSetMappingImpl original) {
this.mappingIdentifier = original.mappingIdentifier;
this.isDynamic = original.isDynamic;
if ( !original.isDynamic || original.resultBuilders == null ) {
this.resultBuilders = null;
}
else {
final List<ResultBuilder> resultBuilders = new ArrayList<>( original.resultBuilders.size() );
for ( ResultBuilder resultBuilder : original.resultBuilders ) {
resultBuilders.add( resultBuilder.cacheKeyInstance() );
}
this.resultBuilders = resultBuilders;
}
if ( !original.isDynamic || original.legacyFetchBuilders == null ) {
this.legacyFetchBuilders = null;
}
else {
final Map<String, Map<String, DynamicFetchBuilderLegacy>> legacyFetchBuilders = new HashMap<>( original.legacyFetchBuilders.size() );
for ( Map.Entry<String, Map<String, DynamicFetchBuilderLegacy>> entry : original.legacyFetchBuilders.entrySet() ) {
final Map<String, DynamicFetchBuilderLegacy> newValue = new HashMap<>( entry.getValue().size() );
for ( Map.Entry<String, DynamicFetchBuilderLegacy> builderEntry : entry.getValue().entrySet() ) {
newValue.put( builderEntry.getKey(), builderEntry.getValue().cacheKeyInstance() );
}
legacyFetchBuilders.put( entry.getKey(), newValue );
}
this.legacyFetchBuilders = legacyFetchBuilders;
}
}
@Override
@ -306,4 +342,43 @@ public class ResultSetMappingImpl implements ResultSetMapping {
public NamedResultSetMappingMemento toMemento(String name) {
throw new NotYetImplementedFor6Exception( getClass() );
}
@Override
public JdbcValuesMappingProducer cacheKeyInstance() {
return new ResultSetMappingImpl( this );
}
@Override
public int hashCode() {
if ( isDynamic ) {
int result = mappingIdentifier != null ? mappingIdentifier.hashCode() : 0;
result = 31 * result + ( resultBuilders != null ? resultBuilders.hashCode() : 0 );
result = 31 * result + ( legacyFetchBuilders != null ? legacyFetchBuilders.hashCode() : 0 );
return result;
}
else {
return mappingIdentifier.hashCode();
}
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final ResultSetMappingImpl that = (ResultSetMappingImpl) o;
if ( isDynamic ) {
return that.isDynamic
&& Objects.equals( mappingIdentifier, that.mappingIdentifier )
&& Objects.equals( resultBuilders, that.resultBuilders )
&& Objects.equals( legacyFetchBuilders, that.legacyFetchBuilders );
}
else {
return !that.isDynamic && mappingIdentifier.equals( that.mappingIdentifier );
}
}
}

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.query.results.complete;
import java.util.Objects;
import java.util.function.BiFunction;
import org.hibernate.engine.FetchTiming;
@ -13,6 +14,7 @@ import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.BasicValuedFetchBuilder;
import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.FetchBuilder;
import org.hibernate.query.results.MissingSqlSelectionException;
import org.hibernate.query.results.PositionalSelectionsNotAllowedException;
import org.hibernate.query.results.SqlSelectionImpl;
@ -45,6 +47,11 @@ public class CompleteFetchBuilderBasicPart implements CompleteFetchBuilder, Basi
this.selectionAlias = selectionAlias;
}
@Override
public FetchBuilder cacheKeyInstance() {
return this;
}
@Override
public NavigablePath getNavigablePath() {
return navigablePath;
@ -113,4 +120,27 @@ public class CompleteFetchBuilderBasicPart implements CompleteFetchBuilder, Basi
domainResultCreationState
);
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final CompleteFetchBuilderBasicPart that = (CompleteFetchBuilderBasicPart) o;
return navigablePath.equals( that.navigablePath )
&& referencedModelPart.equals( that.referencedModelPart )
&& Objects.equals( selectionAlias, that.selectionAlias );
}
@Override
public int hashCode() {
int result = navigablePath.hashCode();
result = 31 * result + referencedModelPart.hashCode();
result = 31 * result + ( selectionAlias != null ? selectionAlias.hashCode() : 0 );
return result;
}
}

View File

@ -12,6 +12,7 @@ import java.util.function.BiFunction;
import org.hibernate.engine.FetchTiming;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.FetchBuilder;
import org.hibernate.query.results.SqlSelectionImpl;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
@ -46,6 +47,15 @@ public class CompleteFetchBuilderEntityValuedModelPart
this.columnAliases = columnAliases;
}
@Override
public FetchBuilder cacheKeyInstance() {
return new CompleteFetchBuilderEntityValuedModelPart(
navigablePath,
modelPart,
List.copyOf( columnAliases )
);
}
@Override
public NavigablePath getNavigablePath() {
return navigablePath;
@ -97,4 +107,26 @@ public class CompleteFetchBuilderEntityValuedModelPart
);
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final CompleteFetchBuilderEntityValuedModelPart that = (CompleteFetchBuilderEntityValuedModelPart) o;
return navigablePath.equals( that.navigablePath )
&& modelPart.equals( that.modelPart )
&& columnAliases.equals( that.columnAliases );
}
@Override
public int hashCode() {
int result = navigablePath.hashCode();
result = 31 * result + modelPart.hashCode();
result = 31 * result + columnAliases.hashCode();
return result;
}
}

View File

@ -11,6 +11,7 @@ import java.util.function.BiFunction;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.SqlSelectionImpl;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
@ -59,6 +60,11 @@ public class CompleteResultBuilderBasicModelPart
return modelPart;
}
@Override
public ResultBuilder cacheKeyInstance() {
return this;
}
@Override
public BasicResult<?> buildResult(
JdbcValuesMetadata jdbcResultsMetadata,
@ -91,4 +97,32 @@ public class CompleteResultBuilderBasicModelPart
modelPart.getJavaTypeDescriptor()
);
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
CompleteResultBuilderBasicModelPart that = (CompleteResultBuilderBasicModelPart) o;
if ( !navigablePath.equals( that.navigablePath ) ) {
return false;
}
if ( !modelPart.equals( that.modelPart ) ) {
return false;
}
return columnAlias.equals( that.columnAlias );
}
@Override
public int hashCode() {
int result = navigablePath.hashCode();
result = 31 * result + modelPart.hashCode();
result = 31 * result + columnAlias.hashCode();
return result;
}
}

View File

@ -6,12 +6,14 @@
*/
package org.hibernate.query.results.complete;
import java.util.Objects;
import java.util.function.BiFunction;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.model.convert.internal.JpaAttributeConverterImpl;
import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.ResultsHelper;
import org.hibernate.query.results.SqlSelectionImpl;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
@ -37,10 +39,8 @@ import static org.hibernate.query.results.ResultsHelper.impl;
*/
public class CompleteResultBuilderBasicValuedConverted<O,R> implements CompleteResultBuilderBasicValued {
private final String explicitColumnName;
private final ManagedBean<? extends AttributeConverter<O, R>> converterBean;
private final JavaType<? extends AttributeConverter<O, R>> converterJtd;
private final BasicJavaType<O> domainJavaType;
private final BasicValuedMapping underlyingMapping;
private final JpaAttributeConverterImpl<O, R> valueConverter;
public CompleteResultBuilderBasicValuedConverted(
String explicitColumnName,
@ -49,15 +49,23 @@ public class CompleteResultBuilderBasicValuedConverted<O,R> implements CompleteR
BasicJavaType<O> domainJavaType,
BasicValuedMapping underlyingMapping) {
this.explicitColumnName = explicitColumnName;
this.converterBean = converterBean;
this.converterJtd = converterJtd;
this.domainJavaType = domainJavaType;
this.underlyingMapping = underlyingMapping;
this.valueConverter = new JpaAttributeConverterImpl<>(
converterBean,
converterJtd,
domainJavaType,
underlyingMapping.getJdbcMapping().getJavaTypeDescriptor()
);
}
@Override
public Class<?> getJavaType() {
return domainJavaType.getJavaTypeClass();
return valueConverter.getDomainJavaDescriptor().getJavaTypeClass();
}
@Override
public ResultBuilder cacheKeyInstance() {
return this;
}
@Override
@ -123,22 +131,43 @@ public class CompleteResultBuilderBasicValuedConverted<O,R> implements CompleteR
return new SqlSelectionImpl( valuesArrayPosition, underlyingMapping );
}
),
domainJavaType,
valueConverter.getDomainJavaDescriptor(),
sessionFactory.getTypeConfiguration()
);
final JpaAttributeConverterImpl<O,R> valueConverter = new JpaAttributeConverterImpl<>(
converterBean,
converterJtd,
domainJavaType,
underlyingMapping.getJdbcMapping().getJavaTypeDescriptor()
);
return new BasicResult<>(
sqlSelection.getValuesArrayPosition(),
columnName,
domainJavaType,
valueConverter.getDomainJavaDescriptor(),
valueConverter
);
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
CompleteResultBuilderBasicValuedConverted<?, ?> that = (CompleteResultBuilderBasicValuedConverted<?, ?>) o;
if ( !Objects.equals( explicitColumnName, that.explicitColumnName ) ) {
return false;
}
if ( !underlyingMapping.equals( that.underlyingMapping ) ) {
return false;
}
return valueConverter.equals( that.valueConverter );
}
@Override
public int hashCode() {
int result = explicitColumnName != null ? explicitColumnName.hashCode() : 0;
result = 31 * result + underlyingMapping.hashCode();
result = 31 * result + valueConverter.hashCode();
return result;
}
}

View File

@ -6,12 +6,14 @@
*/
package org.hibernate.query.results.complete;
import java.util.Objects;
import java.util.function.BiFunction;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.ResultsHelper;
import org.hibernate.query.results.SqlSelectionImpl;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
@ -52,6 +54,11 @@ public class CompleteResultBuilderBasicValuedStandard implements CompleteResultB
this.explicitJavaTypeDescriptor = explicitJavaTypeDescriptor;
}
@Override
public ResultBuilder cacheKeyInstance() {
return this;
}
@Override
public Class<?> getJavaType() {
return explicitJavaTypeDescriptor.getJavaTypeClass();
@ -142,4 +149,31 @@ public class CompleteResultBuilderBasicValuedStandard implements CompleteResultB
);
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
CompleteResultBuilderBasicValuedStandard that = (CompleteResultBuilderBasicValuedStandard) o;
if ( !Objects.equals( explicitColumnName, that.explicitColumnName ) ) {
return false;
}
if ( !Objects.equals( explicitType, that.explicitType ) ) {
return false;
}
return explicitJavaTypeDescriptor.equals( that.explicitJavaTypeDescriptor );
}
@Override
public int hashCode() {
int result = explicitColumnName != null ? explicitColumnName.hashCode() : 0;
result = 31 * result + ( explicitType != null ? explicitType.hashCode() : 0 );
result = 31 * result + explicitJavaTypeDescriptor.hashCode();
return result;
}
}

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.query.results.complete;
import java.util.Arrays;
import java.util.function.BiFunction;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -19,6 +20,7 @@ import org.hibernate.query.NativeQuery;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.FromClauseAccessImpl;
import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.ResultsHelper;
import org.hibernate.query.results.SqlSelectionImpl;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
@ -84,6 +86,11 @@ public class CompleteResultBuilderCollectionStandard implements CompleteResultBu
return navigablePath;
}
@Override
public ResultBuilder cacheKeyInstance() {
return this;
}
@Override
public DomainResult<?> buildResult(
JdbcValuesMetadata jdbcResultsMetadata,
@ -174,4 +181,32 @@ public class CompleteResultBuilderCollectionStandard implements CompleteResultBu
}
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final CompleteResultBuilderCollectionStandard that = (CompleteResultBuilderCollectionStandard) o;
return tableAlias.equals( that.tableAlias )
&& navigablePath.equals( that.navigablePath )
&& pluralAttributeDescriptor.equals( that.pluralAttributeDescriptor )
&& Arrays.equals( keyColumnNames, that.keyColumnNames )
&& Arrays.equals( indexColumnNames, that.indexColumnNames )
&& Arrays.equals( elementColumnNames, that.elementColumnNames );
}
@Override
public int hashCode() {
int result = tableAlias.hashCode();
result = 31 * result + navigablePath.hashCode();
result = 31 * result + pluralAttributeDescriptor.hashCode();
result = 31 * result + Arrays.hashCode( keyColumnNames );
result = 31 * result + Arrays.hashCode( indexColumnNames );
result = 31 * result + Arrays.hashCode( elementColumnNames );
return result;
}
}

View File

@ -7,6 +7,7 @@
package org.hibernate.query.results.complete;
import java.util.HashMap;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@ -17,6 +18,7 @@ import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.BasicValuedFetchBuilder;
import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.FetchBuilder;
import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.ResultsHelper;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.sql.results.graph.DomainResultCreationState;
@ -77,6 +79,11 @@ public class CompleteResultBuilderEntityJpa implements CompleteResultBuilderEnti
return entityDescriptor;
}
@Override
public ResultBuilder cacheKeyInstance() {
return this;
}
@Override
public EntityResult buildResult(
JdbcValuesMetadata jdbcResultsMetadata,
@ -133,4 +140,31 @@ public class CompleteResultBuilderEntityJpa implements CompleteResultBuilderEnti
public void visitFetchBuilders(BiConsumer<String, FetchBuilder> consumer) {
explicitFetchBuilderMap.forEach( consumer );
}
@Override
public int hashCode() {
int result = navigablePath.hashCode();
result = 31 * result + entityDescriptor.hashCode();
result = 31 * result + lockMode.hashCode();
result = 31 * result + ( discriminatorFetchBuilder != null ? discriminatorFetchBuilder.hashCode() : 0 );
result = 31 * result + explicitFetchBuilderMap.hashCode();
return result;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final CompleteResultBuilderEntityJpa that = (CompleteResultBuilderEntityJpa) o;
return navigablePath.equals( that.navigablePath )
&& entityDescriptor.equals( that.entityDescriptor )
&& lockMode == that.lockMode
&& Objects.equals( discriminatorFetchBuilder, that.discriminatorFetchBuilder )
&& explicitFetchBuilderMap.equals( that.explicitFetchBuilderMap );
}
}

View File

@ -7,6 +7,7 @@
package org.hibernate.query.results.complete;
import java.util.HashMap;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@ -18,6 +19,7 @@ import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.BasicValuedFetchBuilder;
import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.FetchBuilder;
import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.ResultsHelper;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.sql.results.graph.DomainResultCreationState;
@ -105,6 +107,11 @@ public class CompleteResultBuilderEntityStandard implements CompleteResultBuilde
throw new UnsupportedOperationException();
}
@Override
public ResultBuilder cacheKeyInstance() {
return this;
}
@Override
public EntityResult buildResult(
JdbcValuesMetadata jdbcResultsMetadata,
@ -161,4 +168,31 @@ public class CompleteResultBuilderEntityStandard implements CompleteResultBuilde
public void visitFetchBuilders(BiConsumer<String, FetchBuilder> consumer) {
explicitFetchBuilderMap.forEach( consumer );
}
@Override
public int hashCode() {
int result = navigablePath.hashCode();
result = 31 * result + entityDescriptor.hashCode();
result = 31 * result + lockMode.hashCode();
result = 31 * result + ( discriminatorFetchBuilder != null ? discriminatorFetchBuilder.hashCode() : 0 );
result = 31 * result + explicitFetchBuilderMap.hashCode();
return result;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final CompleteResultBuilderEntityStandard that = (CompleteResultBuilderEntityStandard) o;
return navigablePath.equals( that.navigablePath )
&& entityDescriptor.equals( that.entityDescriptor )
&& lockMode == that.lockMode
&& Objects.equals( discriminatorFetchBuilder, that.discriminatorFetchBuilder )
&& explicitFetchBuilderMap.equals( that.explicitFetchBuilderMap );
}
}

View File

@ -44,6 +44,11 @@ public class CompleteResultBuilderInstantiation
return javaTypeDescriptor.getJavaTypeClass();
}
@Override
public ResultBuilder cacheKeyInstance() {
return this;
}
@Override
public DomainResult<?> buildResult(
JdbcValuesMetadata jdbcResultsMetadata,
@ -74,4 +79,25 @@ public class CompleteResultBuilderInstantiation
argumentDomainResults
);
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final CompleteResultBuilderInstantiation that = (CompleteResultBuilderInstantiation) o;
return javaTypeDescriptor.equals( that.javaTypeDescriptor )
&& argumentResultBuilders.equals( that.argumentResultBuilders );
}
@Override
public int hashCode() {
int result = javaTypeDescriptor.hashCode();
result = 31 * result + argumentResultBuilders.hashCode();
return result;
}
}

View File

@ -12,6 +12,7 @@ import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.BasicValuedFetchBuilder;
import org.hibernate.query.results.FetchBuilder;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.FetchParent;
@ -36,6 +37,11 @@ public class DelayedFetchBuilderBasicPart
this.isEnhancedForLazyLoading = isEnhancedForLazyLoading;
}
@Override
public FetchBuilder cacheKeyInstance() {
return this;
}
@Override
public NavigablePath getNavigablePath() {
return navigablePath;
@ -64,4 +70,27 @@ public class DelayedFetchBuilderBasicPart
domainResultCreationState
);
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final DelayedFetchBuilderBasicPart that = (DelayedFetchBuilderBasicPart) o;
return isEnhancedForLazyLoading == that.isEnhancedForLazyLoading
&& navigablePath.equals( that.navigablePath )
&& referencedModelPart.equals( that.referencedModelPart );
}
@Override
public int hashCode() {
int result = navigablePath.hashCode();
result = 31 * result + referencedModelPart.hashCode();
result = 31 * result + ( isEnhancedForLazyLoading ? 1 : 0 );
return result;
}
}

View File

@ -9,6 +9,7 @@ package org.hibernate.query.results.dynamic;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.query.results.FetchBuilder;
@ -20,6 +21,26 @@ public abstract class AbstractFetchBuilderContainer<T extends AbstractFetchBuild
implements DynamicFetchBuilderContainer {
private Map<String, DynamicFetchBuilder> fetchBuilderMap;
protected AbstractFetchBuilderContainer() {
}
protected AbstractFetchBuilderContainer(AbstractFetchBuilderContainer<T> original) {
if ( original.fetchBuilderMap != null ) {
final Map<String, DynamicFetchBuilder> fetchBuilderMap = new HashMap<>( original.fetchBuilderMap.size() );
for ( Map.Entry<String, DynamicFetchBuilder> entry : original.fetchBuilderMap.entrySet() ) {
final DynamicFetchBuilder fetchBuilder;
if ( entry.getValue() instanceof DynamicFetchBuilderStandard ) {
fetchBuilder = ( (DynamicFetchBuilderStandard) entry.getValue() ).cacheKeyInstance( this );
}
else {
fetchBuilder = entry.getValue().cacheKeyInstance();
}
fetchBuilderMap.put( entry.getKey(), fetchBuilder );
}
this.fetchBuilderMap = fetchBuilderMap;
}
}
protected abstract String getPropertyBase();
@Override
@ -79,4 +100,22 @@ public abstract class AbstractFetchBuilderContainer<T extends AbstractFetchBuild
}
fetchBuilderMap.put( propertyName, fetchBuilder );
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final AbstractFetchBuilderContainer<?> that = (AbstractFetchBuilderContainer<?>) o;
return Objects.equals( fetchBuilderMap, that.fetchBuilderMap );
}
@Override
public int hashCode() {
return fetchBuilderMap != null ? fetchBuilderMap.hashCode() : 0;
}
}

View File

@ -16,5 +16,7 @@ import org.hibernate.sql.results.graph.Fetchable;
* @author Steve Ebersole
*/
public interface DynamicFetchBuilder extends FetchBuilder, NativeQuery.ReturnProperty {
DynamicFetchBuilder cacheKeyInstance();
List<String> getColumnAliases();
}

View File

@ -6,8 +6,10 @@
*/
package org.hibernate.query.results.dynamic;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@ -59,12 +61,7 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue
String fetchableName,
List<String> columnNames,
Map<String, FetchBuilder> fetchBuilderMap) {
this.tableAlias = tableAlias;
this.ownerTableAlias = ownerTableAlias;
this.fetchableName = fetchableName;
this.columnNames = columnNames;
this.fetchBuilderMap = fetchBuilderMap;
this.resultBuilderEntity = null;
this( tableAlias, ownerTableAlias, fetchableName, columnNames, fetchBuilderMap, null );
}
public DynamicFetchBuilderLegacy(
@ -97,6 +94,28 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue
return fetchableName;
}
@Override
public DynamicFetchBuilderLegacy cacheKeyInstance() {
final Map<String, FetchBuilder> fetchBuilderMap;
if ( this.fetchBuilderMap == null ) {
fetchBuilderMap = null;
}
else {
fetchBuilderMap = new HashMap<>( this.fetchBuilderMap.size() );
for ( Map.Entry<String, FetchBuilder> entry : this.fetchBuilderMap.entrySet() ) {
fetchBuilderMap.put( entry.getKey(), entry.getValue().cacheKeyInstance() );
}
}
return new DynamicFetchBuilderLegacy(
tableAlias,
ownerTableAlias,
fetchableName,
List.copyOf( columnNames ),
fetchBuilderMap,
resultBuilderEntity == null ? null : resultBuilderEntity.cacheKeyInstance()
);
}
@Override
public Fetch buildFetch(
FetchParent parent,
@ -233,4 +252,33 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue
public void visitFetchBuilders(BiConsumer<String, FetchBuilder> consumer) {
fetchBuilderMap.forEach( consumer );
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final DynamicFetchBuilderLegacy that = (DynamicFetchBuilderLegacy) o;
return tableAlias.equals( that.tableAlias )
&& ownerTableAlias.equals( that.ownerTableAlias )
&& fetchableName.equals( that.fetchableName )
&& Objects.equals( columnNames, that.columnNames )
&& Objects.equals( fetchBuilderMap, that.fetchBuilderMap )
&& Objects.equals( resultBuilderEntity, that.resultBuilderEntity );
}
@Override
public int hashCode() {
int result = tableAlias.hashCode();
result = 31 * result + ownerTableAlias.hashCode();
result = 31 * result + fetchableName.hashCode();
result = 31 * result + ( columnNames != null ? columnNames.hashCode() : 0 );
result = 31 * result + ( fetchBuilderMap != null ? fetchBuilderMap.hashCode() : 0 );
result = 31 * result + ( resultBuilderEntity != null ? resultBuilderEntity.hashCode() : 0 );
return result;
}
}

View File

@ -18,6 +18,7 @@ import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.query.NativeQuery;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.FetchBuilder;
import org.hibernate.query.results.ResultsHelper;
import org.hibernate.query.results.SqlSelectionImpl;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
@ -41,13 +42,40 @@ public class DynamicFetchBuilderStandard
private final DynamicFetchBuilderContainer container;
private final String fetchableName;
private final List<String> columnNames = new ArrayList<>();
private final List<String> columnNames;
public DynamicFetchBuilderStandard(
DynamicFetchBuilderContainer container,
String fetchableName) {
this.container = container;
this.fetchableName = fetchableName;
this.columnNames = new ArrayList<>();
}
private DynamicFetchBuilderStandard(
DynamicFetchBuilderContainer container,
String fetchableName,
List<String> columnNames) {
this.container = container;
this.fetchableName = fetchableName;
this.columnNames = columnNames;
}
@Override
public DynamicFetchBuilderStandard cacheKeyInstance() {
return new DynamicFetchBuilderStandard(
container,
fetchableName,
List.copyOf( columnNames )
);
}
public DynamicFetchBuilderStandard cacheKeyInstance(DynamicFetchBuilderContainer container) {
return new DynamicFetchBuilderStandard(
container,
fetchableName,
List.copyOf( columnNames )
);
}
@Override
@ -134,4 +162,25 @@ public class DynamicFetchBuilderStandard
public List<String> getColumnAliases() {
return columnNames;
}
@Override
public int hashCode() {
int result = fetchableName.hashCode();
result = 31 * result + columnNames.hashCode();
return result;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final DynamicFetchBuilderStandard that = (DynamicFetchBuilderStandard) o;
return fetchableName.equals( that.fetchableName )
&& columnNames.equals( that.columnNames );
}
}

View File

@ -22,4 +22,5 @@ import org.hibernate.query.results.ResultBuilder;
* @author Steve Ebersole
*/
public interface DynamicResultBuilder extends ResultBuilder, NativeQuery.ReturnableResultNode {
DynamicResultBuilder cacheKeyInstance();
}

View File

@ -12,6 +12,7 @@ import java.util.function.BiFunction;
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
import org.hibernate.query.NativeQuery;
import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.SqlSelectionImpl;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.spi.SqlSelection;
@ -65,6 +66,11 @@ public class DynamicResultBuilderAttribute implements DynamicResultBuilder, Nati
throw new UnsupportedOperationException();
}
@Override
public DynamicResultBuilderAttribute cacheKeyInstance() {
return this;
}
@Override
public DomainResult<?> buildResult(
JdbcValuesMetadata jdbcResultsMetadata,
@ -97,4 +103,29 @@ public class DynamicResultBuilderAttribute implements DynamicResultBuilder, Nati
attributeMapping.getValueConverter()
);
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final DynamicResultBuilderAttribute that = (DynamicResultBuilderAttribute) o;
return attributeMapping.equals( that.attributeMapping )
&& columnAlias.equals( that.columnAlias )
&& entityName.equals( that.entityName )
&& attributePath.equals( that.attributePath );
}
@Override
public int hashCode() {
int result = attributeMapping.hashCode();
result = 31 * result + columnAlias.hashCode();
result = 31 * result + entityName.hashCode();
result = 31 * result + attributePath.hashCode();
return result;
}
}

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.query.results.dynamic;
import java.util.Objects;
import java.util.function.BiFunction;
import jakarta.persistence.AttributeConverter;
@ -13,6 +14,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.model.convert.internal.JpaAttributeConverterImpl;
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.ResultsHelper;
import org.hibernate.query.results.SqlSelectionImpl;
import org.hibernate.resource.beans.spi.ManagedBean;
@ -35,10 +37,6 @@ import org.hibernate.type.spi.TypeConfiguration;
*/
public class DynamicResultBuilderBasicConverted<O,R> implements DynamicResultBuilderBasic {
private final String columnAlias;
private final JavaType<O> domainJtd;
private final JavaType<R> jdbcJtd;
private final BasicValueConverter<O,R> basicValueConverter;
public DynamicResultBuilderBasicConverted(
@ -49,20 +47,15 @@ public class DynamicResultBuilderBasicConverted<O,R> implements DynamicResultBui
SessionFactoryImplementor sessionFactory) {
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
final JavaTypeRegistry jtdRegistry = typeConfiguration.getJavaTypeDescriptorRegistry();
this.columnAlias = columnAlias;
this.domainJtd = jtdRegistry.getDescriptor( domainJavaType );
this.jdbcJtd = jtdRegistry.getDescriptor( jdbcJavaType );
final JavaType<? extends AttributeConverter> converterJtd = jtdRegistry.getDescriptor( converter.getClass() );
final JavaType<? extends AttributeConverter<O, R>> converterJtd = jtdRegistry.getDescriptor( converter.getClass() );
final ManagedBean<? extends AttributeConverter<O,R>> bean = new ProvidedInstanceManagedBeanImpl<>( converter );
this.basicValueConverter = new JpaAttributeConverterImpl(
this.columnAlias = columnAlias;
this.basicValueConverter = new JpaAttributeConverterImpl<>(
bean,
converterJtd,
domainJtd,
jdbcJtd
jtdRegistry.getDescriptor( domainJavaType ),
jtdRegistry.getDescriptor( jdbcJavaType )
);
}
@ -70,31 +63,31 @@ public class DynamicResultBuilderBasicConverted<O,R> implements DynamicResultBui
String columnAlias,
Class<O> domainJavaType,
Class<R> jdbcJavaType,
Class<? extends AttributeConverter<O,R>> converterJavaType,
Class<? extends AttributeConverter<O, R>> converterJavaType,
SessionFactoryImplementor sessionFactory) {
final ManagedBeanRegistry beans = sessionFactory.getServiceRegistry().getService( ManagedBeanRegistry.class );
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
final JavaTypeRegistry jtdRegistry = typeConfiguration.getJavaTypeDescriptorRegistry();
final JavaType<? extends AttributeConverter<O, R>> converterJtd = jtdRegistry.getDescriptor( converterJavaType );
final ManagedBean<? extends AttributeConverter<O, R>> bean = beans.getBean( converterJavaType );
this.columnAlias = columnAlias;
this.domainJtd = jtdRegistry.getDescriptor( domainJavaType );
this.jdbcJtd = jtdRegistry.getDescriptor( jdbcJavaType );
final JavaType<? extends AttributeConverter<O,R>> converterJtd = jtdRegistry.getDescriptor( converterJavaType );
final ManagedBean<? extends AttributeConverter<O,R>> bean = beans.getBean( converterJavaType );
this.basicValueConverter = new JpaAttributeConverterImpl(
this.basicValueConverter = new JpaAttributeConverterImpl<>(
bean,
converterJtd,
domainJtd,
jdbcJtd
jtdRegistry.getDescriptor( domainJavaType ),
jtdRegistry.getDescriptor( jdbcJavaType )
);
}
@Override
public Class<?> getJavaType() {
return domainJtd.getJavaTypeClass();
return basicValueConverter.getDomainJavaDescriptor().getJavaTypeClass();
}
@Override
public DynamicResultBuilderBasicConverted cacheKeyInstance() {
return this;
}
@Override
@ -134,10 +127,39 @@ public class DynamicResultBuilderBasicConverted<O,R> implements DynamicResultBui
return new SqlSelectionImpl( valuesArrayPosition, (BasicValuedMapping) basicType );
}
),
domainJtd,
basicValueConverter.getDomainJavaDescriptor(),
typeConfiguration
);
return new BasicResult<>( sqlSelection.getValuesArrayPosition(), columnAlias, domainJtd, basicValueConverter );
return new BasicResult<>(
sqlSelection.getValuesArrayPosition(),
columnAlias,
basicValueConverter.getDomainJavaDescriptor(),
basicValueConverter
);
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
DynamicResultBuilderBasicConverted<?, ?> that = (DynamicResultBuilderBasicConverted<?, ?>) o;
if ( !Objects.equals( columnAlias, that.columnAlias ) ) {
return false;
}
return basicValueConverter.equals( that.basicValueConverter );
}
@Override
public int hashCode() {
int result = columnAlias != null ? columnAlias.hashCode() : 0;
result = 31 * result + basicValueConverter.hashCode();
return result;
}
}

View File

@ -6,10 +6,12 @@
*/
package org.hibernate.query.results.dynamic;
import java.util.Objects;
import java.util.function.BiFunction;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.ResultsHelper;
import org.hibernate.query.results.SqlSelectionImpl;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
@ -107,6 +109,11 @@ public class DynamicResultBuilderBasicStandard implements DynamicResultBuilderBa
return columnName;
}
@Override
public DynamicResultBuilderBasicStandard cacheKeyInstance() {
return this;
}
@Override
public BasicResult<?> buildResult(
JdbcValuesMetadata jdbcResultsMetadata,
@ -166,4 +173,39 @@ public class DynamicResultBuilderBasicStandard implements DynamicResultBuilderBa
return new BasicResult<>( sqlSelection.getValuesArrayPosition(), resultAlias, javaTypeDescriptor );
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
DynamicResultBuilderBasicStandard that = (DynamicResultBuilderBasicStandard) o;
if ( columnPosition != that.columnPosition ) {
return false;
}
if ( !columnName.equals( that.columnName ) ) {
return false;
}
if ( !resultAlias.equals( that.resultAlias ) ) {
return false;
}
if ( !Objects.equals( explicitType, that.explicitType ) ) {
return false;
}
return Objects.equals( explicitJavaTypeDescriptor, that.explicitJavaTypeDescriptor );
}
@Override
public int hashCode() {
int result = columnName.hashCode();
result = 31 * result + columnPosition;
result = 31 * result + resultAlias.hashCode();
result = 31 * result + ( explicitType != null ? explicitType.hashCode() : 0 );
result = 31 * result + ( explicitJavaTypeDescriptor != null ? explicitJavaTypeDescriptor.hashCode() : 0 );
return result;
}
}

View File

@ -14,6 +14,7 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.query.NativeQuery;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.ResultsHelper;
import org.hibernate.sql.ast.spi.SqlAliasBaseConstant;
import org.hibernate.sql.ast.tree.from.TableGroup;
@ -99,6 +100,11 @@ public class DynamicResultBuilderEntityCalculated implements DynamicResultBuilde
throw new UnsupportedOperationException();
}
@Override
public DynamicResultBuilderEntityCalculated cacheKeyInstance() {
return this;
}
@Override
public EntityResult buildResult(
JdbcValuesMetadata jdbcResultsMetadata,
@ -130,4 +136,36 @@ public class DynamicResultBuilderEntityCalculated implements DynamicResultBuilde
domainResultCreationState
);
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
DynamicResultBuilderEntityCalculated that = (DynamicResultBuilderEntityCalculated) o;
if ( !navigablePath.equals( that.navigablePath ) ) {
return false;
}
if ( !entityMapping.equals( that.entityMapping ) ) {
return false;
}
if ( !tableAlias.equals( that.tableAlias ) ) {
return false;
}
return explicitLockMode == that.explicitLockMode;
}
@Override
public int hashCode() {
int result = navigablePath.hashCode();
result = 31 * result + entityMapping.hashCode();
result = 31 * result + tableAlias.hashCode();
result = 31 * result + ( explicitLockMode != null ? explicitLockMode.hashCode() : 0 );
return result;
}
}

View File

@ -9,6 +9,7 @@ package org.hibernate.query.results.dynamic;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
@ -22,6 +23,7 @@ import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping
import org.hibernate.query.NativeQuery;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.SqlSelectionImpl;
import org.hibernate.query.results.TableGroupImpl;
import org.hibernate.sql.ast.spi.FromClauseAccess;
@ -72,6 +74,16 @@ public class DynamicResultBuilderEntityStandard
this.tableAlias = tableAlias;
}
private DynamicResultBuilderEntityStandard(DynamicResultBuilderEntityStandard original) {
super( original );
this.navigablePath = original.navigablePath;
this.entityMapping = original.entityMapping;
this.tableAlias = original.tableAlias;
this.lockMode = original.lockMode;
this.idColumnNames = original.idColumnNames == null ? null : List.copyOf( original.idColumnNames );
this.discriminatorColumnName = original.discriminatorColumnName;
}
@Override
public Class<?> getJavaType() {
return entityMapping.getJavaTypeDescriptor().getJavaTypeClass();
@ -111,6 +123,11 @@ public class DynamicResultBuilderEntityStandard
return entityMapping.getEntityName();
}
@Override
public DynamicResultBuilderEntityStandard cacheKeyInstance() {
return new DynamicResultBuilderEntityStandard( this );
}
@Override
public EntityResult buildResult(
JdbcValuesMetadata jdbcResultsMetadata,
@ -280,4 +297,35 @@ public class DynamicResultBuilderEntityStandard
this.discriminatorColumnName = columnName;
return this;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + navigablePath.hashCode();
result = 31 * result + entityMapping.hashCode();
result = 31 * result + tableAlias.hashCode();
result = 31 * result + ( lockMode != null ? lockMode.hashCode() : 0 );
result = 31 * result + ( idColumnNames != null ? idColumnNames.hashCode() : 0 );
result = 31 * result + ( discriminatorColumnName != null ? discriminatorColumnName.hashCode() : 0 );
return result;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final DynamicResultBuilderEntityStandard that = (DynamicResultBuilderEntityStandard) o;
return navigablePath.equals( that.navigablePath )
&& entityMapping.equals( that.entityMapping )
&& tableAlias.equals( that.tableAlias )
&& lockMode == that.lockMode
&& Objects.equals( idColumnNames, that.idColumnNames )
&& Objects.equals( discriminatorColumnName, that.discriminatorColumnName )
&& super.equals( o );
}
}

View File

@ -13,6 +13,7 @@ import java.util.function.BiFunction;
import org.hibernate.query.DynamicInstantiationNature;
import org.hibernate.query.NativeQuery;
import org.hibernate.query.results.Builders;
import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.ResultBuilderInstantiationValued;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
@ -35,13 +36,53 @@ public class DynamicResultBuilderInstantiation<J>
this.argumentBuilder = argumentBuilder;
this.resultAlias = resultAlias;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
InstantiationArgument that = (InstantiationArgument) o;
if ( !argumentBuilder.equals( that.argumentBuilder ) ) {
return false;
}
return resultAlias.equals( that.resultAlias );
}
@Override
public int hashCode() {
int result = argumentBuilder.hashCode();
result = 31 * result + resultAlias.hashCode();
return result;
}
}
private final JavaType<J> javaTypeDescriptor;
private final List<InstantiationArgument> argumentResultBuilders = new ArrayList<>();
private final List<InstantiationArgument> argumentResultBuilders;
public DynamicResultBuilderInstantiation(JavaType<J> javaTypeDescriptor) {
this.javaTypeDescriptor = javaTypeDescriptor;
this.argumentResultBuilders = new ArrayList<>();
}
private DynamicResultBuilderInstantiation(DynamicResultBuilderInstantiation<J> original) {
this.javaTypeDescriptor = original.javaTypeDescriptor;
final List<InstantiationArgument> arguments = new ArrayList<>( original.argumentResultBuilders.size() );
for ( InstantiationArgument argument : original.argumentResultBuilders ) {
arguments.add(
new InstantiationArgument(
argument.argumentBuilder.cacheKeyInstance(),
argument.resultAlias
)
);
}
this.argumentResultBuilders = arguments;
}
@Override
@ -57,6 +98,11 @@ public class DynamicResultBuilderInstantiation<J>
return this;
}
@Override
public DynamicResultBuilderInstantiation cacheKeyInstance() {
return new DynamicResultBuilderInstantiation( this );
}
@Override
public DomainResult<?> buildResult(
JdbcValuesMetadata jdbcResultsMetadata,
@ -72,7 +118,7 @@ public class DynamicResultBuilderInstantiation<J>
for ( int i = 0; i < argumentResultBuilders.size(); i++ ) {
final InstantiationArgument argument = argumentResultBuilders.get( i );
final ArgumentDomainResult<?> argumentDomainResult = new ArgumentDomainResult(
final ArgumentDomainResult<?> argumentDomainResult = new ArgumentDomainResult<>(
argument.argumentBuilder.buildResult(
jdbcResultsMetadata,
i,
@ -84,11 +130,35 @@ public class DynamicResultBuilderInstantiation<J>
argumentDomainResults.add( argumentDomainResult );
}
return new DynamicInstantiationResultImpl(
return new DynamicInstantiationResultImpl<>(
null,
DynamicInstantiationNature.CLASS,
javaTypeDescriptor,
argumentDomainResults
);
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
DynamicResultBuilderInstantiation<?> that = (DynamicResultBuilderInstantiation<?>) o;
if ( !javaTypeDescriptor.equals( that.javaTypeDescriptor ) ) {
return false;
}
return argumentResultBuilders.equals( that.argumentResultBuilders );
}
@Override
public int hashCode() {
int result = javaTypeDescriptor.hashCode();
result = 31 * result + argumentResultBuilders.hashCode();
return result;
}
}

View File

@ -15,6 +15,7 @@ import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.BasicValuedFetchBuilder;
import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.FetchBuilder;
import org.hibernate.query.results.ResultsHelper;
import org.hibernate.query.results.SqlSelectionImpl;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
@ -41,6 +42,11 @@ public class ImplicitFetchBuilderBasic implements ImplicitFetchBuilder, BasicVal
this.fetchable = fetchable;
}
@Override
public FetchBuilder cacheKeyInstance() {
return this;
}
@Override
public BasicFetch<?> buildFetch(
FetchParent parent,
@ -109,4 +115,25 @@ public class ImplicitFetchBuilderBasic implements ImplicitFetchBuilder, BasicVal
public String toString() {
return "ImplicitFetchBuilderBasic(" + fetchPath + ")";
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final ImplicitFetchBuilderBasic that = (ImplicitFetchBuilderBasic) o;
return fetchPath.equals( that.fetchPath )
&& fetchable.equals( that.fetchable );
}
@Override
public int hashCode() {
int result = fetchPath.hashCode();
result = 31 * result + fetchable.hashCode();
return result;
}
}

View File

@ -6,6 +6,8 @@
*/
package org.hibernate.query.results.implicit;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BiConsumer;
@ -67,6 +69,27 @@ public class ImplicitFetchBuilderEmbeddable implements ImplicitFetchBuilder {
this.fetchBuilders = fetchBuilders;
}
private ImplicitFetchBuilderEmbeddable(ImplicitFetchBuilderEmbeddable original) {
this.fetchPath = original.fetchPath;
this.fetchable = original.fetchable;
final Map<NavigablePath, FetchBuilder> fetchBuilders;
if ( original.fetchBuilders.isEmpty() ) {
fetchBuilders = Collections.emptyMap();
}
else {
fetchBuilders = new HashMap<>( original.fetchBuilders.size() );
for ( Map.Entry<NavigablePath, FetchBuilder> entry : original.fetchBuilders.entrySet() ) {
fetchBuilders.put( entry.getKey(), entry.getValue().cacheKeyInstance() );
}
}
this.fetchBuilders = fetchBuilders;
}
@Override
public FetchBuilder cacheKeyInstance() {
return new ImplicitFetchBuilderEmbeddable( this );
}
@Override
public Fetch buildFetch(
FetchParent parent,
@ -118,6 +141,29 @@ public class ImplicitFetchBuilderEmbeddable implements ImplicitFetchBuilder {
return fetch;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final ImplicitFetchBuilderEmbeddable that = (ImplicitFetchBuilderEmbeddable) o;
return fetchPath.equals( that.fetchPath )
&& fetchable.equals( that.fetchable )
&& fetchBuilders.equals( that.fetchBuilders );
}
@Override
public int hashCode() {
int result = fetchPath.hashCode();
result = 31 * result + fetchable.hashCode();
result = 31 * result + fetchBuilders.hashCode();
return result;
}
@Override
public String toString() {
return "ImplicitFetchBuilderEmbeddable(" + fetchPath + ")";

View File

@ -7,6 +7,7 @@
package org.hibernate.query.results.implicit;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BiConsumer;
@ -92,6 +93,27 @@ public class ImplicitFetchBuilderEntity implements ImplicitFetchBuilder {
this.fetchBuilders = fetchBuilders;
}
private ImplicitFetchBuilderEntity(ImplicitFetchBuilderEntity original) {
this.fetchPath = original.fetchPath;
this.fetchable = original.fetchable;
final Map<NavigablePath, FetchBuilder> fetchBuilders;
if ( original.fetchBuilders.isEmpty() ) {
fetchBuilders = Collections.emptyMap();
}
else {
fetchBuilders = new HashMap<>( original.fetchBuilders.size() );
for ( Map.Entry<NavigablePath, FetchBuilder> entry : original.fetchBuilders.entrySet() ) {
fetchBuilders.put( entry.getKey(), entry.getValue().cacheKeyInstance() );
}
}
this.fetchBuilders = fetchBuilders;
}
@Override
public FetchBuilder cacheKeyInstance() {
return new ImplicitFetchBuilderEntity( this );
}
@Override
public Fetch buildFetch(
FetchParent parent,
@ -126,6 +148,29 @@ public class ImplicitFetchBuilderEntity implements ImplicitFetchBuilder {
fetchBuilders.forEach( (k, v) -> consumer.accept( k.getUnaliasedLocalName(), v ) );
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final ImplicitFetchBuilderEntity that = (ImplicitFetchBuilderEntity) o;
return fetchPath.equals( that.fetchPath )
&& fetchable.equals( that.fetchable )
&& fetchBuilders.equals( that.fetchBuilders );
}
@Override
public int hashCode() {
int result = fetchPath.hashCode();
result = 31 * result + fetchable.hashCode();
result = 31 * result + fetchBuilders.hashCode();
return result;
}
@Override
public String toString() {
return "ImplicitFetchBuilderEntity(" + fetchPath + ")";

View File

@ -0,0 +1,80 @@
/*
* 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 http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.query.results.implicit;
import java.util.function.BiFunction;
import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.FetchBuilder;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
/**
* @author Steve Ebersole
*/
public class ImplicitFetchBuilderEntityPart implements ImplicitFetchBuilder {
private final NavigablePath fetchPath;
private final EntityCollectionPart fetchable;
public ImplicitFetchBuilderEntityPart(NavigablePath fetchPath, EntityCollectionPart fetchable) {
this.fetchPath = fetchPath;
this.fetchable = fetchable;
}
@Override
public FetchBuilder cacheKeyInstance() {
return this;
}
@Override
public Fetch buildFetch(
FetchParent parent,
NavigablePath fetchPath,
JdbcValuesMetadata jdbcResultsMetadata,
BiFunction<String, String, DynamicFetchBuilderLegacy> legacyFetchResolver,
DomainResultCreationState creationState) {
return parent.generateFetchableFetch(
fetchable,
fetchPath,
FetchTiming.IMMEDIATE,
true,
null,
creationState
);
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final ImplicitFetchBuilderEntityPart that = (ImplicitFetchBuilderEntityPart) o;
return fetchPath.equals( that.fetchPath )
&& fetchable.equals( that.fetchable );
}
@Override
public int hashCode() {
int result = fetchPath.hashCode();
result = 31 * result + fetchable.hashCode();
return result;
}
@Override
public String toString() {
return "ImplicitFetchBuilderEntityPart(" + fetchPath + ")";
}
}

View File

@ -12,6 +12,7 @@ import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.FetchBuilder;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetch;
@ -35,6 +36,11 @@ public class ImplicitFetchBuilderPlural implements ImplicitFetchBuilder {
this.fetchable = fetchable;
}
@Override
public FetchBuilder cacheKeyInstance() {
return this;
}
@Override
public Fetch buildFetch(
FetchParent parent,
@ -56,6 +62,27 @@ public class ImplicitFetchBuilderPlural implements ImplicitFetchBuilder {
return fetch;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final ImplicitFetchBuilderPlural that = (ImplicitFetchBuilderPlural) o;
return fetchPath.equals( that.fetchPath )
&& fetchable.equals( that.fetchable );
}
@Override
public int hashCode() {
int result = fetchPath.hashCode();
result = 31 * result + fetchable.hashCode();
return result;
}
@Override
public String toString() {
return "ImplicitFetchBuilderPlural(" + fetchPath + ")";

View File

@ -11,6 +11,7 @@ import java.util.function.BiFunction;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.ResultBuilderBasicValued;
import org.hibernate.query.results.ResultsHelper;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
@ -37,6 +38,11 @@ public class ImplicitModelPartResultBuilderBasic
return modelPart.getExpressableJavaTypeDescriptor().getJavaTypeClass();
}
@Override
public ResultBuilder cacheKeyInstance() {
return this;
}
@Override
public BasicResult<?> buildResult(
JdbcValuesMetadata jdbcResultsMetadata,
@ -50,4 +56,28 @@ public class ImplicitModelPartResultBuilderBasic
.getTableGroup( navigablePath.getParent() );
return (BasicResult<?>) modelPart.createDomainResult( navigablePath, tableGroup, null, domainResultCreationState );
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
ImplicitModelPartResultBuilderBasic that = (ImplicitModelPartResultBuilderBasic) o;
if ( !navigablePath.equals( that.navigablePath ) ) {
return false;
}
return modelPart.equals( that.modelPart );
}
@Override
public int hashCode() {
int result = navigablePath.hashCode();
result = 31 * result + modelPart.hashCode();
return result;
}
}

View File

@ -11,6 +11,7 @@ import java.util.function.BiFunction;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.ResultBuilderEmbeddable;
import org.hibernate.query.results.ResultsHelper;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
@ -42,6 +43,11 @@ public class ImplicitModelPartResultBuilderEmbeddable
return modelPart.getJavaTypeDescriptor().getJavaTypeClass();
}
@Override
public ResultBuilder cacheKeyInstance() {
return this;
}
@Override
public EmbeddableResult buildResult(
JdbcValuesMetadata jdbcResultsMetadata,
@ -88,4 +94,25 @@ public class ImplicitModelPartResultBuilderEmbeddable
);
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final ImplicitModelPartResultBuilderEmbeddable that = (ImplicitModelPartResultBuilderEmbeddable) o;
return navigablePath.equals( that.navigablePath )
&& modelPart.equals( that.modelPart );
}
@Override
public int hashCode() {
int result = navigablePath.hashCode();
result = 31 * result + modelPart.hashCode();
return result;
}
}

View File

@ -12,6 +12,7 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.ResultBuilderEntityValued;
import org.hibernate.query.results.ResultsHelper;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
@ -45,6 +46,11 @@ public class ImplicitModelPartResultBuilderEntity
return modelPart.getJavaTypeDescriptor().getJavaTypeClass();
}
@Override
public ResultBuilder cacheKeyInstance() {
return this;
}
@Override
public EntityResult buildResult(
JdbcValuesMetadata jdbcResultsMetadata,
@ -80,4 +86,28 @@ public class ImplicitModelPartResultBuilderEntity
domainResultCreationState
);
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
ImplicitModelPartResultBuilderEntity that = (ImplicitModelPartResultBuilderEntity) o;
if ( !navigablePath.equals( that.navigablePath ) ) {
return false;
}
return modelPart.equals( that.modelPart );
}
@Override
public int hashCode() {
int result = navigablePath.hashCode();
result = 31 * result + modelPart.hashCode();
return result;
}
}

View File

@ -398,7 +398,7 @@ public class NativeQueryImpl<R>
session.isQueryParametersValidationEnabled()
);
this.resultSetMapping = new ResultSetMappingImpl( sqlString );
this.resultSetMapping = new ResultSetMappingImpl( sqlString, true );
this.resultMappingSuppliedToCtor = false;
}
@ -770,6 +770,7 @@ public class NativeQueryImpl<R>
return new SelectInterpretationsKey(
getQueryString(),
resultSetMapping,
getSynchronizedQuerySpaces(),
getQueryOptions().getTupleTransformer(),
getQueryOptions().getResultListTransformer()
);

View File

@ -7,8 +7,11 @@
package org.hibernate.query.sql.spi;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import org.hibernate.query.spi.QueryInterpretationCache;
import org.hibernate.query.sqm.internal.SqmInterpretationsKey;
/**
* QueryInterpretations key for non-select NativeQuery instances
@ -21,11 +24,43 @@ public class NonSelectInterpretationsKey implements QueryInterpretationCache.Key
public NonSelectInterpretationsKey(String sql, Collection<String> querySpaces) {
this.sql = sql;
this.querySpaces = querySpaces;
this.querySpaces = querySpaces == null ? Collections.emptySet() : querySpaces;
}
@Override
public String getQueryString() {
return sql;
}
@Override
public QueryInterpretationCache.Key prepareForStore() {
return new NonSelectInterpretationsKey(
sql,
querySpaces.isEmpty() ? Collections.emptySet() : new HashSet<>( querySpaces )
);
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
NonSelectInterpretationsKey that = (NonSelectInterpretationsKey) o;
if ( !sql.equals( that.sql ) ) {
return false;
}
return querySpaces.equals( that.querySpaces );
}
@Override
public int hashCode() {
int result = sql.hashCode();
result = 31 * result + querySpaces.hashCode();
return result;
}
}

View File

@ -6,6 +6,8 @@
*/
package org.hibernate.query.sql.spi;
import java.util.Collection;
import java.util.HashSet;
import java.util.Objects;
import org.hibernate.query.ResultListTransformer;
@ -19,18 +21,38 @@ import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
public class SelectInterpretationsKey implements QueryInterpretationCache.Key {
private final String sql;
private final JdbcValuesMappingProducer jdbcValuesMappingProducer;
private final Collection<String> querySpaces;
private final TupleTransformer tupleTransformer;
private final ResultListTransformer resultListTransformer;
private final int hash;
public SelectInterpretationsKey(
String sql,
JdbcValuesMappingProducer jdbcValuesMappingProducer,
Collection<String> querySpaces,
TupleTransformer tupleTransformer,
ResultListTransformer resultListTransformer) {
this.sql = sql;
this.jdbcValuesMappingProducer = jdbcValuesMappingProducer;
this.querySpaces = querySpaces;
this.tupleTransformer = tupleTransformer;
this.resultListTransformer = resultListTransformer;
this.hash = generateHashCode();
}
private SelectInterpretationsKey(
String sql,
JdbcValuesMappingProducer jdbcValuesMappingProducer,
Collection<String> querySpaces,
TupleTransformer tupleTransformer,
ResultListTransformer resultListTransformer,
int hash) {
this.sql = sql;
this.jdbcValuesMappingProducer = jdbcValuesMappingProducer;
this.querySpaces = querySpaces;
this.tupleTransformer = tupleTransformer;
this.resultListTransformer = resultListTransformer;
this.hash = hash;
}
@Override
@ -38,6 +60,32 @@ public class SelectInterpretationsKey implements QueryInterpretationCache.Key {
return sql;
}
@Override
public QueryInterpretationCache.Key prepareForStore() {
return new SelectInterpretationsKey(
sql,
jdbcValuesMappingProducer.cacheKeyInstance(),
new HashSet<>( querySpaces ),
tupleTransformer,
resultListTransformer,
hash
);
}
private int generateHashCode() {
int result = sql.hashCode();
result = 31 * result + jdbcValuesMappingProducer.hashCode();
result = 31 * result + ( querySpaces != null ? querySpaces.hashCode() : 0 );
result = 31 * result + ( tupleTransformer != null ? tupleTransformer.hashCode() : 0 );
result = 31 * result + ( resultListTransformer != null ? resultListTransformer.hashCode() : 0 );
return result;
}
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
@ -47,21 +95,11 @@ public class SelectInterpretationsKey implements QueryInterpretationCache.Key {
return false;
}
SelectInterpretationsKey that = (SelectInterpretationsKey) o;
final SelectInterpretationsKey that = (SelectInterpretationsKey) o;
return sql.equals( that.sql )
&& Objects.equals( jdbcValuesMappingProducer, that.jdbcValuesMappingProducer )
&& Objects.equals( querySpaces, that.querySpaces )
&& Objects.equals( tupleTransformer, that.tupleTransformer )
&& Objects.equals( resultListTransformer, that.resultListTransformer );
}
@Override
public int hashCode() {
int result = sql.hashCode();
result = 31 * result + jdbcValuesMappingProducer.hashCode();
result = 31 * result + ( tupleTransformer != null ? tupleTransformer.hashCode() : 0 );
result = 31 * result + ( resultListTransformer != null ? resultListTransformer.hashCode() : 0 );
return result;
}
}

View File

@ -39,4 +39,7 @@ public interface JdbcValuesMappingProducer {
throw new NotYetImplementedFor6Exception( getClass() );
}
default JdbcValuesMappingProducer cacheKeyInstance() {
return this;
}
}

View File

@ -202,6 +202,10 @@ public class QueryStatisticsImpl implements QueryStatistics {
planCacheHitCount.increment();
}
void incrementPlanCacheMissCount() {
planCacheMissCount.increment();
}
public String toString() {
return "QueryStatistics"
+ "[query=" + query

View File

@ -767,11 +767,20 @@ public class StatisticsImpl implements StatisticsImplementor, Service {
}
@Override
public void queryPlanCacheHit(String hql) {
public void queryPlanCacheHit(String query) {
queryPlanCacheHitCount.increment();
if ( hql != null ) {
getQueryStatistics( hql ).incrementPlanCacheHitCount();
if ( query != null ) {
getQueryStatistics( query ).incrementPlanCacheHitCount();
}
}
@Override
public void queryPlanCacheMiss(String query) {
queryPlanCacheMissCount.increment();
if ( query != null ) {
getQueryStatistics( query ).incrementPlanCacheMissCount();
}
}

View File

@ -251,9 +251,18 @@ public interface StatisticsImplementor extends Statistics, Service {
/**
* Callback indicating a get from the query plan cache resulted in a hit.
*
* @param hql The query
* @param query The query
*/
default void queryPlanCacheHit(String hql) {
default void queryPlanCacheHit(String query) {
//For backward compatibility
}
/**
* Callback indicating a get from the query plan cache resulted in a miss.
*
* @param query The query
*/
default void queryPlanCacheMiss(String query) {
//For backward compatibility
}

View File

@ -17,6 +17,9 @@ import jakarta.persistence.Tuple;
import jakarta.persistence.TypedQuery;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.Query;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.stat.QueryStatistics;
import org.hibernate.stat.Statistics;
@ -51,8 +54,6 @@ public class QueryPlanCacheStatisticsTest {
@BeforeAll
protected void afterEntityManagerFactoryBuilt(SessionFactoryScope scope) {
statistics = scope.getSessionFactory().getStatistics();
scope.inTransaction( entityManager -> {
for ( long i = 1; i <= 5; i++ ) {
Employee employee = new Employee();
@ -64,8 +65,10 @@ public class QueryPlanCacheStatisticsTest {
@BeforeEach
protected void cleanup(SessionFactoryScope scope) {
scope.getSessionFactory().getQueryEngine().getInterpretationCache().close();
final SessionFactoryImplementor sessionFactory = scope.getSessionFactory();
statistics = sessionFactory.getStatistics();
statistics.clear();
sessionFactory.getQueryEngine().getInterpretationCache().close();
}
@Test
@ -125,6 +128,24 @@ public class QueryPlanCacheStatisticsTest {
@Test
@TestForIssue( jiraKey = "HHH-13077" )
public void testCreateQueryHitCount(SessionFactoryScope scope) {
scope.inTransaction( entityManager -> {
QueryImplementor<Employee> query = entityManager.createQuery(
"select e from Employee e", Employee.class );
//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 = query.getResultList();
assertEquals( 5, employees.size() );
//The miss count is 2 because the previous cache miss was for the HqlInterpretation and this is for the plan
assertEquals( 2, statistics.getQueryPlanCacheMissCount() );
assertEquals( 0, statistics.getQueryPlanCacheHitCount() );
} );
scope.inTransaction( entityManager -> {
List<Employee> employees = entityManager.createQuery(
@ -133,6 +154,40 @@ public class QueryPlanCacheStatisticsTest {
assertEquals( 5, employees.size() );
//The miss count is still 2, as now we got the query plan from the cache
assertEquals( 2, statistics.getQueryPlanCacheMissCount() );
//And the cache hit count increases.
assertEquals( 2, statistics.getQueryPlanCacheHitCount() );
} );
scope.inTransaction( entityManager -> {
List<Employee> employees = entityManager.createQuery(
"select e from Employee e", Employee.class )
.getResultList();
assertEquals( 5, employees.size() );
//The miss count is still 2, as now we got the query plan from the cache
assertEquals( 2, statistics.getQueryPlanCacheMissCount() );
//And the cache hit count increases.
assertEquals( 4, statistics.getQueryPlanCacheHitCount() );
} );
}
@Test
@TestForIssue( jiraKey = "HHH-14632" )
public void testCreateNativeQueryHitCount(SessionFactoryScope scope) {
statistics.clear();
scope.inTransaction( entityManager -> {
List<Employee> employees = entityManager.createNativeQuery(
"select * from employee e", Employee.class )
.getResultList();
assertEquals( 5, employees.size() );
//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
@ -141,8 +196,8 @@ public class QueryPlanCacheStatisticsTest {
scope.inTransaction( entityManager -> {
List<Employee> employees = entityManager.createQuery(
"select e from Employee e", Employee.class )
List<Employee> employees = entityManager.createNativeQuery(
"select * from employee e", Employee.class )
.getResultList();
assertEquals( 5, employees.size() );
@ -155,8 +210,8 @@ public class QueryPlanCacheStatisticsTest {
scope.inTransaction( entityManager -> {
List<Employee> employees = entityManager.createQuery(
"select e from Employee e", Employee.class )
List<Employee> employees = entityManager.createNativeQuery(
"select * from employee e", Employee.class )
.getResultList();
assertEquals( 5, employees.size() );
@ -176,15 +231,20 @@ public class QueryPlanCacheStatisticsTest {
statistics.clear();
scope.inTransaction( entityManager -> {
Employee employees = entityManager.createNamedQuery(
"find_employee_by_name", Employee.class )
.setParameter( "name", "Employee: 1" )
.getSingleResult();
Query<Employee> query = entityManager.createNamedQuery(
"find_employee_by_name", Employee.class )
.setParameter( "name", "Employee: 1" );
//The miss count is 0 because the plan was compiled when the EMF was built, and we cleared the Statistics
assertEquals( 0, statistics.getQueryPlanCacheMissCount() );
//The hit count is 1 since we got the plan from the cache
assertEquals( 1, statistics.getQueryPlanCacheHitCount() );
Employee employees = query.getSingleResult();
//The miss count is 1 because the previous cache hit was for the HqlInterpretation and this is for the plan
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
assertEquals( 1, statistics.getQueryPlanCacheHitCount() );
} );
scope.inTransaction( entityManager -> {
@ -194,10 +254,11 @@ public class QueryPlanCacheStatisticsTest {
.setParameter( "name", "Employee: 1" )
.getSingleResult();
//The miss count is still 0 because the plan was compiled when the EMF was built, and we cleared the Statistics
assertEquals( 0, statistics.getQueryPlanCacheMissCount() );
//The hit count is 2 since we got the plan from the cache twice
assertEquals( 2, statistics.getQueryPlanCacheHitCount() );
//The miss count is still 1 because the plan was compiled when the EMF was built, and we cleared the Statistics
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
//The hit count is 3 since we got the plan from the cache twice,
//but the second time we have a hit for the HqlInterpretation and one for the plan
assertEquals( 3, statistics.getQueryPlanCacheHitCount() );
} );
}
@ -206,16 +267,21 @@ public class QueryPlanCacheStatisticsTest {
public void testCreateQueryTupleHitCount(SessionFactoryScope scope) {
scope.inTransaction( entityManager -> {
List<Tuple> employees = entityManager.createQuery(
"select e.id, e.name from Employee e", Tuple.class )
.getResultList();
assertEquals( 5, employees.size() );
QueryImplementor<Tuple> query = entityManager.createQuery(
"select e.id, e.name from Employee e", Tuple.class );
//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<Tuple> employees = query.getResultList();
assertEquals( 5, employees.size() );
//The miss count is 2 because the previous cache miss was for the HqlInterpretation and this is for the plan
assertEquals( 2, statistics.getQueryPlanCacheMissCount() );
assertEquals( 0, statistics.getQueryPlanCacheHitCount() );
} );
scope.inTransaction( entityManager -> {
@ -226,10 +292,10 @@ public class QueryPlanCacheStatisticsTest {
assertEquals( 5, employees.size() );
//The miss count is still 1, as now we got the query plan from the cache
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
//The miss count is still 2, as now we got the query plan from the cache
assertEquals( 2, statistics.getQueryPlanCacheMissCount() );
//And the cache hit count increases.
assertEquals( 1, statistics.getQueryPlanCacheHitCount() );
assertEquals( 2, statistics.getQueryPlanCacheHitCount() );
} );
scope.inTransaction( entityManager -> {
@ -240,10 +306,10 @@ public class QueryPlanCacheStatisticsTest {
assertEquals( 5, employees.size() );
//The miss count is still 1, as now we got the query plan from the cache
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
//The miss count is still 2, as now we got the query plan from the cache
assertEquals( 2, statistics.getQueryPlanCacheMissCount() );
//And the cache hit count increases.
assertEquals( 2, statistics.getQueryPlanCacheHitCount() );
assertEquals( 4, statistics.getQueryPlanCacheHitCount() );
} );
}
@ -253,13 +319,17 @@ public class QueryPlanCacheStatisticsTest {
scope.inTransaction( entityManager -> {
TypedQuery<Employee> typedQuery = entityManager.createQuery( "select e from Employee e", Employee.class );
//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() );
//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
//The miss count is 2 because the previous cache miss was for the HqlInterpretation and this is for the plan
assertEquals( 2, statistics.getQueryPlanCacheMissCount() );
assertEquals( 0, statistics.getQueryPlanCacheHitCount() );
typedQuery.setLockMode( LockModeType.READ );

View File

@ -1,144 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.stat.internal;
import java.util.List;
import java.util.Map;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.NamedQuery;
import jakarta.persistence.Table;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Environment;
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
import org.hibernate.stat.Statistics;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
/**
* @author Gail Badner
*/
@TestForIssue(jiraKey = "HHH-12855")
public class QueryPlanCacheStatisticsTest extends BaseEntityManagerFunctionalTestCase {
private Statistics statistics;
@Override
public Class[] getAnnotatedClasses() {
return new Class[] {
Employee.class
};
}
@Override
protected void addConfigOptions(Map options) {
options.put( Environment.GENERATE_STATISTICS, "true" );
}
@Override
protected void afterEntityManagerFactoryBuilt() {
SessionFactory sessionFactory = entityManagerFactory().unwrap( SessionFactory.class );
statistics = sessionFactory.getStatistics();
doInJPA( this::entityManagerFactory, entityManager -> {
for ( long i = 1; i <= 5; i++ ) {
if ( i % 3 == 0 ) {
entityManager.flush();
}
Employee employee = new Employee();
employee.setName( String.format( "Employee: %d", i ) );
entityManager.persist( employee );
}
} );
}
@Test
@TestForIssue( jiraKey = "HHH-14632" )
public void testCreateNativeQueryHitCount() {
statistics.clear();
doInJPA( this::entityManagerFactory, entityManager -> {
List<Employee> employees = entityManager.createNativeQuery(
"select * from employee e", Employee.class )
.getResultList();
assertEquals( 5, employees.size() );
//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() );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
List<Employee> employees = entityManager.createNativeQuery(
"select * from employee e", Employee.class )
.getResultList();
assertEquals( 5, employees.size() );
//The miss count is still 1, as now we got the query plan from the cache
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
//And the cache hit count increases.
assertEquals( 1, statistics.getQueryPlanCacheHitCount() );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
List<Employee> employees = entityManager.createNativeQuery(
"select * from employee e", Employee.class )
.getResultList();
assertEquals( 5, employees.size() );
//The miss count is still 1, as now we got the query plan from the cache
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
//And the cache hit count increases.
assertEquals( 2, statistics.getQueryPlanCacheHitCount() );
} );
}
@Entity(name = "Employee")
@Table(name = "employee")
@NamedQuery(
name = "find_employee_by_name",
query = "select e from Employee e where e.name = :name"
)
public static class Employee {
@Id
@GeneratedValue
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}