initial work on support for discrim-inheritance;

started design doc about SQM model, building and translation;
initial work on `#load` support (strange error in BasicFormatterImpl as part of SqlStatementLogger)
This commit is contained in:
Steve Ebersole 2019-10-18 12:46:22 -05:00
parent 26d914f414
commit 31e2423d8a
20 changed files with 607 additions and 32 deletions

29
design/sqm.adoc Normal file
View File

@ -0,0 +1,29 @@
== SQM
The Semantic Query Model (SQM) is Hibernate's representation of an HQL or Criteria query's semantic (meaning). This
representation is modeled as an "abstract syntax tree" (AST) - meaning it is a structured tree of nodes where each node
represrents an atomic piece of the query. E.g. `SqmSelectClause` represents the query's select clause as you might
imagine. That `SqmSelectClause` is ultimately a collection of one or more `SqmSelection` references representing the
individual selections to be returned from the query (called the domain results).
=== The Model
This SQM model uses the Hibernate domain model, which is Hibernate's extension to the JPA model. This model contains no
relational mapping information, it simply describes the domain model in mostly Java terms although it does incorporate
"classifications" of the type system. E.g. it understand that `Customer` is an entity, but contains no information
about the tables it maps to nor its columns.
See the `design/type-system-domain.adoc` design doc. For details about this domain model
=== Building an SQM
=== Interpreting an SQM
Ultimately Hibernate needs to talk with the database to fulfill these query executions. This is currently a 2-step process.
First we convert the SQM into a new AST called the SQL AST. This is an AST that is more "SQL-y". It's nodes are defined
in terms of Hibernate's mapping model which is the model that actually does understand the relational mapping.
See `design/type-system-mapping.adoc` for details about this model. Part of this conversion step is to resolving
domain model references to mapping model references...

View File

@ -95,6 +95,8 @@ public class BasicFormatterImpl implements Formatter {
String lcToken; String lcToken;
public FormatProcess(String sql) { public FormatProcess(String sql) {
assert sql != null : "SQL to format should not be null";
tokens = new StringTokenizer( tokens = new StringTokenizer(
sql, sql,
"()+*/-=<>'`\"[]," + StringHelper.WHITESPACE, "()+*/-=<>'`\"[]," + StringHelper.WHITESPACE,

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.loader.internal; package org.hibernate.loader.internal;
import java.io.Serializable;
import java.util.EnumMap; import java.util.EnumMap;
import org.hibernate.LockMode; import org.hibernate.LockMode;
@ -14,9 +15,11 @@ import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.loader.entity.BatchingEntityLoaderBuilder;
import org.hibernate.loader.spi.InternalFetchProfile; import org.hibernate.loader.spi.InternalFetchProfile;
import org.hibernate.loader.spi.SingleIdEntityLoader; import org.hibernate.loader.spi.SingleIdEntityLoader;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.exec.spi.JdbcSelect;
/** /**
@ -45,14 +48,21 @@ public class SingleIdEntityLoaderStandardImpl<T> implements SingleIdEntityLoader
@Override @Override
public T load(Object key, LockOptions lockOptions, SharedSessionContractImplementor session) { public T load(Object key, LockOptions lockOptions, SharedSessionContractImplementor session) {
// todo (6.0) : TEMPORARY - use the legacy loaders
//noinspection unchecked
return (T) BatchingEntityLoaderBuilder.getBuilder( session.getFactory() )
.buildLoader( (OuterJoinLoadable) entityDescriptor, -1, lockOptions.getLockMode(), session.getFactory(), session.getLoadQueryInfluencers() )
.load( (Serializable) key, null, session, lockOptions );
// todo (6.0) : see `org.hibernate.loader.internal.StandardSingleIdEntityLoader#load` in "upstream" 6.0 branch // todo (6.0) : see `org.hibernate.loader.internal.StandardSingleIdEntityLoader#load` in "upstream" 6.0 branch
// - and integrate as much as possible with the `o.h.loader.plan` stuff leveraging the similarities // - and integrate as much as possible with the `o.h.loader.plan` stuff leveraging the similarities
// between the legacy LoadPlan stuff and DomainResult, Assembler, etc. // between the legacy LoadPlan stuff and DomainResult, Assembler, etc.
//
final JdbcSelect jdbcSelect = resolveJdbcSelect( lockOptions, session ); // final JdbcSelect jdbcSelect = resolveJdbcSelect( lockOptions, session );
//
throw new NotYetImplementedFor6Exception( getClass() ); // throw new NotYetImplementedFor6Exception( getClass() );
} }
@Override @Override

View File

@ -6,6 +6,9 @@
*/ */
package org.hibernate.loader.spi; package org.hibernate.loader.spi;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.LoadQueryInfluencers;
@ -17,6 +20,7 @@ import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.tree.from.RootTableGroupProducer; import org.hibernate.sql.ast.tree.from.RootTableGroupProducer;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.predicate.Predicate;
/** /**
* Contract for things that can be loaded by a Loader. * Contract for things that can be loaded by a Loader.
@ -40,6 +44,7 @@ public interface Loadable extends ModelPart, RootTableGroupProducer {
LockMode lockMode, LockMode lockMode,
SqlAliasBaseGenerator aliasBaseGenerator, SqlAliasBaseGenerator aliasBaseGenerator,
SqlExpressionResolver sqlExpressionResolver, SqlExpressionResolver sqlExpressionResolver,
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
throw new NotYetImplementedFor6Exception( getClass() ); throw new NotYetImplementedFor6Exception( getClass() );
} }

View File

@ -0,0 +1,14 @@
/*
* 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.metamodel.mapping;
/**
* @author Steve Ebersole
*/
public interface EntityDiscriminatorMapping extends VirtualModelPart, BasicValuedModelPart {
String ROLE_NAME = "{discriminator}";
}

View File

@ -33,6 +33,10 @@ public interface EntityMappingType extends ManagedMappingType {
EntityVersionMapping getVersionMapping(); EntityVersionMapping getVersionMapping();
default EntityDiscriminatorMapping getDiscriminatorMapping() {
return null;
}
NaturalIdMapping getNaturalIdMapping(); NaturalIdMapping getNaturalIdMapping();
@Override @Override

View File

@ -0,0 +1,16 @@
/*
* 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.metamodel.mapping;
/**
* Marker interface for parts of the application domain model that are virtual - do not
* actually exist in the model classes
*
* @author Steve Ebersole
*/
public interface VirtualModelPart extends ModelPart {
}

View File

@ -121,8 +121,8 @@ public class BasicValuedSingularAttributeMapping extends AbstractSingularAttribu
getMappedColumnExpression() getMappedColumnExpression()
), ),
sqlAstProcessingState -> new ColumnReference( sqlAstProcessingState -> new ColumnReference(
getMappedColumnExpression(),
tableReference.getIdentificationVariable(), tableReference.getIdentificationVariable(),
getMappedColumnExpression(),
jdbcMapping, jdbcMapping,
creationState.getSqlAstCreationState().getCreationContext().getSessionFactory() creationState.getSqlAstCreationState().getCreationContext().getSessionFactory()
) )
@ -149,8 +149,8 @@ public class BasicValuedSingularAttributeMapping extends AbstractSingularAttribu
getMappedColumnExpression() getMappedColumnExpression()
), ),
sqlAstProcessingState -> new ColumnReference( sqlAstProcessingState -> new ColumnReference(
getMappedColumnExpression(),
tableReference.getIdentificationVariable(), tableReference.getIdentificationVariable(),
getMappedColumnExpression(),
jdbcMapping, jdbcMapping,
creationState.getSqlAstCreationState().getCreationContext().getSessionFactory() creationState.getSqlAstCreationState().getCreationContext().getSessionFactory()
) )

View File

@ -189,8 +189,8 @@ public class EmbeddedAttributeMapping
attrColumnExpr attrColumnExpr
), ),
sqlAstProcessingState -> new ColumnReference( sqlAstProcessingState -> new ColumnReference(
attrColumnExpr,
tableReference.getIdentificationVariable(), tableReference.getIdentificationVariable(),
attrColumnExpr,
jdbcMapping, jdbcMapping,
sqlAstCreationState.getCreationContext().getSessionFactory() sqlAstCreationState.getCreationContext().getSessionFactory()
) )

View File

@ -0,0 +1,144 @@
/*
* 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.metamodel.mapping.internal;
import org.hibernate.LockMode;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.sql.SqlAstCreationState;
import org.hibernate.query.sqm.sql.SqlExpressionResolver;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.results.internal.domain.basic.BasicFetch;
import org.hibernate.sql.results.spi.DomainResultCreationState;
import org.hibernate.sql.results.spi.Fetch;
import org.hibernate.sql.results.spi.FetchParent;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
/**
* @author Steve Ebersole
*/
public class EntityDiscriminatorMappingImpl implements EntityDiscriminatorMapping {
private final EntityPersister entityDescriptor;
private final String tableExpression;
private final String mappedColumnExpression;
private final BasicType mappingType;
public EntityDiscriminatorMappingImpl(
EntityPersister entityDescriptor,
String tableExpression,
String mappedColumnExpression,
BasicType mappingType) {
this.entityDescriptor = entityDescriptor;
this.tableExpression = tableExpression;
this.mappedColumnExpression = mappedColumnExpression;
this.mappingType = mappingType;
}
@Override
public String getContainingTableExpression() {
return tableExpression;
}
@Override
public String getMappedColumnExpression() {
return mappedColumnExpression;
}
@Override
public BasicValueConverter getConverter() {
return null;
}
@Override
public String getFetchableName() {
return ROLE_NAME;
}
@Override
public FetchStrategy getMappedFetchStrategy() {
return FetchStrategy.IMMEDIATE_JOIN;
}
@Override
public Fetch generateFetch(
FetchParent fetchParent,
NavigablePath fetchablePath,
FetchTiming fetchTiming,
boolean selected,
LockMode lockMode,
String resultVariable,
DomainResultCreationState creationState) {
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
final TableGroup tableGroup = sqlAstCreationState.getFromClauseAccess().getTableGroup(
fetchParent.getNavigablePath()
);
assert tableGroup != null;
final SqlSelection sqlSelection = resolveSqlSelection( tableGroup, creationState );
return new BasicFetch(
sqlSelection.getValuesArrayPosition(),
fetchParent,
fetchablePath,
this,
false,
getConverter(),
fetchTiming,
creationState
);
}
private SqlSelection resolveSqlSelection(TableGroup tableGroup, DomainResultCreationState creationState) {
final SqlExpressionResolver expressionResolver = creationState.getSqlAstCreationState().getSqlExpressionResolver();
final TableReference tableReference = tableGroup.resolveTableReference( getContainingTableExpression() );
return expressionResolver.resolveSqlSelection(
expressionResolver.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey(
tableReference,
getMappedColumnExpression()
),
sqlAstProcessingState -> new ColumnReference(
tableReference.getIdentificationVariable(),
getMappedColumnExpression(),
mappingType.getJdbcMapping(),
creationState.getSqlAstCreationState().getCreationContext().getSessionFactory()
)
),
getMappedTypeDescriptor().getMappedJavaTypeDescriptor(),
creationState.getSqlAstCreationState().getCreationContext().getDomainModel().getTypeConfiguration()
);
}
@Override
public JavaTypeDescriptor getJavaTypeDescriptor() {
return getMappedTypeDescriptor().getMappedJavaTypeDescriptor();
}
@Override
public MappingType getMappedTypeDescriptor() {
return mappingType;
}
@Override
public JdbcMapping getJdbcMapping() {
return mappingType.getJdbcMapping();
}
}

View File

@ -135,8 +135,8 @@ public class MappingModelCreationHelper {
final Expression expression = expressionResolver.resolveSqlExpression( final Expression expression = expressionResolver.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey( rootTableReference, pkColumnName ), SqlExpressionResolver.createColumnReferenceKey( rootTableReference, pkColumnName ),
sqlAstProcessingState -> new ColumnReference( sqlAstProcessingState -> new ColumnReference(
pkColumnName,
rootTableReference.getIdentificationVariable(), rootTableReference.getIdentificationVariable(),
pkColumnName,
( (BasicValuedMapping) entityPersister.getIdentifierType() ).getJdbcMapping(), ( (BasicValuedMapping) entityPersister.getIdentifierType() ).getJdbcMapping(),
creationProcess.getCreationContext().getSessionFactory() creationProcess.getCreationContext().getSessionFactory()
) )
@ -167,8 +167,8 @@ public class MappingModelCreationHelper {
final Expression expression = expressionResolver.resolveSqlExpression( final Expression expression = expressionResolver.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey( rootTableReference, pkColumnName ), SqlExpressionResolver.createColumnReferenceKey( rootTableReference, pkColumnName ),
sqlAstProcessingState -> new ColumnReference( sqlAstProcessingState -> new ColumnReference(
pkColumnName,
rootTable, rootTable,
pkColumnName,
( (BasicValuedModelPart) entityPersister.getIdentifierType() ).getJdbcMapping(), ( (BasicValuedModelPart) entityPersister.getIdentifierType() ).getJdbcMapping(),
creationProcess.getCreationContext().getSessionFactory() creationProcess.getCreationContext().getSessionFactory()
) )

View File

@ -31,6 +31,7 @@ import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
import org.hibernate.EntityMode; import org.hibernate.EntityMode;
@ -104,7 +105,6 @@ import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.FilterHelper; import org.hibernate.internal.FilterHelper;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.jdbc.Expectation; import org.hibernate.jdbc.Expectation;
import org.hibernate.jdbc.Expectations; import org.hibernate.jdbc.Expectations;
import org.hibernate.jdbc.TooManyRowsAffectedException; import org.hibernate.jdbc.TooManyRowsAffectedException;
@ -134,6 +134,7 @@ import org.hibernate.metadata.ClassMetadata;
import org.hibernate.metamodel.RepresentationMode; import org.hibernate.metamodel.RepresentationMode;
import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.AttributeMetadata; import org.hibernate.metamodel.mapping.AttributeMetadata;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.EntityVersionMapping; import org.hibernate.metamodel.mapping.EntityVersionMapping;
@ -144,6 +145,7 @@ import org.hibernate.metamodel.mapping.NaturalIdMapping;
import org.hibernate.metamodel.mapping.Queryable; import org.hibernate.metamodel.mapping.Queryable;
import org.hibernate.metamodel.mapping.StateArrayContributorMapping; import org.hibernate.metamodel.mapping.StateArrayContributorMapping;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadata; import org.hibernate.metamodel.mapping.StateArrayContributorMetadata;
import org.hibernate.metamodel.mapping.internal.EntityDiscriminatorMappingImpl;
import org.hibernate.metamodel.mapping.internal.InFlightEntityMappingType; import org.hibernate.metamodel.mapping.internal.InFlightEntityMappingType;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper; import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
@ -179,6 +181,7 @@ import org.hibernate.sql.ast.spi.SqlAliasStemHelper;
import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.from.StandardTableGroup; import org.hibernate.sql.ast.tree.from.StandardTableGroup;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
@ -1180,6 +1183,7 @@ public abstract class AbstractEntityPersister
LockMode lockMode, LockMode lockMode,
SqlAliasBaseGenerator aliasBaseGenerator, SqlAliasBaseGenerator aliasBaseGenerator,
SqlExpressionResolver sqlExpressionResolver, SqlExpressionResolver sqlExpressionResolver,
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() ); final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() );
@ -1274,8 +1278,8 @@ public abstract class AbstractEntityPersister
rootPkColumnName rootPkColumnName
), ),
sqlAstProcessingState -> new ColumnReference( sqlAstProcessingState -> new ColumnReference(
rootPkColumnName,
rootTableReference.getIdentificationVariable(), rootTableReference.getIdentificationVariable(),
rootPkColumnName,
jdbcMapping, jdbcMapping,
getFactory() getFactory()
) )
@ -1288,8 +1292,8 @@ public abstract class AbstractEntityPersister
fkColumnName fkColumnName
), ),
sqlAstProcessingState -> new ColumnReference( sqlAstProcessingState -> new ColumnReference(
fkColumnName,
joinedTableReference.getIdentificationVariable(), joinedTableReference.getIdentificationVariable(),
fkColumnName,
jdbcMapping, jdbcMapping,
getFactory() getFactory()
) )
@ -5404,18 +5408,22 @@ public abstract class AbstractEntityPersister
return this; return this;
} }
else { else {
final String concreteEntityName = getEntityTuplizer().determineConcreteSubclassEntityName( // todo (6.0) : this previously used `org.hibernate.tuple.entity.EntityTuplizer#determineConcreteSubclassEntityName`
instance, // - we may need something similar here...
factory
); if ( getRepresentationStrategy().getInstantiator().isInstance( instance, factory ) ) {
if ( concreteEntityName == null || getEntityName().equals( concreteEntityName ) ) {
// the contract of EntityTuplizer.determineConcreteSubclassEntityName says that returning null
// is an indication that the specified entity-name (this.getEntityName) should be used.
return this; return this;
} }
else {
return factory.getEntityPersister( concreteEntityName ); if ( hasSubclasses() ) {
for ( EntityMappingType sub : subclassMappingTypes.values() ) {
if ( sub.getEntityPersister().getRepresentationStrategy().getInstantiator().isInstance( instance, factory ) ) {
return sub.getEntityPersister();
}
}
} }
return this;
} }
} }
@ -6048,6 +6056,7 @@ public abstract class AbstractEntityPersister
private EntityIdentifierMapping identifierMapping; private EntityIdentifierMapping identifierMapping;
private NaturalIdMapping naturalIdMapping; private NaturalIdMapping naturalIdMapping;
private EntityVersionMapping versionMapping; private EntityVersionMapping versionMapping;
private EntityDiscriminatorMapping discriminatorMapping;
private SortedMap<String, AttributeMapping> declaredAttributeMappings = new TreeMap<>(); private SortedMap<String, AttributeMapping> declaredAttributeMappings = new TreeMap<>();
private Collection<AttributeMapping> attributeMappings; private Collection<AttributeMapping> attributeMappings;
@ -6088,6 +6097,18 @@ public abstract class AbstractEntityPersister
); );
} }
if ( getDiscriminatorType() == null ) {
discriminatorMapping = null;
}
else {
discriminatorMapping = new EntityDiscriminatorMappingImpl(
this,
getRootTableName(),
getDiscriminatorColumnName(),
(BasicType) getDiscriminatorType()
);
}
final EntityMetamodel currentEntityMetamodel = this.getEntityMetamodel(); final EntityMetamodel currentEntityMetamodel = this.getEntityMetamodel();
int stateArrayPosition = superMappingType == null ? 0 : superMappingType.getNumberOfAttributeMappings(); int stateArrayPosition = superMappingType == null ? 0 : superMappingType.getNumberOfAttributeMappings();
@ -6285,6 +6306,11 @@ public abstract class AbstractEntityPersister
return versionMapping; return versionMapping;
} }
@Override
public EntityDiscriminatorMapping getDiscriminatorMapping() {
return discriminatorMapping;
}
@Override @Override
public Collection<AttributeMapping> getAttributeMappings() { public Collection<AttributeMapping> getAttributeMappings() {
if ( attributeMappings == null ) { if ( attributeMappings == null ) {

View File

@ -14,8 +14,11 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.boot.model.relational.Database; import org.hibernate.boot.model.relational.Database;
import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.cache.spi.access.EntityDataAccess;
@ -37,10 +40,23 @@ import org.hibernate.mapping.Subclass;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.mapping.Value; import org.hibernate.mapping.Value;
import org.hibernate.persister.spi.PersisterCreationContext; import org.hibernate.persister.spi.PersisterCreationContext;
import org.hibernate.query.ComparisonOperator;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.sql.SqlExpressionResolver;
import org.hibernate.sql.InFragment; import org.hibernate.sql.InFragment;
import org.hibernate.sql.Insert; import org.hibernate.sql.Insert;
import org.hibernate.sql.SelectFragment; import org.hibernate.sql.SelectFragment;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.JoinType;
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.type.AssociationType; import org.hibernate.type.AssociationType;
import org.hibernate.type.BasicType;
import org.hibernate.type.DiscriminatorType; import org.hibernate.type.DiscriminatorType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
@ -847,4 +863,60 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
public FilterAliasGenerator getFilterAliasGenerator(String rootAlias) { public FilterAliasGenerator getFilterAliasGenerator(String rootAlias) {
return new DynamicFilterAliasGenerator( qualifiedTableNames, rootAlias ); return new DynamicFilterAliasGenerator( qualifiedTableNames, rootAlias );
} }
@Override
public TableGroup createRootTableGroup(
NavigablePath navigablePath,
String explicitSourceAlias,
JoinType tableReferenceJoinType,
LockMode lockMode,
SqlAliasBaseGenerator aliasBaseGenerator,
SqlExpressionResolver sqlExpressionResolver,
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
SqlAstCreationContext creationContext) {
final TableGroup tableGroup = super.createRootTableGroup(
navigablePath,
explicitSourceAlias,
tableReferenceJoinType,
lockMode,
aliasBaseGenerator,
sqlExpressionResolver,
additionalPredicateCollectorAccess,
creationContext
);
if ( needsDiscriminator() ) {
final Predicate discriminatorPredicate = createDiscriminatorPredicate(
tableGroup,
sqlExpressionResolver,
creationContext
);
additionalPredicateCollectorAccess.get().accept( discriminatorPredicate );
}
return tableGroup;
}
private Predicate createDiscriminatorPredicate(
TableGroup tableGroup,
SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) {
return new ComparisonPredicate(
sqlExpressionResolver.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey( tableGroup.getPrimaryTableReference(), getDiscriminatorColumnName() ),
sqlAstProcessingState -> new ColumnReference(
tableGroup.getPrimaryTableReference().getIdentificationVariable(),
getDiscriminatorColumnName(),
( (BasicType) getDiscriminatorType() ).getJdbcMapping(),
getFactory()
)
),
ComparisonOperator.EQUAL,
new QueryLiteral<>(
getDiscriminatorValue(),
( (BasicType) getDiscriminatorType() ),
Clause.WHERE
)
);
}
} }

View File

@ -37,6 +37,7 @@ public final class PersisterFactoryImpl implements PersisterFactory, ServiceRegi
/** /**
* The constructor signature for {@link EntityPersister} implementations * The constructor signature for {@link EntityPersister} implementations
*/ */
@SuppressWarnings({"WeakerAccess", "deprecation"})
public static final Class[] ENTITY_PERSISTER_CONSTRUCTOR_ARGS = new Class[] { public static final Class[] ENTITY_PERSISTER_CONSTRUCTOR_ARGS = new Class[] {
PersistentClass.class, PersistentClass.class,
EntityDataAccess.class, EntityDataAccess.class,
@ -47,6 +48,7 @@ public final class PersisterFactoryImpl implements PersisterFactory, ServiceRegi
/** /**
* The constructor signature for {@link CollectionPersister} implementations * The constructor signature for {@link CollectionPersister} implementations
*/ */
@SuppressWarnings({"WeakerAccess", "deprecation"})
public static final Class[] COLLECTION_PERSISTER_CONSTRUCTOR_ARGS = new Class[] { public static final Class[] COLLECTION_PERSISTER_CONSTRUCTOR_ARGS = new Class[] {
Collection.class, Collection.class,
CollectionDataAccess.class, CollectionDataAccess.class,
@ -66,7 +68,7 @@ public final class PersisterFactoryImpl implements PersisterFactory, ServiceRegi
PersistentClass entityBinding, PersistentClass entityBinding,
EntityDataAccess entityCacheAccessStrategy, EntityDataAccess entityCacheAccessStrategy,
NaturalIdDataAccess naturalIdCacheAccessStrategy, NaturalIdDataAccess naturalIdCacheAccessStrategy,
PersisterCreationContext creationContext) throws HibernateException { @SuppressWarnings("deprecation") PersisterCreationContext creationContext) throws HibernateException {
// If the metadata for the entity specified an explicit persister class, use it... // If the metadata for the entity specified an explicit persister class, use it...
Class<? extends EntityPersister> persisterClass = entityBinding.getEntityPersisterClass(); Class<? extends EntityPersister> persisterClass = entityBinding.getEntityPersisterClass();
if ( persisterClass == null ) { if ( persisterClass == null ) {
@ -83,13 +85,12 @@ public final class PersisterFactoryImpl implements PersisterFactory, ServiceRegi
); );
} }
@SuppressWarnings( {"unchecked"})
private EntityPersister createEntityPersister( private EntityPersister createEntityPersister(
Class<? extends EntityPersister> persisterClass, Class<? extends EntityPersister> persisterClass,
PersistentClass entityBinding, PersistentClass entityBinding,
EntityDataAccess entityCacheAccessStrategy, EntityDataAccess entityCacheAccessStrategy,
NaturalIdDataAccess naturalIdCacheAccessStrategy, NaturalIdDataAccess naturalIdCacheAccessStrategy,
PersisterCreationContext creationContext) { @SuppressWarnings("deprecation") PersisterCreationContext creationContext) {
try { try {
final Constructor<? extends EntityPersister> constructor = persisterClass.getConstructor( ENTITY_PERSISTER_CONSTRUCTOR_ARGS ); final Constructor<? extends EntityPersister> constructor = persisterClass.getConstructor( ENTITY_PERSISTER_CONSTRUCTOR_ARGS );
try { try {
@ -116,7 +117,7 @@ public final class PersisterFactoryImpl implements PersisterFactory, ServiceRegi
throw new MappingException( "Could not instantiate persister " + persisterClass.getName(), e ); throw new MappingException( "Could not instantiate persister " + persisterClass.getName(), e );
} }
} }
catch (MappingException e) { catch (HibernateException e) {
throw e; throw e;
} }
catch (Exception e) { catch (Exception e) {
@ -129,7 +130,7 @@ public final class PersisterFactoryImpl implements PersisterFactory, ServiceRegi
public CollectionPersister createCollectionPersister( public CollectionPersister createCollectionPersister(
Collection collectionBinding, Collection collectionBinding,
CollectionDataAccess cacheAccessStrategy, CollectionDataAccess cacheAccessStrategy,
PersisterCreationContext creationContext) throws HibernateException { @SuppressWarnings("deprecation") PersisterCreationContext creationContext) throws HibernateException {
// If the metadata for the collection specified an explicit persister class, use it // If the metadata for the collection specified an explicit persister class, use it
Class<? extends CollectionPersister> persisterClass = collectionBinding.getCollectionPersisterClass(); Class<? extends CollectionPersister> persisterClass = collectionBinding.getCollectionPersisterClass();
if ( persisterClass == null ) { if ( persisterClass == null ) {
@ -140,12 +141,11 @@ public final class PersisterFactoryImpl implements PersisterFactory, ServiceRegi
return createCollectionPersister( persisterClass, collectionBinding, cacheAccessStrategy, creationContext ); return createCollectionPersister( persisterClass, collectionBinding, cacheAccessStrategy, creationContext );
} }
@SuppressWarnings( {"unchecked"})
private CollectionPersister createCollectionPersister( private CollectionPersister createCollectionPersister(
Class<? extends CollectionPersister> persisterClass, Class<? extends CollectionPersister> persisterClass,
Collection collectionBinding, Collection collectionBinding,
CollectionDataAccess cacheAccessStrategy, CollectionDataAccess cacheAccessStrategy,
PersisterCreationContext creationContext) { @SuppressWarnings("deprecation") PersisterCreationContext creationContext) {
try { try {
Constructor<? extends CollectionPersister> constructor = persisterClass.getConstructor( COLLECTION_PERSISTER_CONSTRUCTOR_ARGS ); Constructor<? extends CollectionPersister> constructor = persisterClass.getConstructor( COLLECTION_PERSISTER_CONSTRUCTOR_ARGS );
try { try {

View File

@ -96,6 +96,7 @@ import org.hibernate.sql.ast.JoinType;
import org.hibernate.sql.ast.spi.FromClauseAccess; import org.hibernate.sql.ast.spi.FromClauseAccess;
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator; import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlAstTreeHelper;
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
@ -278,6 +279,8 @@ public abstract class BaseSqmToSqlAstConverter
public QuerySpec visitQuerySpec(SqmQuerySpec sqmQuerySpec) { public QuerySpec visitQuerySpec(SqmQuerySpec sqmQuerySpec) {
final QuerySpec sqlQuerySpec = new QuerySpec( processingStateStack.isEmpty(), sqmQuerySpec.getFromClause().getNumberOfRoots() ); final QuerySpec sqlQuerySpec = new QuerySpec( processingStateStack.isEmpty(), sqmQuerySpec.getFromClause().getNumberOfRoots() );
additionalRestrictions = null;
processingStateStack.push( processingStateStack.push(
new SqlAstQuerySpecProcessingStateImpl( new SqlAstQuerySpecProcessingStateImpl(
sqlQuerySpec, sqlQuerySpec,
@ -308,6 +311,10 @@ public abstract class BaseSqmToSqlAstConverter
} }
} }
if ( additionalRestrictions != null ) {
sqlQuerySpec.applyPredicate( additionalRestrictions );
}
// todo : group-by // todo : group-by
// todo : having // todo : having
@ -412,6 +419,8 @@ public abstract class BaseSqmToSqlAstConverter
return null; return null;
} }
Predicate additionalRestrictions;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
protected void consumeFromClauseRoot(SqmRoot<?> sqmRoot) { protected void consumeFromClauseRoot(SqmRoot<?> sqmRoot) {
log.tracef( "Resolving SqmRoot [%s] to TableGroup", sqmRoot ); log.tracef( "Resolving SqmRoot [%s] to TableGroup", sqmRoot );
@ -427,6 +436,7 @@ public abstract class BaseSqmToSqlAstConverter
LockMode.NONE, LockMode.NONE,
sqlAliasBaseManager, sqlAliasBaseManager,
getSqlExpressionResolver(), getSqlExpressionResolver(),
() -> predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates( additionalRestrictions, predicate ),
creationContext creationContext
); );
@ -512,6 +522,7 @@ public abstract class BaseSqmToSqlAstConverter
determineLockMode( sqmJoin.getExplicitAlias() ), determineLockMode( sqmJoin.getExplicitAlias() ),
sqlAliasBaseManager, sqlAliasBaseManager,
getSqlExpressionResolver(), getSqlExpressionResolver(),
() -> predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates( additionalRestrictions, predicate ),
getCreationContext() getCreationContext()
); );
@ -539,6 +550,7 @@ public abstract class BaseSqmToSqlAstConverter
determineLockMode( sqmJoin.getExplicitAlias() ), determineLockMode( sqmJoin.getExplicitAlias() ),
sqlAliasBaseManager, sqlAliasBaseManager,
getSqlExpressionResolver(), getSqlExpressionResolver(),
() -> predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates( additionalRestrictions, predicate ),
getCreationContext() getCreationContext()
); );
fromClauseIndex.register( sqmJoin, tableGroup ); fromClauseIndex.register( sqmJoin, tableGroup );

View File

@ -52,8 +52,8 @@ public class BasicValuedPathInterpretation<T> implements AssignableSqmPathInterp
mapping.getMappedColumnExpression() mapping.getMappedColumnExpression()
), ),
sacs -> new ColumnReference( sacs -> new ColumnReference(
mapping.getMappedColumnExpression(),
tableReference.getIdentificationVariable(), tableReference.getIdentificationVariable(),
mapping.getMappedColumnExpression(),
mapping.getJdbcMapping(), mapping.getJdbcMapping(),
sqlAstCreationState.getCreationContext().getSessionFactory() sqlAstCreationState.getCreationContext().getSessionFactory()
) )

View File

@ -29,8 +29,8 @@ public class ColumnReference implements Expression {
private final JdbcMapping jdbcMapping; private final JdbcMapping jdbcMapping;
public ColumnReference( public ColumnReference(
String columnExpression,
String qualifier, String qualifier,
String columnExpression,
JdbcMapping jdbcMapping, JdbcMapping jdbcMapping,
SessionFactoryImplementor sessionFactory) { SessionFactoryImplementor sessionFactory) {
this( this(

View File

@ -6,6 +6,9 @@
*/ */
package org.hibernate.sql.ast.tree.from; package org.hibernate.sql.ast.tree.from;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
@ -13,6 +16,7 @@ import org.hibernate.query.sqm.sql.SqlExpressionResolver;
import org.hibernate.sql.ast.JoinType; import org.hibernate.sql.ast.JoinType;
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator; import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.tree.predicate.Predicate;
/** /**
* Contract for things that can produce the {@link TableGroup} that is the root of a * Contract for things that can produce the {@link TableGroup} that is the root of a
@ -31,5 +35,6 @@ public interface RootTableGroupProducer extends TableGroupProducer, ModelPartCon
LockMode lockMode, LockMode lockMode,
SqlAliasBaseGenerator aliasBaseGenerator, SqlAliasBaseGenerator aliasBaseGenerator,
SqlExpressionResolver sqlExpressionResolver, SqlExpressionResolver sqlExpressionResolver,
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
SqlAstCreationContext creationContext); SqlAstCreationContext creationContext);
} }

View File

@ -0,0 +1,238 @@
/*
* 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.orm.test.metamodel.mapping;
import java.util.List;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.FailureExpected;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.hamcrest.CoreMatchers;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* @author Steve Ebersole
*/
@SuppressWarnings("WeakerAccess")
@DomainModel(
annotatedClasses = {
InheritanceTests.Customer.class,
InheritanceTests.DomesticCustomer.class,
InheritanceTests.ForeignCustomer.class
}
)
@ServiceRegistry
@SessionFactory
public class InheritanceTests {
@Test
public void basicTest(SessionFactoryScope scope) {
final EntityPersister customerDescriptor = scope.getSessionFactory()
.getMetamodel()
.findEntityDescriptor( Customer.class );
final EntityPersister domesticCustomerDescriptor = scope.getSessionFactory()
.getMetamodel()
.findEntityDescriptor( DomesticCustomer.class );
final EntityPersister foreignCustomerDescriptor = scope.getSessionFactory()
.getMetamodel()
.findEntityDescriptor( ForeignCustomer.class );
assert customerDescriptor.isTypeOrSuperType( customerDescriptor );
assert ! customerDescriptor.isTypeOrSuperType( domesticCustomerDescriptor );
assert ! customerDescriptor.isTypeOrSuperType( foreignCustomerDescriptor );
assert domesticCustomerDescriptor.isTypeOrSuperType( customerDescriptor );
assert domesticCustomerDescriptor.isTypeOrSuperType( domesticCustomerDescriptor );
assert ! domesticCustomerDescriptor.isTypeOrSuperType( foreignCustomerDescriptor );
assert foreignCustomerDescriptor.isTypeOrSuperType( customerDescriptor );
assert ! foreignCustomerDescriptor.isTypeOrSuperType( domesticCustomerDescriptor );
assert foreignCustomerDescriptor.isTypeOrSuperType( foreignCustomerDescriptor );
}
@BeforeEach
public void createTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.persist( new DomesticCustomer( 1, "domestic", "123" ) );
session.persist( new ForeignCustomer( 2, "foreign", "987" ) );
}
);
}
@AfterEach
public void cleanupTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "from DomesticCustomer", DomesticCustomer.class ).list().forEach(
cust -> session.delete( cust )
);
session.createQuery( "from ForeignCustomer", ForeignCustomer.class ).list().forEach(
cust -> session.delete( cust )
);
}
);
}
@Test
@FailureExpected
public void rootQueryExecutionTest(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
{
final List<Customer> results = session.createQuery(
"select c from Customer c",
Customer.class
).list();
assertThat( results.size(), is( 2 ) );
for ( Customer result : results ) {
if ( result.getId() == 1 ) {
assertThat( result, instanceOf( DomesticCustomer.class ) );
assertThat( ( (DomesticCustomer) result ).getTaxId(), is( "123" ) );
}
else {
assertThat( result.getId(), is( 2 ) );
assertThat( ( (ForeignCustomer) result ).getVat(), is( "987" ) );
}
}
}
}
);
}
@Test
public void subclassQueryExecutionTest(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
{
final DomesticCustomer result = session.createQuery(
"select c from DomesticCustomer c",
DomesticCustomer.class
).uniqueResult();
assertThat( result, notNullValue() );
assertThat( result.getId(), is( 1 ) );
assertThat( result.getName(), is( "domestic" ) );
assertThat( result.getTaxId(), is( "123" ) );
}
{
final ForeignCustomer result = session.createQuery(
"select c from ForeignCustomer c",
ForeignCustomer.class
).uniqueResult();
assertThat( result, notNullValue() );
assertThat( result.getId(), is( 2 ) );
assertThat( result.getName(), is( "foreign" ) );
assertThat( result.getVat(), is( "987" ) );
}
}
);
}
@Entity( name = "Customer" )
@Inheritance( strategy = InheritanceType.SINGLE_TABLE )
@Table( name = "customer" )
@DiscriminatorColumn( name = "cust_type" )
public static abstract class Customer {
private Integer id;
private String name;
public Customer() {
}
public Customer(Integer id, String name) {
this.id = id;
this.name = name;
}
@Id
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Entity( name = "DomesticCustomer" )
@DiscriminatorValue( "dc" )
public static class DomesticCustomer extends Customer {
private String taxId;
public DomesticCustomer() {
}
public DomesticCustomer(Integer id, String name, String taxId) {
super( id, name );
this.taxId = taxId;
}
public String getTaxId() {
return taxId;
}
public void setTaxId(String taxId) {
this.taxId = taxId;
}
}
@Entity( name = "ForeignCustomer" )
@DiscriminatorValue( "fc" )
public static class ForeignCustomer extends Customer {
private String vat;
public ForeignCustomer() {
}
public ForeignCustomer(Integer id, String name, String vat) {
super( id, name );
this.vat = vat;
}
public String getVat() {
return vat;
}
public void setVat(String vat) {
this.vat = vat;
}
}
}

View File

@ -25,5 +25,3 @@ javax.persistence.validation.mode=NONE
hibernate.service.allow_crawling=false hibernate.service.allow_crawling=false
hibernate.session.events.log=true hibernate.session.events.log=true
hibernate.hql.bulk_id_strategy.global_temporary.drop_tables=true hibernate.hql.bulk_id_strategy.global_temporary.drop_tables=true
hibernate.bytecode.use_reflection_optimizer=true