perf work

This commit is contained in:
Steve Ebersole 2019-10-09 11:40:48 -05:00
parent 2502f4c108
commit 6f77e0d261
38 changed files with 470 additions and 288 deletions

View File

@ -1255,9 +1255,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
@Override
@SuppressWarnings({ "unchecked" })
public void addUniqueConstraints(Table table, List uniqueConstraints) {
List<UniqueConstraintHolder> constraintHolders = new ArrayList<>(
CollectionHelper.determineProperSizing( uniqueConstraints.size() )
);
List<UniqueConstraintHolder> constraintHolders = new ArrayList<>( uniqueConstraints.size() );
int keyNameBase = determineCurrentNumberOfUniqueConstraintHolders( table );
for ( String[] columns : ( List<String[]> ) uniqueConstraints ) {

View File

@ -781,7 +781,7 @@ public class TableBinder {
result = java.util.Collections.emptyList();
}
else {
result = new ArrayList<UniqueConstraintHolder>( CollectionHelper.determineProperSizing( annotations.length ) );
result = CollectionHelper.arrayList( annotations.length );
for ( UniqueConstraint uc : annotations ) {
result.add(
new UniqueConstraintHolder()

View File

@ -614,14 +614,13 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
try {
final QueryEngine queryEngine = getFactory().getQueryEngine();
final QueryInterpretationCache interpretationCache = queryEngine.getInterpretationCache();
final SqmStatement sqm = interpretationCache.resolveSqmStatement(
queryString,
s -> queryEngine.getHqlTranslator().interpret( queryString )
);
final QuerySqmImpl query = new QuerySqmImpl(
queryString,
sqm,
interpretationCache.resolveHqlInterpretation(
queryString,
s -> queryEngine.getHqlTranslator().interpret( queryString )
),
resultClass,
this
);
@ -886,7 +885,6 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
try {
return new QuerySqmImpl<>(
"<criteria>",
(SqmStatement) criteriaQuery,
criteriaQuery.getResultType(),
this
@ -902,7 +900,6 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
checkOpen();
try {
return new QuerySqmImpl<>(
"<criteria>",
(SqmUpdateStatement) criteriaUpdate,
null,
this
@ -918,7 +915,6 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
checkOpen();
try {
return new QuerySqmImpl<>(
"<criteria>",
(SqmDeleteStatement) criteriaDelete,
null,
this

View File

@ -28,25 +28,13 @@ public class LoggingHelper {
return UNREFERENCED;
}
if ( role.isRoot() ) {
return StringHelper.collapse( role.getFullPath() );
}
else {
assert role.getParent() != null;
return StringHelper.collapse( role.getParent().getFullPath() ) + '.' + role.getNavigableName();
}
return role.getFullPath();
}
public static String toLoggableString(NavigablePath path) {
assert path != null;
if ( path.isRoot() ) {
return StringHelper.collapse( path.getFullPath() );
}
else {
assert path.getParent() != null;
return StringHelper.collapse( path.getParent().getFullPath() ) + '.' + path.getLocalName();
}
return path.getFullPath();
}
public static String toLoggableString(NavigableRole role, Object key) {

View File

@ -25,6 +25,7 @@ import java.util.function.Function;
* @author Steve Ebersole
*/
public final class CollectionHelper {
public static final int DEFAULT_LIST_CAPACITY = 10;
public static final int MINIMUM_INITIAL_CAPACITY = 16;
public static final float LOAD_FACTOR = 0.75f;
@ -178,8 +179,8 @@ public final class CollectionHelper {
return new ConcurrentHashMap<K, V>( size, loadFactor );
}
public static <T> ArrayList<T> arrayList(int anticipatedSize) {
return new ArrayList<T>( anticipatedSize );
public static <T> ArrayList<T> arrayList(int expectedNumberOfElements) {
return new ArrayList<>( Math.max( expectedNumberOfElements + 1, DEFAULT_LIST_CAPACITY ) );
}
public static <T> Set<T> makeCopy(Set<T> source) {

View File

@ -251,20 +251,35 @@ public class EmbeddableMappingType implements ManagedMappingType {
@Override
public Object disassemble(Object value, SharedSessionContractImplementor session) {
return null;
throw new NotYetImplementedFor6Exception( getClass() );
}
@Override
public void visitDisassembledJdbcValues(
Object value, Clause clause, JdbcValuesConsumer valuesConsumer, SharedSessionContractImplementor session) {
Object value,
Clause clause,
JdbcValuesConsumer valuesConsumer,
SharedSessionContractImplementor session) {
throw new NotYetImplementedFor6Exception( getClass() );
}
@Override
public void visitJdbcValues(
Object value, Clause clause, JdbcValuesConsumer valuesConsumer, SharedSessionContractImplementor session) {
Object value,
Clause clause,
JdbcValuesConsumer valuesConsumer,
SharedSessionContractImplementor session) {
throw new NotYetImplementedFor6Exception( getClass() );
}
@Override
public int getNumberOfAttributeMappings() {
return attributeMappings.size();
}
@Override
public int getNumberOfDeclaredAttributeMappings() {
return getNumberOfAttributeMappings();
}
@Override

View File

@ -18,6 +18,9 @@ import org.hibernate.sql.results.spi.FetchableContainer;
* @author Steve Ebersole
*/
public interface ManagedMappingType extends MappingType, FetchableContainer {
int getNumberOfAttributeMappings();
int getNumberOfDeclaredAttributeMappings();
Collection<AttributeMapping> getAttributeMappings();
/**

View File

@ -156,6 +156,7 @@ public class BasicValuedSingularAttributeMapping extends AbstractSingularAttribu
@Override
public Fetch generateFetch(
FetchParent fetchParent,
NavigablePath fetchablePath,
FetchTiming fetchTiming,
boolean selected,
LockMode lockMode,
@ -173,6 +174,7 @@ public class BasicValuedSingularAttributeMapping extends AbstractSingularAttribu
return new BasicFetch(
sqlSelection.getValuesArrayPosition(),
fetchParent,
fetchablePath,
this,
getAttributeMetadataAccess().resolveAttributeMetadata( null ).isNullable(),
getConverter(),

View File

@ -6,7 +6,6 @@
*/
package org.hibernate.metamodel.mapping.internal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
@ -15,6 +14,8 @@ import org.hibernate.LockMode;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType;
@ -38,7 +39,6 @@ import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.from.CompositeTableGroup;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.results.internal.domain.composite.CompositeFetch;
import org.hibernate.sql.results.internal.domain.composite.CompositeResult;
@ -47,6 +47,7 @@ import org.hibernate.sql.results.spi.DomainResultCreationState;
import org.hibernate.sql.results.spi.Fetch;
import org.hibernate.sql.results.spi.FetchParent;
import org.hibernate.sql.results.spi.Fetchable;
import org.hibernate.type.spi.TypeConfiguration;
/**
* @author Steve Ebersole
@ -106,6 +107,23 @@ public class EmbeddedAttributeMapping
return Arrays.asList( attrColumnNames );
}
@Override
public void visitJdbcTypes(
Consumer<JdbcMapping> action,
Clause clause,
TypeConfiguration typeConfiguration) {
getEmbeddableTypeDescriptor().visitJdbcTypes( action, clause, typeConfiguration );
}
@Override
public void visitJdbcValues(
Object value,
Clause clause,
JdbcValuesConsumer valuesConsumer,
SharedSessionContractImplementor session) {
getEmbeddableTypeDescriptor().visitJdbcValues( value, clause, valuesConsumer, session );
}
@Override
public <T> DomainResult<T> createDomainResult(
NavigablePath navigablePath,
@ -132,13 +150,14 @@ public class EmbeddedAttributeMapping
@Override
public Fetch generateFetch(
FetchParent fetchParent,
NavigablePath fetchablePath,
FetchTiming fetchTiming,
boolean selected,
LockMode lockMode,
String resultVariable,
DomainResultCreationState creationState) {
return new CompositeFetch(
fetchParent.getNavigablePath().append( getFetchableName() ),
fetchablePath,
this,
fetchParent,
fetchTiming,
@ -152,7 +171,7 @@ public class EmbeddedAttributeMapping
Clause clause,
SqmToSqlAstConverter walker,
SqlAstCreationState sqlAstCreationState) {
final List<ColumnReference> columnReferences = new ArrayList<>();
final List<ColumnReference> columnReferences = CollectionHelper.arrayList( attrColumnNames.length );
final TableReference tableReference = tableGroup.resolveTableReference( getContainingTableExpression() );
getEmbeddableTypeDescriptor().visitJdbcTypes(

View File

@ -29,6 +29,7 @@ import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import org.hibernate.AssertionFailure;
@ -103,6 +104,7 @@ import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.FilterHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.jdbc.Expectation;
import org.hibernate.jdbc.Expectations;
import org.hibernate.jdbc.TooManyRowsAffectedException;
@ -5313,16 +5315,24 @@ public abstract class AbstractEntityPersister
return accessOptimizer.getPropertyValues( object );
}
else {
final List<Object> values = new ArrayList<>();
final Object[] values = new Object[ getNumberOfAttributeMappings() ];
final AtomicInteger index = new AtomicInteger( 0 );
//noinspection Convert2Lambda
visitAttributeMappings(
attributeMapping -> {
final AttributeMetadata attributeMetadata = attributeMapping.getAttributeMetadataAccess()
.resolveAttributeMetadata( this );
values.add( attributeMetadata.getPropertyAccess().getGetter().get( object ) );
new Consumer<AttributeMapping>() {
@Override
public void accept(AttributeMapping mapping) {
values[ index.getAndIncrement() ] = mapping.getAttributeMetadataAccess()
.resolveAttributeMetadata( AbstractEntityPersister.this )
.getPropertyAccess()
.getGetter()
.get( object );
}
}
);
return values.toArray();
return values;
}
}
@ -6040,6 +6050,7 @@ public abstract class AbstractEntityPersister
private EntityVersionMapping versionMapping;
private SortedMap<String, AttributeMapping> declaredAttributeMappings = new TreeMap<>();
private Collection<AttributeMapping> attributeMappings;
private ReflectionOptimizer.AccessOptimizer accessOptimizer;
@ -6079,7 +6090,7 @@ public abstract class AbstractEntityPersister
final EntityMetamodel currentEntityMetamodel = this.getEntityMetamodel();
int stateArrayPosition = superMappingType == null ? 0 : superMappingType.getAttributeMappings().size();
int stateArrayPosition = superMappingType == null ? 0 : superMappingType.getNumberOfAttributeMappings();
for ( int i = 0; i < currentEntityMetamodel.getPropertySpan(); i++ ) {
final NonIdentifierAttribute runtimeAttrDefinition = currentEntityMetamodel.getProperties()[i];
@ -6126,6 +6137,20 @@ public abstract class AbstractEntityPersister
subclassMappingTypes.put( sub.getEntityName(), sub );
}
@Override
public int getNumberOfAttributeMappings() {
if ( attributeMappings == null ) {
// force calculation of `attributeMappings`
getAttributeMappings();
}
return attributeMappings.size();
}
@Override
public int getNumberOfDeclaredAttributeMappings() {
return declaredAttributeMappings.size();
}
@Override
public boolean isTypeOrSuperType(EntityMappingType targetType) {
if ( targetType == null ) {
@ -6260,8 +6285,6 @@ public abstract class AbstractEntityPersister
return versionMapping;
}
private Collection<AttributeMapping> attributeMappings;
@Override
public Collection<AttributeMapping> getAttributeMappings() {
if ( attributeMappings == null ) {

View File

@ -118,6 +118,18 @@ public interface EntityPersister extends EntityDefinition, EntityValuedModelPart
return SqlAliasStemHelper.INSTANCE.generateStemFromEntityName( getEntityName() );
}
@Override
default int getNumberOfAttributeMappings() {
// for backwards-compatibility
return getAttributeMappings().size();
}
@Override
default int getNumberOfDeclaredAttributeMappings() {
// for backwards-compatibility
return getAttributeMappings().size();
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// stuff that is persister-centric and/or EntityInfo-centric ~~~~~~~~~~~~~~
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -17,6 +17,7 @@ import org.hibernate.PropertyAccessException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import static org.hibernate.internal.CoreLogging.messageLogger;
@ -39,7 +40,7 @@ public class GetterMethodImpl implements Getter {
@Override
public Object get(Object owner) {
try {
return getterMethod.invoke( owner );
return getterMethod.invoke( owner, ArrayHelper.EMPTY_OBJECT_ARRAY );
}
catch (InvocationTargetException ite) {
throw new PropertyAccessException(

View File

@ -24,14 +24,12 @@ public class NavigablePath implements DotIdentifierSequence {
public static final String IDENTIFIER_MAPPER_PROPERTY = "_identifierMapper";
private final NavigablePath parent;
private final String localName;
private final String fullPath;
private final int hashCode;
public NavigablePath(NavigablePath parent, String navigableName) {
this.parent = parent;
this.localName = navigableName;
// the _identifierMapper is a "hidden property" on entities with composite keys.
// concatenating it will prevent the path from correctly being used to look up
@ -54,31 +52,12 @@ public class NavigablePath implements DotIdentifierSequence {
this.hashCode = fullPath.hashCode();
}
public NavigablePath(NavigablePath parent, String navigableName, String alias) {
assert parent != null;
this.parent = parent;
this.localName = navigableName;
final String prefix;
final String parentFullPath = parent.getFullPath();
prefix = StringHelper.isEmpty( parentFullPath )
? ""
: parentFullPath + '.';
this.fullPath = alias == null ? prefix : prefix + '(' + alias + ')';
this.hashCode = fullPath.hashCode();
}
public NavigablePath(String localName) {
this( localName, null );
}
public NavigablePath(String rootName, String alias) {
this.parent = null;
this.localName = rootName;
this.fullPath = alias == null ? rootName : rootName + '(' + alias + ')';
@ -102,7 +81,7 @@ public class NavigablePath implements DotIdentifierSequence {
}
public String getLocalName() {
return localName;
return parent == null ? fullPath : StringHelper.unqualify( fullPath );
}
public String getFullPath() {

View File

@ -180,7 +180,7 @@ public class QuerySplitter {
final SqmFromClause previousCurrent = currentFromClauseCopy;
try {
SqmFromClause copy = new SqmFromClause();
SqmFromClause copy = new SqmFromClause( fromClause.getNumberOfRoots() );
currentFromClauseCopy = copy;
super.visitFromClause( fromClause );
return copy;

View File

@ -804,7 +804,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
treatHandlerStack.push( new TreatHandlerFromClause() );
try {
final SqmFromClause fromClause = new SqmFromClause();
final SqmFromClause fromClause = new SqmFromClause( parserFromClause.fromClauseSpace().size() );
for ( HqlParser.FromClauseSpaceContext parserSpace : parserFromClause.fromClauseSpace() ) {
final SqmRoot sqmPathRoot = visitFromClauseSpace( parserSpace );
fromClause.addRoot( sqmPathRoot );

View File

@ -7,12 +7,15 @@
package org.hibernate.query.internal;
import java.util.function.Function;
import java.util.function.Supplier;
import org.hibernate.query.ParameterMetadata;
import org.hibernate.query.spi.HqlInterpretation;
import org.hibernate.query.spi.NonSelectQueryPlan;
import org.hibernate.query.spi.ParameterMetadataImplementor;
import org.hibernate.query.spi.QueryInterpretationCache;
import org.hibernate.query.spi.SelectQueryPlan;
import org.hibernate.query.sql.spi.ParameterInterpretation;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.tree.SqmStatement;
/**
@ -25,14 +28,10 @@ public class QueryInterpretationCacheDisabledImpl implements QueryInterpretation
public static final QueryInterpretationCacheDisabledImpl INSTANCE = new QueryInterpretationCacheDisabledImpl();
@Override
public SelectQueryPlan getSelectQueryPlan(Key key) {
public SelectQueryPlan resolveSelectQueryPlan(Key key, Supplier<SelectQueryPlan> creator) {
return null;
}
@Override
public void cacheSelectQueryPlan(Key key, SelectQueryPlan plan) {
}
@Override
public NonSelectQueryPlan getNonSelectQueryPlan(Key key) {
return null;
@ -43,8 +42,36 @@ public class QueryInterpretationCacheDisabledImpl implements QueryInterpretation
}
@Override
public SqmStatement resolveSqmStatement(String queryString, Function<String, SqmStatement<?>> creator) {
return creator.apply( queryString );
public HqlInterpretation resolveHqlInterpretation(String queryString, Function<String, SqmStatement<?>> creator) {
final SqmStatement<?> sqmStatement = creator.apply( queryString );
final DomainParameterXref domainParameterXref;
final ParameterMetadataImplementor parameterMetadata;
if ( sqmStatement.getSqmParameters().isEmpty() ) {
domainParameterXref = DomainParameterXref.empty();
parameterMetadata = ParameterMetadataImpl.EMPTY;
}
else {
domainParameterXref = DomainParameterXref.from( sqmStatement );
parameterMetadata = new ParameterMetadataImpl( domainParameterXref.getQueryParameters() );
}
return new HqlInterpretation() {
@Override
public SqmStatement getSqmStatement() {
return sqmStatement;
}
@Override
public ParameterMetadataImplementor getParameterMetadata() {
return parameterMetadata;
}
@Override
public DomainParameterXref getDomainParameterXref() {
return domainParameterXref;
}
};
}
@Override

View File

@ -7,14 +7,19 @@
package org.hibernate.query.internal;
import java.util.function.Function;
import java.util.function.Supplier;
import org.hibernate.internal.util.collections.BoundedConcurrentHashMap;
import org.hibernate.query.QueryLogger;
import org.hibernate.query.spi.HqlInterpretation;
import org.hibernate.query.spi.NonSelectQueryPlan;
import org.hibernate.query.spi.ParameterMetadataImplementor;
import org.hibernate.query.spi.QueryInterpretationCache;
import org.hibernate.query.spi.QueryPlan;
import org.hibernate.query.spi.SelectQueryPlan;
import org.hibernate.query.spi.SimpleHqlInterpretationImpl;
import org.hibernate.query.sql.spi.ParameterInterpretation;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.tree.SqmStatement;
import org.jboss.logging.Logger;
@ -29,8 +34,12 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation
/**
* The default strong reference count.
*
* @deprecated No longer used
*/
@Deprecated
public static final int DEFAULT_PARAMETER_METADATA_MAX_COUNT = 128;
/**
* The default soft reference count.
*/
@ -41,27 +50,31 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation
*/
private final BoundedConcurrentHashMap<Key, QueryPlan> queryPlanCache;
private final BoundedConcurrentHashMap<String, SqmStatement<?>> sqmStatementCache;
private final BoundedConcurrentHashMap<String, HqlInterpretation> hqlInterpretationCache;
private final BoundedConcurrentHashMap<String, ParameterInterpretation> nativeQueryParamCache;
public QueryInterpretationCacheStandardImpl(int maxQueryPlanCount) {
log.debugf( "Starting QueryPlanCache(%s)", maxQueryPlanCount );
queryPlanCache = new BoundedConcurrentHashMap<>( maxQueryPlanCount, 20, BoundedConcurrentHashMap.Eviction.LIRS );
sqmStatementCache = new BoundedConcurrentHashMap<>( maxQueryPlanCount, 20, BoundedConcurrentHashMap.Eviction.LIRS );
hqlInterpretationCache = new BoundedConcurrentHashMap<>( maxQueryPlanCount, 20, BoundedConcurrentHashMap.Eviction.LIRS );
nativeQueryParamCache = new BoundedConcurrentHashMap<>( maxQueryPlanCount, 20, BoundedConcurrentHashMap.Eviction.LIRS );
}
@Override
public SelectQueryPlan getSelectQueryPlan(Key key) {
public SelectQueryPlan resolveSelectQueryPlan(
Key key,
Supplier<SelectQueryPlan> creator) {
log.tracef( "QueryPlan#getSelectQueryPlan(%s)", key );
return (SelectQueryPlan) queryPlanCache.get( key );
}
@Override
public void cacheSelectQueryPlan(Key key, SelectQueryPlan plan) {
log.tracef( "QueryPlan#cacheSelectQueryPlan(%s)", key );
queryPlanCache.putIfAbsent( key, plan );
final SelectQueryPlan cached = (SelectQueryPlan) queryPlanCache.get( key );
if ( cached != null ) {
return cached;
}
final SelectQueryPlan plan = creator.get();
queryPlanCache.put( key, plan );
return plan;
}
@Override
@ -76,17 +89,37 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation
}
@Override
public SqmStatement resolveSqmStatement(
public HqlInterpretation resolveHqlInterpretation(
String queryString,
Function<String, SqmStatement<?>> creator) {
log.tracef( "QueryPlan#resolveSqmStatement(%s)", queryString );
return sqmStatementCache.computeIfAbsent(
queryString,
s -> {
log.debugf( "Creating and caching SqmStatement - %s", queryString );
return creator.apply( queryString );
}
);
log.tracef( "QueryPlan#resolveHqlInterpretation( `%s` )", queryString );
final HqlInterpretation cached = hqlInterpretationCache.get( queryString );
if ( cached != null ) {
return cached;
}
log.debugf( "Creating and caching HqlInterpretation - %s", queryString );
final SqmStatement<?> sqmStatement = creator.apply( queryString );
final DomainParameterXref domainParameterXref;
final ParameterMetadataImplementor parameterMetadata;
if ( sqmStatement.getSqmParameters().isEmpty() ) {
domainParameterXref = DomainParameterXref.empty();
parameterMetadata = ParameterMetadataImpl.EMPTY;
}
else {
domainParameterXref = DomainParameterXref.from( sqmStatement );
parameterMetadata = new ParameterMetadataImpl( domainParameterXref.getQueryParameters() );
}
final HqlInterpretation interpretation = new SimpleHqlInterpretationImpl(
sqmStatement,
parameterMetadata,
domainParameterXref);
hqlInterpretationCache.put( queryString, interpretation );
return interpretation;
}
@Override

View File

@ -0,0 +1,15 @@
package org.hibernate.query.spi;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.tree.SqmStatement;
/**
* @author Steve Ebersole
*/
public interface HqlInterpretation {
SqmStatement getSqmStatement();
ParameterMetadataImplementor getParameterMetadata();
DomainParameterXref getDomainParameterXref();
}

View File

@ -16,6 +16,7 @@ import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.query.spi.NativeQueryInterpreter;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.config.ConfigurationHelper;
@ -64,6 +65,7 @@ public class QueryEngine {
private final SqmCriteriaNodeBuilder criteriaBuilder;
private final HqlTranslator hqlTranslator;
private final SqmTranslatorFactory sqmTranslatorFactory;
private final NativeQueryInterpreter nativeQueryInterpreter;
private final QueryInterpretationCache interpretationCache;
private final SqmFunctionRegistry sqmFunctionRegistry;
@ -101,6 +103,8 @@ public class QueryEngine {
serviceRegistry
);
this.nativeQueryInterpreter = serviceRegistry.getService( NativeQueryInterpreter.class );
this.interpretationCache = buildInterpretationCache( properties );
this.sqmFunctionRegistry = new SqmFunctionRegistry();
@ -163,20 +167,21 @@ public class QueryEngine {
final boolean explicitUseCache = ConfigurationHelper.getBoolean(
AvailableSettings.QUERY_PLAN_CACHE_ENABLED,
properties,
false
// enabled by default
true
);
final Integer explicitMaxPlanCount = ConfigurationHelper.getInteger(
final Integer explicitMaxPlanSize = ConfigurationHelper.getInteger(
AvailableSettings.QUERY_PLAN_CACHE_MAX_SIZE,
properties
);
if ( explicitUseCache || ( explicitMaxPlanCount != null && explicitMaxPlanCount > 0 ) ) {
return new QueryInterpretationCacheStandardImpl(
explicitMaxPlanCount != null
? explicitMaxPlanCount
: QueryInterpretationCacheStandardImpl.DEFAULT_QUERY_PLAN_MAX_COUNT
);
if ( explicitUseCache || ( explicitMaxPlanSize != null && explicitMaxPlanSize > 0 ) ) {
final int size = explicitMaxPlanSize != null
? explicitMaxPlanSize
: QueryInterpretationCacheStandardImpl.DEFAULT_QUERY_PLAN_MAX_COUNT;
return new QueryInterpretationCacheStandardImpl( size );
}
else {
// disabled
@ -218,6 +223,10 @@ public class QueryEngine {
return sqmTranslatorFactory;
}
public NativeQueryInterpreter getNativeQueryInterpreter() {
return nativeQueryInterpreter;
}
public QueryInterpretationCache getInterpretationCache() {
return interpretationCache;
}

View File

@ -7,14 +7,14 @@
package org.hibernate.query.spi;
import java.util.function.Function;
import java.util.function.Supplier;
import org.hibernate.Incubating;
import org.hibernate.query.sql.spi.ParameterInterpretation;
import org.hibernate.query.sqm.tree.SqmStatement;
/**
* A cache for QueryPlans used (and produced) by the translation
* and execution of a query.
* Cache for various parts of translating or interpreting queries.
*
* @author Steve Ebersole
*/
@ -23,13 +23,15 @@ public interface QueryInterpretationCache {
interface Key {
}
SelectQueryPlan getSelectQueryPlan(Key key);
void cacheSelectQueryPlan(Key key, SelectQueryPlan plan);
SelectQueryPlan resolveSelectQueryPlan(Key key, Supplier<SelectQueryPlan> creator);
NonSelectQueryPlan getNonSelectQueryPlan(Key key);
void cacheNonSelectQueryPlan(Key key, NonSelectQueryPlan plan);
SqmStatement resolveSqmStatement(String queryString, Function<String, SqmStatement<?>> creator);
/**
* todo (6.0) : Doesn't holding these separate from the QueryPlans lead to extra, unnecessary memory use?
*/
HqlInterpretation resolveHqlInterpretation(String queryString, Function<String, SqmStatement<?>> creator);
ParameterInterpretation resolveNativeQueryParameters(String queryString, Function<String, ParameterInterpretation> creator);
@ -45,4 +47,5 @@ public interface QueryInterpretationCache {
* memory until they are replaced by others. It is not considered a memory leak as the cache is bounded.
*/
void close();
}

View File

@ -0,0 +1,43 @@
/*
* 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.spi;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.tree.SqmStatement;
/**
* @author Steve Ebersole
*/
public class SimpleHqlInterpretationImpl implements HqlInterpretation {
private final SqmStatement sqmStatement;
private final ParameterMetadataImplementor parameterMetadata;
private final DomainParameterXref domainParameterXref;
public SimpleHqlInterpretationImpl(
SqmStatement sqmStatement,
ParameterMetadataImplementor parameterMetadata,
DomainParameterXref domainParameterXref) {
this.sqmStatement = sqmStatement;
this.parameterMetadata = parameterMetadata;
this.domainParameterXref = domainParameterXref;
}
@Override
public SqmStatement getSqmStatement() {
return sqmStatement;
}
@Override
public ParameterMetadataImplementor getParameterMetadata() {
return parameterMetadata;
}
@Override
public DomainParameterXref getDomainParameterXref() {
return domainParameterXref;
}
}

View File

@ -76,6 +76,7 @@ import org.hibernate.query.spi.SelectQueryPlan;
import org.hibernate.query.sql.spi.NamedNativeQueryMemento;
import org.hibernate.query.sql.spi.NativeQueryImplementor;
import org.hibernate.query.sql.spi.NativeSelectQueryDefinition;
import org.hibernate.query.sql.spi.NativeSelectQueryPlan;
import org.hibernate.query.sql.spi.NonSelectInterpretationsKey;
import org.hibernate.query.sql.spi.ParameterInterpretation;
import org.hibernate.query.sql.spi.SelectInterpretationsKey;
@ -112,6 +113,7 @@ public class NativeQueryImpl<R>
private boolean autoDiscoverTypes;
private Serializable collectionKey;
private NativeQueryInterpreter nativeQueryInterpreter;
/**
* Constructs a NativeQueryImpl given a sql query defined in the mappings.
@ -344,33 +346,31 @@ public class NativeQueryImpl<R>
@SuppressWarnings("unchecked")
private SelectQueryPlan<R> resolveSelectQueryPlan() {
final NativeQueryInterpreter interpreter = getSessionFactory().getServiceRegistry().getService( NativeQueryInterpreter.class );
SelectQueryPlan<R> queryPlan = null;
final JdbcValuesMappingProducer resultSetMapping = getJdbcValuesMappingProducer();
final RowTransformer rowTransformer = resolveRowTransformer();
final QueryInterpretationCache.Key cacheKey = generateSelectInterpretationsKey( resultSetMapping );
if ( cacheKey != null ) {
queryPlan = getSession().getFactory().getQueryEngine().getInterpretationCache().getSelectQueryPlan( cacheKey );
}
if ( queryPlan == null ) {
queryPlan = interpreter.createQueryPlan(
generateSelectQueryDefinition(),
getSessionFactory()
return getSession().getFactory().getQueryEngine().getInterpretationCache().resolveSelectQueryPlan(
cacheKey,
this::createQueryPlan
);
if ( cacheKey != null ) {
getSession().getFactory()
.getQueryEngine()
.getInterpretationCache()
.cacheSelectQueryPlan( cacheKey, queryPlan );
}
}
else {
return createQueryPlan();
}
}
return queryPlan;
private NativeSelectQueryPlan<R> createQueryPlan() {
final RowTransformer rowTransformer = resolveRowTransformer();
return getSessionFactory().getQueryEngine().getNativeQueryInterpreter().createQueryPlan(
generateSelectQueryDefinition(),
getSessionFactory()
);
}
private JdbcValuesMappingProducer getJdbcValuesMappingProducer() {

View File

@ -15,6 +15,7 @@ import java.util.Set;
import java.util.TreeMap;
import org.hibernate.HibernateException;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.query.QueryLogger;
import org.hibernate.query.internal.QueryParameterNamedImpl;
import org.hibernate.query.internal.QueryParameterPositionalImpl;
@ -42,9 +43,6 @@ public class DomainParameterXref {
* SQM statement
*/
public static DomainParameterXref from(SqmStatement<?> sqmStatement) {
final Map<QueryParameterImplementor<?>, List<SqmParameter>> sqmParamsByQueryParam = new IdentityHashMap<>();
final Map<SqmParameter, QueryParameterImplementor<?>> queryParamBySqmParam = new IdentityHashMap<>();
// `xrefMap` is used to help maintain the proper cardinality between an
// SqmParameter and a QueryParameter. Multiple SqmParameter references
// can map to the same QueryParameter. Consider, e.g.,
@ -90,6 +88,13 @@ public class DomainParameterXref {
return empty();
}
final Map<QueryParameterImplementor<?>, List<SqmParameter>> sqmParamsByQueryParam = new IdentityHashMap<>();
final int sqmParamCount = parameterResolutions.getSqmParameters().size();
final Map<SqmParameter, QueryParameterImplementor<?>> queryParamBySqmParam = new IdentityHashMap<>(
CollectionHelper.determineProperSizing( sqmParamCount )
);
for ( SqmParameter<?> sqmParameter : parameterResolutions.getSqmParameters() ) {
if ( sqmParameter instanceof JpaCriteriaParameter ) {
// see discussion on `SqmJpaCriteriaParameterWrapper#accept`
@ -181,6 +186,10 @@ public class DomainParameterXref {
return sqmParamsByQueryParam.keySet();
}
public int getQueryParameterCount() {
return sqmParamsByQueryParam.size();
}
/**
* Get the mapping of all QueryParameters to the List of its corresponding
* SqmParameters

View File

@ -35,6 +35,7 @@ import org.hibernate.query.internal.ParameterMetadataImpl;
import org.hibernate.query.internal.QueryOptionsImpl;
import org.hibernate.query.internal.QueryParameterBindingsImpl;
import org.hibernate.query.spi.AbstractQuery;
import org.hibernate.query.spi.HqlInterpretation;
import org.hibernate.query.spi.MutableQueryOptions;
import org.hibernate.query.spi.NonSelectQueryPlan;
import org.hibernate.query.spi.ParameterMetadataImplementor;
@ -72,9 +73,9 @@ public class QuerySqmImpl<R>
private final SqmStatement sqmStatement;
private final Class resultType;
private final ParameterMetadataImplementor parameterMetadata;
private final DomainParameterXref domainParameterXref;
private final ParameterMetadataImpl parameterMetadata;
private final QueryParameterBindingsImpl parameterBindings;
private final QueryOptionsImpl queryOptions = new QueryOptionsImpl();
@ -141,27 +142,57 @@ public class QuerySqmImpl<R>
}
}
/**
* Form used for HQL queries
*/
public QuerySqmImpl(
String hqlString,
HqlInterpretation hqlInterpretation,
Class resultType,
SharedSessionContractImplementor producer) {
super( producer );
this.hqlString = hqlString;
this.resultType = resultType;
this.sqmStatement = hqlInterpretation.getSqmStatement();
if ( resultType != null ) {
SqmUtil.verifyIsSelectStatement( sqmStatement );
//noinspection unchecked
checkQueryReturnType( (SqmSelectStatement<R>) sqmStatement, resultType, producer.getFactory() );
}
this.parameterMetadata = hqlInterpretation.getParameterMetadata();
this.domainParameterXref = hqlInterpretation.getDomainParameterXref();
this.parameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, producer.getFactory() );
}
/**
* Form used for criteria queries
*/
public QuerySqmImpl(
SqmStatement sqmStatement,
Class resultType,
SharedSessionContractImplementor producer) {
super( producer );
SqmUtil.verifyIsSelectStatement( sqmStatement );
checkQueryReturnType( (SqmSelectStatement<R>) sqmStatement, resultType, producer.getFactory() );
if ( resultType != null ) {
if ( sqmStatement instanceof SqmDmlStatement ) {
if ( sqmStatement instanceof SqmSelectStatement ) {
//noinspection unchecked
checkQueryReturnType( (SqmSelectStatement<R>) sqmStatement, resultType, producer.getFactory() );
}
else {
assert sqmStatement instanceof SqmDmlStatement;
throw new IllegalArgumentException( "Non-select queries cannot be typed" );
}
}
this.hqlString = hqlString;
this.hqlString = "<criteria>";
this.sqmStatement = sqmStatement;
this.resultType = resultType;
this.domainParameterXref = DomainParameterXref.from( sqmStatement );
if ( ! domainParameterXref.hasParameters() ) {
this.parameterMetadata = ParameterMetadataImpl.EMPTY;
@ -391,27 +422,22 @@ public class QuerySqmImpl<R>
@SuppressWarnings("unchecked")
private SelectQueryPlan<R> resolveSelectQueryPlan() {
// resolve (or make) the QueryPlan. This QueryPlan might be an
// aggregation of multiple plans. QueryPlans can be cached, except
// for in certain circumstances, the determination of which occurs in
// SqmInterpretationsKey#generateFrom - if SqmInterpretationsKey#generateFrom
// returns null the query is not cacheable
SelectQueryPlan<R> queryPlan = null;
// resolve (or make) the QueryPlan. This QueryPlan might be an aggregation of multiple plans.
//
// QueryPlans can be cached, except for in certain circumstances
// - the determination of these circumstances occurs in SqmInterpretationsKey#generateFrom.
// If SqmInterpretationsKey#generateFrom returns null the query is not cacheable
final QueryInterpretationCache.Key cacheKey = SqmInterpretationsKey.generateFrom( this );
if ( cacheKey != null ) {
queryPlan = getSession().getFactory().getQueryEngine().getInterpretationCache().getSelectQueryPlan( cacheKey );
return getSession().getFactory().getQueryEngine().getInterpretationCache().resolveSelectQueryPlan(
cacheKey,
this::buildSelectQueryPlan
);
}
if ( queryPlan == null ) {
queryPlan = buildSelectQueryPlan();
if ( cacheKey != null ) {
getSession().getFactory().getQueryEngine().getInterpretationCache().cacheSelectQueryPlan( cacheKey, queryPlan );
}
else {
return buildSelectQueryPlan();
}
return queryPlan;
}
private SelectQueryPlan<R> buildSelectQueryPlan() {

View File

@ -17,6 +17,7 @@ import java.util.function.Consumer;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.Bindable;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingModelExpressable;
@ -81,7 +82,10 @@ public class SqmUtil {
return Collections.emptyMap();
}
final Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> result = new IdentityHashMap<>();
final int queryParameterCount = domainParameterXref.getQueryParameterCount();
final Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> result = new IdentityHashMap<>(
CollectionHelper.determineProperSizing( queryParameterCount )
);
for ( Map.Entry<QueryParameterImplementor<?>, List<SqmParameter>> entry :
domainParameterXref.getSqmParamByQueryParam().entrySet() ) {

View File

@ -276,7 +276,7 @@ public abstract class BaseSqmToSqlAstConverter
@Override
public QuerySpec visitQuerySpec(SqmQuerySpec sqmQuerySpec) {
final QuerySpec sqlQuerySpec = new QuerySpec( processingStateStack.isEmpty() );
final QuerySpec sqlQuerySpec = new QuerySpec( processingStateStack.isEmpty(), sqmQuerySpec.getFromClause().getNumberOfRoots() );
processingStateStack.push(
new SqlAstQuerySpecProcessingStateImpl(
@ -737,12 +737,12 @@ public abstract class BaseSqmToSqlAstConverter
}
if ( sqmExpression instanceof SqmPath ) {
log.debugf( "Determining mapping-model type for SqmPath : " + sqmExpression );
log.debugf( "Determining mapping-model type for SqmPath : %s ", sqmExpression );
return SqmMappingModelHelper.resolveMappingModelExpressable( sqmExpression, this );
}
log.debugf( "Determining mapping-model type for generalized SqmExpression : " + sqmExpression );
log.debugf( "Determining mapping-model type for generalized SqmExpression : %s", sqmExpression );
final SqmExpressable<?> nodeType = sqmExpression.getNodeType();
final MappingModelExpressable valueMapping = getCreationContext().getDomainModel().resolveMappingExpressable( nodeType );
@ -763,7 +763,7 @@ public abstract class BaseSqmToSqlAstConverter
@SuppressWarnings("WeakerAccess")
protected MappingModelExpressable<?> determineValueMapping(SqmParameter<?> sqmParameter) {
log.debugf( "Determining mapping-model type for SqmParameter : " + sqmParameter );
log.debugf( "Determining mapping-model type for SqmParameter : %s", sqmParameter );
final QueryParameterImplementor<?> queryParameter = domainParameterXref.getQueryParameter( sqmParameter );
final QueryParameterBinding<?> binding = domainParameterBindings.getBinding( queryParameter );

View File

@ -12,8 +12,6 @@ import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.internal.SqmMappingModelHelper;
import org.hibernate.query.sqm.sql.SqlAstCreationState;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath;

View File

@ -6,7 +6,6 @@
*/
package org.hibernate.query.sqm.sql.internal;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -21,6 +20,7 @@ import org.hibernate.engine.profile.FetchProfile;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.graph.spi.AttributeNodeImplementor;
import org.hibernate.graph.spi.GraphImplementor;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.persister.entity.EntityPersister;
@ -71,7 +71,8 @@ public class StandardSqmSelectToSqlAstConverter
private final LoadQueryInfluencers fetchInfluencers;
private final CircularFetchDetector circularFetchDetector = new CircularFetchDetector();
private final List<DomainResult> domainResults = new ArrayList<>();
// prepare for 10 root selections to avoid list growth in most cases
private final List<DomainResult> domainResults = CollectionHelper.arrayList( 10 );
private GraphImplementor<?> currentJpaGraphNode;
@ -154,29 +155,33 @@ public class StandardSqmSelectToSqlAstConverter
@Override
public List<Fetch> visitFetches(FetchParent fetchParent) {
final List<Fetch> fetches = new ArrayList();
final List<Fetch> fetches = CollectionHelper.arrayList( fetchParent.getReferencedMappingType().getNumberOfAttributeMappings() );
final Consumer<Fetchable> fetchableConsumer = fetchable -> {
final Fetch biDirectionalFetch = circularFetchDetector.findBiDirectionalFetch(
fetchParent,
fetchable
);
//noinspection Convert2Lambda
final Consumer<Fetchable> fetchableConsumer = new Consumer<Fetchable>() {
@Override
public void accept(Fetchable fetchable) {
final Fetch biDirectionalFetch = circularFetchDetector.findBiDirectionalFetch(
fetchParent,
fetchable
);
if ( biDirectionalFetch != null ) {
fetches.add( biDirectionalFetch );
return;
}
try {
fetchDepth++;
final Fetch fetch = buildFetch( fetchParent, fetchable );
if ( fetch != null ) {
fetches.add( fetch );
if ( biDirectionalFetch != null ) {
fetches.add( biDirectionalFetch );
return;
}
try {
fetchDepth++;
final Fetch fetch = buildFetch( fetchParent, fetchable );
if ( fetch != null ) {
fetches.add( fetch );
}
}
finally {
fetchDepth--;
}
}
finally {
fetchDepth--;
}
};
@ -297,6 +302,7 @@ public class StandardSqmSelectToSqlAstConverter
try {
return fetchable.generateFetch(
fetchParent,
fetchablePath,
fetchTiming,
joined,
lockMode,

View File

@ -328,6 +328,6 @@ public abstract class AbstractSqmPath<T> extends AbstractSqmExpression<T> implem
@Override
public String toString() {
return getClass().getSimpleName() + '(' + navigablePath.getFullPath() + ')';
return getClass().getSimpleName() + "(" + navigablePath.getFullPath() + ")";
}
}

View File

@ -64,12 +64,12 @@ public class SqmEmbeddedValuedSimplePath<T> extends AbstractSqmSimplePath<T> {
@Override
public <S extends T> SqmTreatedPath<T, S> treatAs(Class<S> treatJavaType) throws PathException {
throw new UnsupportedOperationException();
throw new PathException( "Embeddable paths cannot be TREAT-ed" );
}
@Override
public <S extends T> SqmTreatedPath<T, S> treatAs(EntityDomainType<S> treatTarget) throws PathException {
return null;
throw new PathException( "Embeddable paths cannot be TREAT-ed" );
}
// @Override

View File

@ -10,6 +10,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.sql.ast.spi.SqlAstWalker;
import org.hibernate.sql.ast.tree.SqlAstNode;
@ -19,9 +20,14 @@ import org.hibernate.sql.ast.tree.SqlAstNode;
* @author Steve Ebersole
*/
public class FromClause implements SqlAstNode {
private final List<TableGroup> roots = new ArrayList<>();
private final List<TableGroup> roots;
public FromClause() {
roots = new ArrayList<>();
}
public FromClause(int expectedNumberOfRoots) {
roots = CollectionHelper.arrayList( expectedNumberOfRoots );
}
public List<TableGroup> getRoots() {

View File

@ -24,7 +24,7 @@ import org.hibernate.sql.ast.tree.predicate.PredicateContainer;
public class QuerySpec implements SqlAstNode, PredicateContainer {
private final boolean isRoot;
private final FromClause fromClause = new FromClause();
private final FromClause fromClause;
private final SelectClause selectClause = new SelectClause();
private Predicate whereClauseRestrictions;
@ -34,6 +34,12 @@ public class QuerySpec implements SqlAstNode, PredicateContainer {
public QuerySpec(boolean isRoot) {
this.isRoot = isRoot;
this.fromClause = new FromClause();
}
public QuerySpec(boolean isRoot, int expectedNumberOfRoots) {
this.isRoot = isRoot;
this.fromClause = new FromClause( expectedNumberOfRoots );
}
/**

View File

@ -99,6 +99,7 @@ public class RootBiDirectionalFetchImpl implements BiDirectionalFetch, Fetchable
@Override
public Fetch generateFetch(
FetchParent fetchParent,
NavigablePath fetchablePath,
FetchTiming fetchTiming,
boolean selected,
LockMode lockMode,

View File

@ -39,13 +39,14 @@ public class BasicFetch<T> implements Fetch, BasicResultMappingNode<T> {
public BasicFetch(
int valuesArrayPosition,
FetchParent fetchParent,
NavigablePath fetchablePath,
BasicValuedModelPart valuedMapping,
boolean nullable,
BasicValueConverter valueConverter,
FetchTiming fetchTiming,
DomainResultCreationState creationState) {
this.nullable = nullable;
this.navigablePath = fetchParent.getNavigablePath().append( valuedMapping.getFetchableName() );
this.navigablePath = fetchablePath;
this.fetchParent = fetchParent;
this.valuedMapping = valuedMapping;

View File

@ -10,6 +10,8 @@ import java.util.ArrayList;
import java.util.List;
import org.hibernate.LockMode;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
@ -34,7 +36,7 @@ public abstract class AbstractEntityResultNode extends AbstractFetchParent imple
private final EntityMappingType targetType;
private final List<DomainResult> attributeDomainResults = new ArrayList<>();
private final List<DomainResult> attributeDomainResults;
public AbstractEntityResultNode(
EntityValuedModelPart referencedModelPart,
@ -92,18 +94,20 @@ public abstract class AbstractEntityResultNode extends AbstractFetchParent imple
);
}
// entityDescriptor.visitAttributeMappings(
// mapping -> attributeDomainResults.add(
// mapping.createDomainResult(
// navigablePath.append( mapping.getAttributeName() ),
// entityTableGroup,
// null,
// creationState
// )
// )
// );
// todo (6.0) : handle other special navigables such as discriminator, row-id, tenant-id, etc
attributeDomainResults = CollectionHelper.arrayList( entityDescriptor.getNumberOfAttributeMappings() );
entityDescriptor.visitAttributeMappings(
mapping -> attributeDomainResults.add(
mapping.createDomainResult(
navigablePath.append( mapping.getAttributeName() ),
entityTableGroup,
null,
creationState
)
)
);
}
@Override

View File

@ -37,7 +37,7 @@ public interface DomainResultCreationState {
* BiFunction<FetchParent,Fetchable,LockMode> lockModeResolver)
*
* [1] `selected` refers to the named parameter in
* {@link Fetchable#generateFetch(FetchParent, org.hibernate.engine.FetchTiming, boolean, LockMode, String, DomainResultCreationState)}.
* {@link Fetchable#generateFetch(FetchParent, org.hibernate.query.NavigablePath, org.hibernate.engine.FetchTiming, boolean, LockMode, String, DomainResultCreationState)}.
* For {@link org.hibernate.engine.FetchTiming#IMMEDIATE}, this boolean value indicates
* whether the values for the generated assembler/initializers are or should be available in
* the {@link JdbcValues} being processed. For {@link org.hibernate.engine.FetchTiming#DELAYED} this

View File

@ -10,6 +10,7 @@ import org.hibernate.LockMode;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.query.NavigablePath;
/**
* @author Steve Ebersole
@ -26,6 +27,7 @@ public interface Fetchable extends ModelPart {
Fetch generateFetch(
FetchParent fetchParent,
NavigablePath fetchablePath,
FetchTiming fetchTiming,
boolean selected,
LockMode lockMode,

View File

@ -11,15 +11,12 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import org.hibernate.boot.model.TypeContributor;
import org.hibernate.internal.util.SerializationHelper;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.AbstractTypeDescriptor;
import org.hibernate.type.descriptor.java.EnumJavaTypeDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.SerializableTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
import org.hibernate.type.descriptor.sql.VarbinaryTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
import org.hibernate.type.spi.TypeConfigurationAware;
@ -74,19 +71,34 @@ public class JavaTypeDescriptorRegistry implements JavaTypeDescriptorBaseline.Ba
// descriptor access
public <T> JavaTypeDescriptor<T> getDescriptor(Class<T> javaType) {
return RegistryHelper.INSTANCE.resolveDescriptor(
descriptorsByClass,
javaType,
() -> {
log.debugf(
"Could not find matching scoped JavaTypeDescriptor for requested Java class [%s]; " +
"falling back to static registry",
javaType.getName()
);
return org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry.INSTANCE.getDescriptor( javaType );
}
);
return resolveDescriptor( javaType );
// return RegistryHelper.INSTANCE.resolveDescriptor(
// descriptorsByClass,
// javaType,
// () -> {
// log.debugf(
// "Could not find matching scoped JavaTypeDescriptor for requested Java class [%s]; " +
// "falling back to static registry",
// javaType.getName()
// );
//
// if ( Serializable.class.isAssignableFrom( javaType ) ) {
// return new SerializableTypeDescriptor( javaType );
// }
//
// if ( !AttributeConverter.class.isAssignableFrom( javaType ) ) {
// log.debugf(
// "Could not find matching JavaTypeDescriptor for requested Java class [%s]; using fallback. " +
// "This means Hibernate does not know how to perform certain basic operations in relation to this Java type." +
// "",
// javaType.getName()
// );
// checkEqualsAndHashCode( javaType );
// }
//
// return new FallbackJavaTypeDescriptor<>( javaType );
// }
// );
}
public void addDescriptor(JavaTypeDescriptor descriptor) {
@ -103,15 +115,15 @@ public class JavaTypeDescriptorRegistry implements JavaTypeDescriptorBaseline.Ba
}
public <J> JavaTypeDescriptor<J> resolveDescriptor(Class<J> javaType, Supplier<JavaTypeDescriptor<J>> creator) {
//noinspection unchecked
return descriptorsByClass.computeIfAbsent(
javaType,
jt -> {
final JavaTypeDescriptor<J> jtd = creator.get();
performInjections( jtd );
return jtd;
}
);
final JavaTypeDescriptor cached = descriptorsByClass.get( javaType );
if ( cached != null ) {
//noinspection unchecked
return cached;
}
final JavaTypeDescriptor<J> created = creator.get();
descriptorsByClass.put( javaType, created );
return created;
}
@SuppressWarnings("unchecked")
@ -126,7 +138,7 @@ public class JavaTypeDescriptorRegistry implements JavaTypeDescriptorBaseline.Ba
fallbackDescriptor = new EnumJavaTypeDescriptor( javaType );
}
else if ( Serializable.class.isAssignableFrom( javaType ) ) {
fallbackDescriptor = new OnTheFlySerializableJavaDescriptor( javaType );
fallbackDescriptor = new SerializableTypeDescriptor( javaType );
}
else {
fallbackDescriptor = new JavaTypeDescriptorBasicAdaptor( javaType );
@ -146,7 +158,7 @@ public class JavaTypeDescriptorRegistry implements JavaTypeDescriptorBaseline.Ba
return new DynamicJtd();
}
private class DynamicJtd implements JavaTypeDescriptor<Map> {
private static class DynamicJtd implements JavaTypeDescriptor<Map> {
@Override
public SqlTypeDescriptor getJdbcRecommendedSqlType(SqlTypeDescriptorIndicators context) {
throw new UnsupportedOperationException();
@ -172,64 +184,4 @@ public class JavaTypeDescriptorRegistry implements JavaTypeDescriptorBaseline.Ba
return Map.class;
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private class OnTheFlySerializableJavaDescriptor<T extends Serializable> extends AbstractTypeDescriptor<T> {
private final SqlTypeDescriptor sqlTypeDescriptor;
public OnTheFlySerializableJavaDescriptor(Class<T> type) {
super( type );
// todo (6.0) : would be nice to expose for config by user
// todo (6.0) : ^^ might also be nice to allow them to plug in a "JavaTypeDescriptorResolver"
// - that allows them to hook into the #getDescriptor call either as the primary or as a fallback
log.debugf(
"Could not find matching JavaTypeDescriptor for requested Java class [%s]; using fallback via its Serializable interface. " +
"This means Hibernate does not know how to perform certain basic operations in relation to this Java type" +
"which can lead to those operations having a large performance impact. Consider registering these " +
"JavaTypeDescriptors with the %s during bootstrap, either directly or through a registered %s " +
"accessing the %s ",
getJavaType().getName(),
JavaTypeDescriptorRegistry.class.getName(),
TypeContributor.class.getName(),
TypeConfiguration.class.getName()
);
sqlTypeDescriptor = VarbinaryTypeDescriptor.INSTANCE;
}
@Override
public SqlTypeDescriptor getJdbcRecommendedSqlType(SqlTypeDescriptorIndicators context) {
return sqlTypeDescriptor;
}
@Override
public <X> X unwrap(T value, Class<X> type, WrapperOptions options) {
if ( type.equals( byte[].class ) ) {
throw new UnsupportedOperationException( "Cannot unwrap Serializable to format other than byte[]" );
}
return (X) SerializationHelper.serialize( value );
}
@Override
@SuppressWarnings("unchecked")
public <X> T wrap(X value, WrapperOptions options) {
if ( value == null ) {
return null;
}
if ( value.getClass().equals( byte[].class ) ) {
throw new UnsupportedOperationException( "Cannot unwrap Serializable to format other than byte[]" );
}
final byte[] bytes = (byte[]) value;
return (T) SerializationHelper.deserialize( bytes );
}
}
}