Implement support for using generically typed associations to re-enable inheritance/discriminator tests

This commit is contained in:
Christian Beikov 2022-02-04 11:23:39 +01:00
parent eb572376a9
commit 9b53ca8559
40 changed files with 887 additions and 125 deletions

View File

@ -13,19 +13,25 @@ import jakarta.persistence.Converts;
import jakarta.persistence.JoinTable;
import org.hibernate.AssertionFailure;
import org.hibernate.MappingException;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.annotations.EntityBinder;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.IndexedCollection;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value;
/**
* @author Emmanuel Bernard
@ -234,7 +240,82 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
private void addPropertyToMappedSuperclass(Property prop, XClass declaringClass) {
final Class<?> type = getContext().getBootstrapContext().getReflectionManager().toClass( declaringClass );
MappedSuperclass superclass = getContext().getMetadataCollector().getMappedSuperclass( type );
superclass.addDeclaredProperty( prop );
if ( type.getTypeParameters().length == 0 ) {
superclass.addDeclaredProperty( prop );
}
else {
// If the type has type parameters, we have to look up the XClass and actual property again
// because the given XClass has a TypeEnvironment based on the type variable assignments of a subclass
// and that might result in a wrong property type being used for a property which uses a type variable
final XClass actualDeclaringClass = getContext().getBootstrapContext().getReflectionManager().toXClass( type );
for ( XProperty declaredProperty : actualDeclaringClass.getDeclaredProperties( prop.getPropertyAccessorName() ) ) {
if ( prop.getName().equals( declaredProperty.getName() ) ) {
final PropertyData inferredData = new PropertyInferredData(
actualDeclaringClass,
declaredProperty,
null,
getContext().getBootstrapContext().getReflectionManager()
);
final Value originalValue = prop.getValue();
if ( originalValue instanceof SimpleValue ) {
// Avoid copying when the property doesn't depend on a type variable
if ( inferredData.getTypeName().equals( ( (SimpleValue) originalValue ).getTypeName() ) ) {
superclass.addDeclaredProperty( prop );
return;
}
}
// If the property depends on a type variable, we have to copy it and the Value
final Property actualProperty = prop.copy();
actualProperty.setReturnedClassName( inferredData.getTypeName() );
final Value value = actualProperty.getValue().copy();
if ( value instanceof Collection ) {
final Collection collection = (Collection) value;
// The owner is a MappedSuperclass which is not a PersistentClass, so set it to null
// collection.setOwner( null );
collection.setRole( type.getName() + "." + prop.getName() );
// To copy the element and key values, we need to defer setting the type name until the CollectionBinder ran
getContext().getMetadataCollector().addSecondPass(
new SecondPass() {
@Override
public void doSecondPass(Map persistentClasses) throws MappingException {
final Collection initializedCollection = (Collection) originalValue;
final Value element = initializedCollection.getElement().copy();
setTypeName( element, inferredData.getProperty().getElementClass().getName() );
if ( initializedCollection instanceof IndexedCollection ) {
final Value index = ( (IndexedCollection) initializedCollection ).getIndex().copy();
setTypeName( index, inferredData.getProperty().getMapKey().getName() );
( (IndexedCollection) collection ).setIndex( index );
}
collection.setElement( element );
}
}
);
}
else {
setTypeName( value, inferredData.getTypeName() );
}
actualProperty.setValue( value );
superclass.addDeclaredProperty( actualProperty );
break;
}
}
}
}
private void setTypeName(Value value, String typeName) {
if ( value instanceof ToOne ) {
final ToOne toOne = (ToOne) value;
toOne.setReferencedEntityName( typeName );
toOne.setTypeName( typeName );
}
else if ( value instanceof Component ) {
final Component component = (Component) value;
component.setComponentClassName( typeName );
component.setTypeName( typeName );
}
else if ( value instanceof SimpleValue ) {
( (SimpleValue) value ).setTypeName( typeName );
}
}
private void addPropertyToJoin(Property prop, XClass declaringClass, Join join) {

View File

@ -63,6 +63,11 @@ public class ExportableColumn extends Column {
this.database = database;
}
@Override
public Value copy() {
return new ValueImpl( column, table, type, database );
}
@Override
public int getColumnSpan() {
return 1;

View File

@ -57,6 +57,30 @@ public class Any extends SimpleValue {
}
public Any(Any original) {
super( original );
this.metaMapping = original.metaMapping == null ? null : original.metaMapping.copy();
this.keyMapping = original.keyMapping == null ? null : (SimpleValue) original.keyMapping.copy();
// annotations
this.discriminatorDescriptor = original.discriminatorDescriptor == null
? null
: original.discriminatorDescriptor.copy();
this.keyDescriptor = original.keyDescriptor == null ? null : original.keyDescriptor.copy();
// common
this.metaValueToEntityNameMap = original.metaValueToEntityNameMap == null
? null
: new HashMap<>(original.metaValueToEntityNameMap);
this.lazy = original.lazy;
}
@Override
public Any copy() {
return new Any( this );
}
public void addSelectable(Selectable selectable) {
if ( selectable == null ) {
return;
@ -281,6 +305,18 @@ public class Any extends SimpleValue {
this.selectableConsumer = selectableConsumer;
}
private MetaValue(MetaValue original) {
super( original );
this.typeName = original.typeName;
this.columnName = original.columnName;
this.selectableConsumer = original.selectableConsumer;
}
@Override
public MetaValue copy() {
return new MetaValue( this );
}
@Override
public Type getType() throws MappingException {
return getMetadata().getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( typeName );
@ -365,6 +401,17 @@ public class Any extends SimpleValue {
this.selectableConsumer = selectableConsumer;
}
private KeyValue(KeyValue original) {
super( original );
this.typeName = original.typeName;
this.selectableConsumer = original.selectableConsumer;
}
@Override
public KeyValue copy() {
return new KeyValue( this );
}
@Override
public Type getType() throws MappingException {
return getMetadata().getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( typeName );

View File

@ -35,6 +35,16 @@ public class Array extends List {
super( customTypeBeanResolver, owner, buildingContext );
}
protected Array(Array original) {
super( original );
this.elementClassName = original.elementClassName;
}
@Override
public Array copy() {
return new Array( this );
}
public Class<?> getElementClass() throws MappingException {
if ( elementClassName == null ) {
final org.hibernate.type.Type elementType = getElement().getType();

View File

@ -34,6 +34,15 @@ public class Bag extends Collection {
super( customTypeBeanResolver, owner, buildingContext );
}
private Bag(Collection original) {
super( original );
}
@Override
public Bag copy() {
return new Bag( this );
}
public CollectionType getDefaultCollectionType() {
return new BagType( getRole(), getReferencedPropertyName() );
}

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.mapping;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.function.Consumer;
@ -109,6 +110,29 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
buildingContext.getMetadataCollector().registerValueMappingResolver( this::resolve );
}
public BasicValue(BasicValue original) {
super( original );
this.typeConfiguration = original.typeConfiguration;
this.explicitTypeName = original.explicitTypeName;
this.explicitLocalTypeParams = original.explicitLocalTypeParams == null
? null
: new HashMap(original.explicitLocalTypeParams);
this.explicitJavaTypeAccess = original.explicitJavaTypeAccess;
this.explicitJdbcTypeAccess = original.explicitJdbcTypeAccess;
this.explicitMutabilityPlanAccess = original.explicitMutabilityPlanAccess;
this.implicitJavaTypeAccess = original.implicitJavaTypeAccess;
this.enumerationStyle = original.enumerationStyle;
this.temporalPrecision = original.temporalPrecision;
this.timeZoneStorageType = original.timeZoneStorageType;
this.resolvedJavaType = original.resolvedJavaType;
this.ownerName = original.ownerName;
this.propertyName = original.propertyName;
}
@Override
public BasicValue copy() {
return new BasicValue( this );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Setters - in preparation of resolution

View File

@ -121,6 +121,55 @@ public abstract class Collection implements Fetchable, Value, Filterable {
this.buildingContext = buildingContext;
}
protected Collection(Collection original) {
this.buildingContext = original.buildingContext;
this.owner = original.owner;
this.key = original.key == null ? null : (KeyValue) original.key.copy();
this.element = original.element == null ? null : original.element.copy();
this.collectionTable = original.collectionTable;
this.role = original.role;
this.lazy = original.lazy;
this.extraLazy = original.extraLazy;
this.inverse = original.inverse;
this.mutable = original.mutable;
this.subselectLoadable = original.subselectLoadable;
this.cacheConcurrencyStrategy = original.cacheConcurrencyStrategy;
this.cacheRegionName = original.cacheRegionName;
this.orderBy = original.orderBy;
this.where = original.where;
this.manyToManyWhere = original.manyToManyWhere;
this.manyToManyOrderBy = original.manyToManyOrderBy;
this.referencedPropertyName = original.referencedPropertyName;
this.mappedByProperty = original.mappedByProperty;
this.sorted = original.sorted;
this.comparator = original.comparator;
this.comparatorClassName = original.comparatorClassName;
this.orphanDelete = original.orphanDelete;
this.batchSize = original.batchSize;
this.fetchMode = original.fetchMode;
this.optimisticLocked = original.optimisticLocked;
this.typeName = original.typeName;
this.typeParameters = original.typeParameters == null ? null : new Properties(original.typeParameters);
this.customTypeBeanResolver = original.customTypeBeanResolver;
this.collectionPersisterClass = original.collectionPersisterClass;
this.filters.addAll( original.filters );
this.manyToManyFilters.addAll( original.manyToManyFilters );
this.synchronizedTables.addAll( original.synchronizedTables );
this.customSQLInsert = original.customSQLInsert;
this.customInsertCallable = original.customInsertCallable;
this.insertCheckStyle = original.insertCheckStyle;
this.customSQLUpdate = original.customSQLUpdate;
this.customUpdateCallable = original.customUpdateCallable;
this.updateCheckStyle = original.updateCheckStyle;
this.customSQLDelete = original.customSQLDelete;
this.customDeleteCallable = original.customDeleteCallable;
this.deleteCheckStyle = original.deleteCheckStyle;
this.customSQLDeleteAll = original.customSQLDeleteAll;
this.customDeleteAllCallable = original.customDeleteAllCallable;
this.deleteAllCheckStyle = original.deleteAllCheckStyle;
this.loaderName = original.loaderName;
}
public MetadataBuildingContext getBuildingContext() {
return buildingContext;
}

View File

@ -8,6 +8,7 @@ package org.hibernate.mapping;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -81,6 +82,27 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
metadata.getMetadataCollector().registerComponent( this );
}
private Component(Component original) {
super( original );
this.properties.addAll( original.properties );
this.originalPropertyOrder = original.originalPropertyOrder.clone();
this.componentClassName = original.componentClassName;
this.embedded = original.embedded;
this.parentProperty = original.parentProperty;
this.owner = original.owner;
this.dynamic = original.dynamic;
this.metaAttributes = original.metaAttributes == null ? null : new HashMap(original.metaAttributes);
this.isKey = original.isKey;
this.roleName = original.roleName;
this.customInstantiator = original.customInstantiator;
this.type = original.type;
}
@Override
public Component copy() {
return new Component( this );
}
public int getPropertySpan() {
return properties.size();
}

View File

@ -29,6 +29,18 @@ public class DependantBasicValue extends BasicValue {
this.updateable = updateable;
}
private DependantBasicValue(DependantBasicValue original) {
super( original );
this.referencedValue = original.referencedValue.copy();
this.nullable = original.nullable;
this.updateable = original.updateable;
}
@Override
public DependantBasicValue copy() {
return new DependantBasicValue( this );
}
@Override
protected Resolution<?> buildResolution() {
return referencedValue.resolve();

View File

@ -28,6 +28,19 @@ public class DependantValue extends SimpleValue implements Resolvable, SortableV
this.wrappedValue = prototype;
}
private DependantValue(DependantValue original) {
super( original );
this.wrappedValue = (KeyValue) original.wrappedValue.copy();
this.nullable = original.nullable;
this.updateable = original.updateable;
this.sorted = original.sorted;
}
@Override
public DependantValue copy() {
return new DependantValue( this );
}
public KeyValue getWrappedValue() {
return wrappedValue;
}

View File

@ -34,6 +34,15 @@ public class IdentifierBag extends IdentifierCollection {
super( customTypeBeanResolver, owner, buildingContext );
}
public IdentifierBag(IdentifierBag original) {
super( original );
}
@Override
public IdentifierBag copy() {
return new IdentifierBag( this );
}
public CollectionType getDefaultCollectionType() {
return new IdentifierBagType( getRole(), getReferencedPropertyName() );
}

View File

@ -31,6 +31,11 @@ public abstract class IdentifierCollection extends Collection {
super( customTypeBeanResolver, owner, buildingContext );
}
protected IdentifierCollection(IdentifierCollection original) {
super( original );
this.identifier = (KeyValue) original.identifier.copy();
}
public KeyValue getIdentifier() {
return identifier;
}

View File

@ -33,6 +33,11 @@ public abstract class IndexedCollection extends Collection {
super( customTypeBeanResolver, owner, buildingContext );
}
protected IndexedCollection(IndexedCollection original) {
super( original );
this.index = original.index == null ? null : original.index.copy();
}
public Value getIndex() {
return index;
}

View File

@ -38,6 +38,16 @@ public class List extends IndexedCollection {
super( customTypeBeanResolver, owner, buildingContext );
}
protected List(List original) {
super( original );
this.baseIndex = original.baseIndex;
}
@Override
public List copy() {
return new List( this );
}
public boolean isList() {
return true;
}

View File

@ -28,6 +28,17 @@ public class ManyToOne extends ToOne {
super( buildingContext, table );
}
private ManyToOne(ManyToOne original) {
super( original );
this.ignoreNotFound = original.ignoreNotFound;
this.isLogicalOneToOne = original.isLogicalOneToOne;
}
@Override
public ManyToOne copy() {
return new ManyToOne( this );
}
public Type getType() throws MappingException {
if ( resolvedType == null ) {
resolvedType = MappingHelper.manyToOne(

View File

@ -33,6 +33,15 @@ public class Map extends IndexedCollection {
super( customTypeBeanResolver, owner, buildingContext );
}
private Map(Map original) {
super( original );
}
@Override
public Map copy() {
return new Map( this );
}
public boolean isMap() {
return true;
}

View File

@ -36,6 +36,19 @@ public class OneToMany implements Value {
this.referencingTable = owner == null ? null : owner.getTable();
}
private OneToMany(OneToMany original) {
this.buildingContext = original.buildingContext;
this.referencingTable = original.referencingTable;
this.referencedEntityName = original.referencedEntityName;
this.associatedClass = original.associatedClass;
this.ignoreNotFound = original.ignoreNotFound;
}
@Override
public Value copy() {
return new OneToMany( this );
}
public MetadataBuildingContext getBuildingContext() {
return buildingContext;
}

View File

@ -35,6 +35,21 @@ public class OneToOne extends ToOne {
this.entityName = owner.getEntityName();
}
private OneToOne(OneToOne original) {
super( original );
this.constrained = original.constrained;
this.foreignKeyType = original.foreignKeyType;
this.identifier = original.identifier == null ? null : (KeyValue) original.identifier.copy();
this.propertyName = original.propertyName;
this.entityName = original.entityName;
this.mappedByProperty = original.mappedByProperty;
}
@Override
public OneToOne copy() {
return new OneToOne( this );
}
public String getPropertyName() {
return propertyName;
}

View File

@ -24,6 +24,15 @@ public class PrimitiveArray extends Array {
super( customTypeBeanResolver, owner, buildingContext );
}
private PrimitiveArray(PrimitiveArray original) {
super( original );
}
@Override
public Array copy() {
return new PrimitiveArray( this );
}
public boolean isPrimitiveArray() {
return true;
}

View File

@ -426,4 +426,27 @@ public class Property implements Serializable, MetaAttributable {
public void setReturnedClassName(String returnedClassName) {
this.returnedClassName = returnedClassName;
}
public Property copy() {
final Property prop = new Property();
prop.setName( getName() );
prop.setValue( getValue() );
prop.setCascade( getCascade() );
prop.setUpdateable( isUpdateable() );
prop.setInsertable( isInsertable() );
prop.setSelectable( isSelectable() );
prop.setOptimisticLocked( isOptimisticLocked() );
prop.setValueGenerationStrategy( getValueGenerationStrategy() );
prop.setPropertyAccessorName( getPropertyAccessorName() );
prop.setLazy( isLazy() );
prop.setLazyGroup( getLazyGroup() );
prop.setOptional( isOptional() );
prop.setMetaAttributes( getMetaAttributes() );
prop.setPersistentClass( getPersistentClass() );
prop.setNaturalIdentifier( isNaturalIdentifier() );
prop.setLob( isLob() );
prop.addCallbackDefinitions( getCallbackDefinitions() );
prop.setReturnedClassName( getReturnedClassName() );
return prop;
}
}

View File

@ -38,6 +38,15 @@ public class Set extends Collection {
super( customTypeBeanResolver, persistentClass, buildingContext );
}
private Set(Collection original) {
super( original );
}
@Override
public Set copy() {
return new Set( this );
}
public void validate(Mapping mapping) throws MappingException {
super.validate( mapping );
//for backward compatibility, disable this:

View File

@ -108,6 +108,33 @@ public abstract class SimpleValue implements KeyValue {
this.table = table;
}
protected SimpleValue(SimpleValue original) {
this.buildingContext = original.buildingContext;
this.metadata = original.metadata;
this.columns.addAll( original.columns );
this.insertability.addAll( original.insertability );
this.updatability.addAll( original.updatability );
this.typeName = original.typeName;
this.typeParameters = original.typeParameters == null ? null : new Properties( original.typeParameters );
this.isVersion = original.isVersion;
this.isNationalized = original.isNationalized;
this.isLob = original.isLob;
this.identifierGeneratorProperties = original.identifierGeneratorProperties == null
? null
: new Properties( original.identifierGeneratorProperties );
this.identifierGeneratorStrategy = original.identifierGeneratorStrategy;
this.nullValue = original.nullValue;
this.table = original.table;
this.foreignKeyName = original.foreignKeyName;
this.foreignKeyDefinition = original.foreignKeyDefinition;
this.alternateUniqueKey = original.alternateUniqueKey;
this.cascadeDeleteEnabled = original.cascadeDeleteEnabled;
this.attributeConverterDescriptor = original.attributeConverterDescriptor;
this.type = original.type;
this.customIdGeneratorCreator = original.customIdGeneratorCreator;
this.identifierGenerator = original.identifierGenerator;
}
public MetadataBuildingContext getBuildingContext() {
return buildingContext;
}

View File

@ -34,6 +34,19 @@ public abstract class ToOne extends SimpleValue implements Fetchable, SortableVa
super( buildingContext, table );
}
protected ToOne(ToOne original) {
super( original );
this.fetchMode = original.fetchMode;
this.referencedPropertyName = original.referencedPropertyName;
this.referencedEntityName = original.referencedEntityName;
this.propertyName = original.propertyName;
this.lazy = original.lazy;
this.sorted = original.sorted;
this.unwrapProxy = original.unwrapProxy;
this.isUnwrapProxyImplicit = original.isUnwrapProxyImplicit;
this.referenceToPrimaryKey = original.referenceToPrimaryKey;
}
public FetchMode getFetchMode() {
return fetchMode;
}

View File

@ -100,4 +100,5 @@ public interface Value extends Serializable {
boolean hasAnyUpdatableColumns();
ServiceRegistry getServiceRegistry();
Value copy();
}

View File

@ -40,6 +40,7 @@ import org.hibernate.metamodel.model.domain.SimpleDomainType;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.metamodel.model.domain.internal.AnyMappingDomainTypeImpl;
import org.hibernate.metamodel.model.domain.internal.EmbeddableTypeImpl;
import org.hibernate.metamodel.model.domain.internal.EntityTypeImpl;
import org.hibernate.metamodel.model.domain.internal.MapMember;
import org.hibernate.metamodel.model.domain.internal.MappedSuperclassTypeImpl;
import org.hibernate.metamodel.model.domain.internal.PluralAttributeBuilder;
@ -223,7 +224,17 @@ public class AttributeFactory {
final org.hibernate.type.Type type = typeContext.getHibernateValue().getType();
if ( type instanceof EntityType ) {
final EntityType entityType = (EntityType) type;
return context.locateEntityType( entityType.getAssociatedEntityName() );
final IdentifiableDomainType<Y> domainType = context.locateIdentifiableType( entityType.getAssociatedEntityName() );
if ( domainType == null ) {
// Due to the use of generics, it can happen that a mapped super class uses a type
// for an attribute that is not a managed type. Since this case is not specifically mentioned
// in the Jakarta Persistence spec, we handle this by returning a "dummy" entity type
final JavaType<Y> domainJavaType = context.getJavaTypeRegistry().resolveDescriptor(
typeContext.getJpaBindableType()
);
return new EntityTypeImpl<>( domainJavaType, context.getJpaMetamodel() );
}
return domainType;
}
assert type instanceof AnyType;

View File

@ -83,7 +83,7 @@ public class MetadataContext {
private final AttributeFactory attributeFactory = new AttributeFactory( this );
private final Map<Class<?>, EntityDomainType<?>> entityTypes = new HashMap<>();
private final Map<String, EntityDomainType<?>> entityTypesByEntityName = new HashMap<>();
private final Map<String, IdentifiableDomainType<?>> identifiableTypesByName = new HashMap<>();
private final Map<PersistentClass, EntityDomainType<?>> entityTypesByPersistentClass = new HashMap<>();
private final Map<Class<?>, EmbeddableDomainType<?>> embeddables = new HashMap<>();
@ -172,7 +172,7 @@ public class MetadataContext {
entityTypes.put( entityType.getBindableJavaType(), entityType );
}
entityTypesByEntityName.put( persistentClass.getEntityName(), entityType );
identifiableTypesByName.put( persistentClass.getEntityName(), entityType );
entityTypesByPersistentClass.put( persistentClass, entityType );
orderedMappings.add( persistentClass );
}
@ -202,6 +202,7 @@ public class MetadataContext {
public void registerMappedSuperclassType(
MappedSuperclass mappedSuperclass,
MappedSuperclassDomainType<?> mappedSuperclassType) {
identifiableTypesByName.put( mappedSuperclassType.getTypeName(), mappedSuperclassType );
mappedSuperclassByMappedSuperclassMapping.put( mappedSuperclass, mappedSuperclassType );
orderedMappings.add( mappedSuperclass );
mappedSuperClassTypeToPersistentClass.put( mappedSuperclassType, getEntityWorkedOn() );
@ -242,12 +243,12 @@ public class MetadataContext {
* @return The corresponding JPA {@link org.hibernate.type.EntityType}, or null.
*/
@SuppressWarnings("unchecked")
public <E> EntityDomainType<E> locateEntityType(String entityName) {
return (EntityDomainType<E>) entityTypesByEntityName.get( entityName );
public <E> IdentifiableDomainType<E> locateIdentifiableType(String entityName) {
return (IdentifiableDomainType<E>) identifiableTypesByName.get( entityName );
}
public Map<String, EntityDomainType<?>> getEntityTypesByEntityName() {
return Collections.unmodifiableMap( entityTypesByEntityName );
public Map<String, IdentifiableDomainType<?>> getIdentifiableTypesByName() {
return Collections.unmodifiableMap( identifiableTypesByName );
}
@SuppressWarnings("unchecked")
@ -452,7 +453,7 @@ public class MetadataContext {
assert cidValue.isEmbedded();
AbstractIdentifiableType<?> idType = (AbstractIdentifiableType<?>)
entityTypesByEntityName.get( cidValue.getOwner().getEntityName() );
identifiableTypesByName.get( cidValue.getOwner().getEntityName() );
//noinspection rawtypes
Set idAttributes = idType.getIdClassAttributesSafely();
if ( idAttributes == null ) {

View File

@ -10,6 +10,7 @@ import java.util.ArrayList;
import java.util.List;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.entity.UnionSubclassEntityPersister;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression;
@ -22,6 +23,11 @@ import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnRefere
* @author Steve Ebersole
*/
public class MappingModelHelper {
private MappingModelHelper() {
// disallow direct instantiation
}
public static Expression buildColumnReferenceExpression(
ModelPart modelPart,
SqlExpressionResolver sqlExpressionResolver,
@ -99,8 +105,60 @@ public class MappingModelHelper {
}
}
}
private MappingModelHelper() {
// disallow direct instantiation
public static boolean isCompatibleModelPart(ModelPart attribute1, ModelPart attribute2) {
if ( attribute1 == attribute2 ) {
return true;
}
if ( attribute1.getClass() != attribute2.getClass() || attribute1.getJavaType() != attribute2.getJavaType() ) {
return false;
}
if ( attribute1 instanceof Association ) {
final Association association1 = (Association) attribute1;
final Association association2 = (Association) attribute2;
return association1.getForeignKeyDescriptor().getAssociationKey().equals(
association2.getForeignKeyDescriptor().getAssociationKey()
);
}
else if ( attribute1 instanceof PluralAttributeMapping ) {
final PluralAttributeMapping plural1 = (PluralAttributeMapping) attribute1;
final PluralAttributeMapping plural2 = (PluralAttributeMapping) attribute2;
final CollectionPart element1 = plural1.getElementDescriptor();
final CollectionPart element2 = plural2.getElementDescriptor();
final CollectionPart index1 = plural1.getIndexDescriptor();
final CollectionPart index2 = plural2.getIndexDescriptor();
return plural1.getKeyDescriptor().getAssociationKey().equals(
plural2.getKeyDescriptor().getAssociationKey()
) && ( index1 == null && index2 == null || isCompatibleModelPart( index1, index2 ) )
&& isCompatibleModelPart( element1, element2 );
}
else if ( attribute1 instanceof EmbeddableValuedModelPart ) {
final EmbeddableValuedModelPart embedded1 = (EmbeddableValuedModelPart) attribute1;
final EmbeddableValuedModelPart embedded2 = (EmbeddableValuedModelPart) attribute2;
final List<AttributeMapping> attrs1 = embedded1.getEmbeddableTypeDescriptor().getAttributeMappings();
final List<AttributeMapping> attrs2 = embedded2.getEmbeddableTypeDescriptor().getAttributeMappings();
if ( attrs1.size() != attrs2.size() ) {
return false;
}
for ( int i = 0; i < attrs1.size(); i++ ) {
if ( !isCompatibleModelPart( attrs1.get( i ), attrs2.get( i ) ) ) {
return false;
}
}
return true;
}
else if ( attribute1 instanceof BasicValuedModelPart ) {
final BasicValuedModelPart basic1 = (BasicValuedModelPart) attribute1;
final BasicValuedModelPart basic2 = (BasicValuedModelPart) attribute2;
if ( !basic1.getSelectionExpression().equals( basic2.getSelectionExpression() ) ) {
return false;
}
if ( basic1.getContainingTableExpression().equals( basic2.getContainingTableExpression() ) ) {
return true;
}
// For union subclass mappings we also consider mappings compatible that just match the selection expression,
// because we match up columns of disjoint union subclass types by column name
return attribute1.findContainingEntityMapping().getEntityPersister() instanceof UnionSubclassEntityPersister;
}
return false;
}
}

View File

@ -15,6 +15,7 @@ import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
@ -31,8 +32,12 @@ import org.hibernate.graph.internal.SubGraphImpl;
import org.hibernate.graph.spi.SubGraphImplementor;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.RepresentationMode;
import org.hibernate.metamodel.RuntimeMetamodels;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.MappingModelHelper;
import org.hibernate.metamodel.model.domain.internal.AttributeContainer;
import org.hibernate.metamodel.model.domain.internal.DomainModelHelper;
import org.hibernate.query.SemanticException;
import org.hibernate.type.descriptor.java.JavaType;
/**
@ -167,11 +172,45 @@ public abstract class AbstractManagedType<J>
for ( ManagedDomainType subType : subTypes ) {
PersistentAttribute subTypeAttribute = subType.findSubTypesAttribute( name );
if ( subTypeAttribute != null ) {
return subTypeAttribute;
if ( attribute != null && !isCompatible( attribute, subTypeAttribute ) ) {
throw new IllegalArgumentException(
new SemanticException(
String.format(
Locale.ROOT,
"Could not resolve attribute '%s' of '%s' due to the attribute being declared in multiple sub types: ['%s', '%s']",
name,
getExpressibleJavaType().getJavaType().getTypeName(),
attribute.getDeclaringType().getExpressibleJavaType().getJavaType().getTypeName(),
subTypeAttribute.getDeclaringType().getExpressibleJavaType().getJavaType().getTypeName()
)
)
);
}
attribute = subTypeAttribute;
}
}
return null;
return attribute;
}
private boolean isCompatible(PersistentAttribute<?, ?> attribute1, PersistentAttribute<?, ?> attribute2) {
if ( attribute1 == attribute2 ) {
return true;
}
final RuntimeMetamodels runtimeMetamodels = jpaMetamodel().getTypeConfiguration()
.getSessionFactory()
.getRuntimeMetamodels();
final EntityMappingType entity1 = runtimeMetamodels.getEntityMappingType(
attribute1.getDeclaringType().getTypeName()
);
final EntityMappingType entity2 = runtimeMetamodels.getEntityMappingType(
attribute2.getDeclaringType().getTypeName()
);
return entity1 != null && entity2 != null && MappingModelHelper.isCompatibleModelPart(
entity1.findSubPart( attribute1.getName() ),
entity2.findSubPart( attribute2.getName() )
);
}
@Override

View File

@ -6,6 +6,8 @@
*/
package org.hibernate.metamodel.model.domain;
import org.hibernate.query.sqm.SqmPathSource;
import jakarta.persistence.metamodel.MappedSuperclassType;
/**
@ -13,5 +15,5 @@ import jakarta.persistence.metamodel.MappedSuperclassType;
*
* @author Steve Ebersole
*/
public interface MappedSuperclassDomainType<J> extends IdentifiableDomainType<J>, MappedSuperclassType<J> {
public interface MappedSuperclassDomainType<J> extends IdentifiableDomainType<J>, MappedSuperclassType<J>, SqmPathSource<J> {
}

View File

@ -82,6 +82,20 @@ public class EntityTypeImpl<J>
entityDescriptor
);
}
public EntityTypeImpl(JavaType<J> javaTypeDescriptor, JpaMetamodel jpaMetamodel) {
super(
javaTypeDescriptor.getJavaTypeClass().getName(),
javaTypeDescriptor,
null,
false,
false,
false,
jpaMetamodel
);
this.jpaEntityName = javaTypeDescriptor.getJavaTypeClass().getName();
this.discriminatorPathSource = null;
}
@Override
public String getName() {
@ -131,7 +145,6 @@ public class EntityTypeImpl<J>
}
if ( "id".equalsIgnoreCase( name ) || EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( name ) ) {
//noinspection unchecked
final SingularPersistentAttribute<J, ?> idAttribute = findIdAttribute();
//noinspection RedundantIfStatement
if ( idAttribute != null ) {

View File

@ -490,16 +490,21 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
context.wrapUp();
this.jpaEntityTypeMap.putAll( context.getEntityTypesByEntityName() );
for ( Map.Entry<String, IdentifiableDomainType<?>> entry : context.getIdentifiableTypesByName().entrySet() ) {
if ( entry.getValue() instanceof EntityDomainType<?> ) {
this.jpaEntityTypeMap.put( entry.getKey(), (EntityDomainType<?>) entry.getValue() );
}
}
this.jpaManagedTypeMap.putAll( context.getEntityTypeMap() );
this.jpaManagedTypeMap.putAll( context.getMappedSuperclassTypeMap() );
this.jpaManagedTypes.addAll( context.getMappedSuperclassTypeMap().values() );
switch ( jpaMetaModelPopulationSetting ) {
case IGNORE_UNSUPPORTED:
this.jpaManagedTypes.addAll( context.getEntityTypeMap().values() );
this.jpaManagedTypes.addAll( context.getMappedSuperclassTypeMap().values() );
break;
case ENABLED:
this.jpaManagedTypes.addAll( context.getEntityTypesByEntityName().values() );
this.jpaManagedTypes.addAll( context.getIdentifiableTypesByName().values() );
break;
}
@ -562,11 +567,8 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
private static Stream<ManagedDomainType<?>> domainTypeStream(MetadataContext context) {
return Stream.concat(
context.getEntityTypesByEntityName().values().stream(),
Stream.concat(
context.getMappedSuperclassTypeMap().values().stream(),
context.getEmbeddableTypeSet().stream()
)
context.getIdentifiableTypesByName().values().stream(),
context.getEmbeddableTypeSet().stream()
);
}

View File

@ -0,0 +1,80 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.metamodel.model.domain.internal;
import org.hibernate.metamodel.model.domain.MappedSuperclassDomainType;
import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.spi.NavigablePath;
import org.hibernate.query.sqm.SqmJoinable;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
import org.hibernate.query.sqm.tree.from.SqmFrom;
/**
* @author Steve Ebersole
*/
public class MappedSuperclassSqmPathSource<J> extends AbstractSqmPathSource<J> implements SqmJoinable<Object, J> {
public MappedSuperclassSqmPathSource(
String localPathName,
MappedSuperclassDomainType<J> domainType,
BindableType jpaBindableType) {
super( localPathName, domainType, jpaBindableType );
}
@Override
public MappedSuperclassDomainType<J> getSqmPathType() {
//noinspection unchecked
return ( MappedSuperclassDomainType<J> ) super.getSqmPathType();
}
@Override
public SqmPathSource<?> findSubPathSource(String name) {
final MappedSuperclassDomainType<J> sqmPathType = getSqmPathType();
return sqmPathType.findSubPathSource( name );
}
@Override
public SqmPath<J> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) {
final NavigablePath navigablePath;
if ( intermediatePathSource == null ) {
navigablePath = lhs.getNavigablePath().append( getPathName() );
}
else {
navigablePath = lhs.getNavigablePath().append( intermediatePathSource.getPathName() ).append( getPathName() );
}
return new SqmEntityValuedSimplePath<>(
navigablePath,
this,
lhs,
lhs.nodeBuilder()
);
}
@Override
public SqmPluralPartJoin<Object, J> createSqmJoin(
SqmFrom<?, Object> lhs,
SqmJoinType joinType,
String alias,
boolean fetched,
SqmCreationState creationState) {
return new SqmPluralPartJoin<>(
lhs,
this,
alias,
joinType,
creationState.getCreationContext().getNodeBuilder()
);
}
@Override
public String getName() {
return getPathName();
}
}

View File

@ -7,23 +7,33 @@
package org.hibernate.metamodel.model.domain.internal;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.graph.internal.SubGraphImpl;
import org.hibernate.graph.spi.SubGraphImplementor;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.metamodel.UnsupportedMappingException;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.model.domain.AbstractIdentifiableType;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.IdentifiableDomainType;
import org.hibernate.metamodel.model.domain.JpaMetamodel;
import org.hibernate.metamodel.model.domain.MappedSuperclassDomainType;
import org.hibernate.metamodel.model.domain.PersistentAttribute;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.type.descriptor.java.JavaType;
/**
* @author Emmanuel Bernard
* @author Steve Ebersole
*/
public class MappedSuperclassTypeImpl<X> extends AbstractIdentifiableType<X> implements MappedSuperclassDomainType<X> {
public class MappedSuperclassTypeImpl<J> extends AbstractIdentifiableType<J> implements MappedSuperclassDomainType<J> {
public MappedSuperclassTypeImpl(
JavaType<X> javaType,
JavaType<J> javaType,
MappedSuperclass mappedSuperclass,
IdentifiableDomainType<? super X> superType,
IdentifiableDomainType<? super J> superType,
JpaMetamodel jpaMetamodel) {
super(
javaType.getJavaType().getTypeName(),
@ -36,18 +46,91 @@ public class MappedSuperclassTypeImpl<X> extends AbstractIdentifiableType<X> imp
);
}
@Override
public String getPathName() {
return getTypeName();
}
@Override
public MappedSuperclassDomainType<J> getSqmPathType() {
return this;
}
@Override
public SqmPathSource<?> findSubPathSource(String name) {
final PersistentAttribute<?,?> attribute = findAttribute( name );
if ( attribute != null ) {
return (SqmPathSource<?>) attribute;
}
if ( "id".equalsIgnoreCase( name ) ) {
if ( hasIdClass() ) {
return getIdentifierDescriptor();
}
}
return null;
}
@Override
public PersistentAttribute<? super J, ?> findAttribute(String name) {
final PersistentAttribute<? super J, ?> attribute = super.findAttribute( name );
if ( attribute != null ) {
return attribute;
}
if ( "id".equalsIgnoreCase( name ) || EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( name ) ) {
final SingularPersistentAttribute<J, ?> idAttribute = findIdAttribute();
//noinspection RedundantIfStatement
if ( idAttribute != null ) {
return idAttribute;
}
}
return null;
}
@Override
public BindableType getBindableType() {
return BindableType.ENTITY_TYPE;
}
@Override
public PersistenceType getPersistenceType() {
return PersistenceType.MAPPED_SUPERCLASS;
}
@Override
public <S extends X> SubGraphImplementor<S> makeSubGraph(Class<S> subType) {
throw new NotYetImplementedException( );
@SuppressWarnings("unchecked")
public <S extends J> SubGraphImplementor<S> makeSubGraph(Class<S> subType) {
if ( ! getBindableJavaType().isAssignableFrom( subType ) ) {
throw new IllegalArgumentException(
String.format(
"MappedSuperclass type [%s] cannot be treated as requested sub-type [%s]",
getTypeName(),
subType.getName()
)
);
}
return new SubGraphImpl( this, true, jpaMetamodel() );
}
@Override
public SubGraphImplementor<J> makeSubGraph() {
return makeSubGraph( getBindableJavaType() );
}
@Override
protected boolean isIdMappingRequired() {
return false;
}
@Override
public SqmPath<J> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) {
throw new UnsupportedMappingException(
"MappedSuperclassType cannot be used to create an SqmPath - that would be an SqmFrom which are created directly"
);
}
}

View File

@ -21,6 +21,7 @@ import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@ -164,6 +165,7 @@ import org.hibernate.metamodel.mapping.EntityVersionMapping;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.MappingModelHelper;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.NaturalIdMapping;
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
@ -202,6 +204,7 @@ import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.property.access.spi.Setter;
import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.query.spi.NavigablePath;
import org.hibernate.query.SemanticException;
import org.hibernate.query.named.NamedQueryMemento;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.sql.internal.SQLQueryParser;
@ -6337,13 +6340,36 @@ public abstract class AbstractEntityPersister
}
else {
if ( subclassMappingTypes != null && !subclassMappingTypes.isEmpty() ) {
ModelPart attribute = null;
for ( EntityMappingType subMappingType : subclassMappingTypes.values() ) {
final ModelPart subDefinedAttribute = subMappingType.findSubTypesSubPart( name, treatTargetType );
if ( subDefinedAttribute != null ) {
return subDefinedAttribute;
if ( attribute != null && !MappingModelHelper.isCompatibleModelPart( attribute, subDefinedAttribute ) ) {
throw new IllegalArgumentException(
new SemanticException(
String.format(
Locale.ROOT,
"Could not resolve attribute '%s' of '%s' due to the attribute being declared in multiple sub types: ['%s', '%s']",
name,
getJavaType().getJavaType().getTypeName(),
( (AttributeMapping) attribute ).getDeclaringType()
.getJavaType()
.getJavaType()
.getTypeName(),
( (AttributeMapping) subDefinedAttribute ).getDeclaringType()
.getJavaType()
.getJavaType()
.getTypeName()
)
)
);
}
attribute = subDefinedAttribute;
}
}
if ( attribute != null ) {
return attribute;
}
}
}

View File

@ -245,7 +245,12 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
public void consumeTreat(String entityName, boolean isTerminal) {
final EntityDomainType<Object> entityDomainType = creationState.getCreationContext().getJpaMetamodel()
.entity( entityName );
currentPath = currentPath.treatAs( entityDomainType, isTerminal ? alias : null );
if ( isTerminal ) {
currentPath = currentPath.treatAs( entityDomainType, alias );
}
else {
currentPath = currentPath.treatAs( entityDomainType );
}
creationState.getCurrentProcessingState().getPathRegistry().register( currentPath );
}

View File

@ -4259,8 +4259,16 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
@Override
public SqmPath<?> visitTreatedNavigablePath(HqlParser.TreatedNavigablePathContext ctx) {
final DotIdentifierConsumer consumer = dotIdentifierConsumerStack.getCurrent();
final boolean madeNested;
if ( consumer instanceof QualifiedJoinPathConsumer) {
( (QualifiedJoinPathConsumer) consumer ).setNested( true );
final QualifiedJoinPathConsumer qualifiedJoinPathConsumer = (QualifiedJoinPathConsumer) consumer;
madeNested = !qualifiedJoinPathConsumer.isNested();
if ( madeNested ) {
qualifiedJoinPathConsumer.setNested( true );
}
}
else {
madeNested = false;
}
consumeManagedTypeReference( (HqlParser.PathContext) ctx.getChild( 2 ) );
@ -4272,18 +4280,27 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
SqmPath<?> result = (SqmPath<?>) consumer.getConsumedPart();
if ( hasContinuation ) {
dotIdentifierConsumerStack.push(
new BasicDotIdentifierConsumer( result, this ) {
@Override
protected void reset() {
if ( madeNested ) {
// Reset the nested state before consuming the terminal identifier
( (QualifiedJoinPathConsumer) consumer ).setNested( false );
}
final boolean addConsumer = !( consumer instanceof QualifiedJoinPathConsumer );
if ( addConsumer ) {
dotIdentifierConsumerStack.push(
new BasicDotIdentifierConsumer( result, this ) {
@Override
protected void reset() {
}
}
}
);
);
}
try {
result = consumeDomainPath( (HqlParser.SimplePathContext) ctx.getChild( 6 ).getChild( 1 ) );
}
finally {
dotIdentifierConsumerStack.pop();
if ( addConsumer ) {
dotIdentifierConsumerStack.pop();
}
}
}

View File

@ -20,11 +20,13 @@ import org.hibernate.metamodel.model.domain.BasicDomainType;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.MappedSuperclassDomainType;
import org.hibernate.metamodel.model.domain.internal.AnyMappingSqmPathSource;
import org.hibernate.metamodel.model.domain.internal.BasicSqmPathSource;
import org.hibernate.metamodel.model.domain.internal.EmbeddedSqmPathSource;
import org.hibernate.metamodel.model.domain.internal.EntitySqmPathSource;
import org.hibernate.metamodel.MappingMetamodel;
import org.hibernate.metamodel.model.domain.internal.MappedSuperclassSqmPathSource;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.spi.NavigablePath;
import org.hibernate.query.sqm.SqmExpressible;
@ -75,7 +77,7 @@ public class SqmMappingModelHelper {
DomainType<J> valueDomainType,
Bindable.BindableType jpaBindableType) {
if ( valueDomainType instanceof BasicDomainType ) {
if ( valueDomainType instanceof BasicDomainType<?> ) {
return new BasicSqmPathSource<>(
name,
(BasicDomainType<J>) valueDomainType,
@ -83,7 +85,7 @@ public class SqmMappingModelHelper {
);
}
if ( valueDomainType instanceof AnyMappingDomainType ) {
if ( valueDomainType instanceof AnyMappingDomainType<?> ) {
return new AnyMappingSqmPathSource<>(
name,
(AnyMappingDomainType<J>) valueDomainType,
@ -91,7 +93,7 @@ public class SqmMappingModelHelper {
);
}
if ( valueDomainType instanceof EmbeddableDomainType ) {
if ( valueDomainType instanceof EmbeddableDomainType<?> ) {
return new EmbeddedSqmPathSource<>(
name,
(EmbeddableDomainType<J>) valueDomainType,
@ -99,7 +101,7 @@ public class SqmMappingModelHelper {
);
}
if ( valueDomainType instanceof EntityDomainType ) {
if ( valueDomainType instanceof EntityDomainType<?> ) {
return new EntitySqmPathSource<>(
name,
(EntityDomainType<J>) valueDomainType,
@ -107,6 +109,14 @@ public class SqmMappingModelHelper {
);
}
if ( valueDomainType instanceof MappedSuperclassDomainType<?> ) {
return new MappedSuperclassSqmPathSource<>(
name,
(MappedSuperclassDomainType<J>) valueDomainType,
jpaBindableType
);
}
throw new IllegalArgumentException(
"Unrecognized value type Java-type [" + valueDomainType.getTypeName() + "] for plural attribute value"
);
@ -169,8 +179,9 @@ public class SqmMappingModelHelper {
public static EntityMappingType resolveExplicitTreatTarget(
SqmPath<?> sqmPath,
SqmToSqlAstConverter converter) {
if ( sqmPath instanceof SqmTreatedPath ) {
final SqmTreatedPath treatedPath = (SqmTreatedPath) sqmPath;
final SqmPath<?> parentPath = sqmPath.getLhs();
if ( parentPath instanceof SqmTreatedPath ) {
final SqmTreatedPath<?, ?> treatedPath = (SqmTreatedPath<?, ?>) parentPath;
return resolveEntityPersister( treatedPath.getTreatTarget(), converter.getCreationContext().getSessionFactory() );
}

View File

@ -39,19 +39,15 @@ import jakarta.persistence.InheritanceType;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import org.hibernate.query.sqm.InterpretationException;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.NotImplementedYet;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.Assert;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
/**
@ -109,7 +105,6 @@ public class JoinedInheritanceEagerTest {
@Test
@JiraKey("HHH-12375")
@NotImplementedYet( strict = false )
public void joinUnrelatedCollectionOnBaseType(SessionFactoryScope scope) {
scope.inSession(
session -> {
@ -119,8 +114,8 @@ public class JoinedInheritanceEagerTest {
session.createQuery( "from BaseEntity b join b.attributes" ).list();
fail( "Expected a resolution exception for property 'attributes'!" );
}
catch (InterpretationException ex) {
assertTrue( ex.getMessage().contains( "could not resolve property: attributes " ) );
catch (IllegalArgumentException ex) {
Assert.assertTrue( ex.getCause().getCause().getMessage().contains( "Could not resolve attribute 'attributes' "));
}
finally {
session.getTransaction().commit();

View File

@ -37,6 +37,14 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.testing.jdbc.SQLStatementInspector;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import jakarta.persistence.AssociationOverride;
import jakarta.persistence.AssociationOverrides;
import jakarta.persistence.Basic;
@ -57,14 +65,6 @@ import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OrderColumn;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.NotImplementedYet;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
/**
* @author Christian Beikov
*/
@ -79,68 +79,60 @@ import org.junit.jupiter.api.Test;
MultiInheritanceImplicitDowncastTest.PolymorphicPropertySub2.class,
MultiInheritanceImplicitDowncastTest.PolymorphicSub1.class,
MultiInheritanceImplicitDowncastTest.PolymorphicSub2.class
}
)
@SessionFactory
@NotImplementedYet( strict = false )
})
@SessionFactory(statementInspectorClass = SQLStatementInspector.class)
public class MultiInheritanceImplicitDowncastTest {
@Test
public void testQueryingSingle(SessionFactoryScope scope) {
scope.inTransaction( (s) -> {
final String base = "from PolymorphicPropertyBase p left join ";
s.createQuery( base + "p.base b left join b.relation1 " ).getResultList();
s.createQuery( base + "p.base b left join b.relation2 " ).getResultList();
s.createQuery( base + "p.baseEmbeddable.embeddedRelation1 b left join b.relation1" ).getResultList();
s.createQuery( base + "p.baseEmbeddable.embeddedRelation2 b left join b.relation2" ).getResultList();
s.createQuery( base + "p.baseEmbeddable.embeddedBase b left join b.relation1" ).getResultList();
s.createQuery( base + "p.baseEmbeddable.embeddedBase b left join b.relation2" ).getResultList();
} );
}
@Test
public void testQueryingMultiple(SessionFactoryScope scope) {
scope.inTransaction( (s) -> {
final String base = "from PolymorphicPropertyBase p left join ";
s.createQuery( base + "p.base b left join b.relation1 left join b.relation2" ).getResultList();
s.createQuery( base + "p.base b left join b.relation2 left join b.relation1" ).getResultList();
s.createQuery( base + "p.baseEmbeddable.embeddedBase b left join b.relation1 left join b.relation2" ).getResultList();
s.createQuery( base + "p.baseEmbeddable.embeddedBase b left join b.relation2 left join b.relation1" ).getResultList();
} );
public void testIllegalBaseJoin(SessionFactoryScope scope) {
try {
scope.inSession(
s -> s.createQuery( "from PolymorphicPropertyBase p left join p.base b left join b.relation1" )
);
}
catch (IllegalArgumentException ex) {
Assertions.assertTrue( ex.getCause()
.getCause()
.getMessage()
.contains( "Could not resolve attribute 'base' " ) );
}
}
@Test
public void testMultiJoinAddition1(SessionFactoryScope scope) {
testMultiJoinAddition( "from PolymorphicPropertyBase p left join p.base b left join b.relation1", scope );
testMultiJoinAddition(
scope,
"base_sub_1",
"select 1 from PolymorphicPropertyBase p left join treat(p as PolymorphicPropertySub1).base b left join b.relation1"
);
}
@Test
public void testMultiJoinAddition2(SessionFactoryScope scope) {
testMultiJoinAddition( "from PolymorphicPropertyBase p left join p.base b left join b.relation2", scope );
testMultiJoinAddition(
scope,
"base_sub_2",
"select 1 from PolymorphicPropertyBase p left join treat(p as PolymorphicPropertySub2).base b left join b.relation2"
);
}
private void testMultiJoinAddition(String hql, SessionFactoryScope scope) {
throw new NotYetImplementedFor6Exception( getClass() );
// final HQLQueryPlan plan = sessionFactory().getQueryInterpretationCache().getHQLQueryPlan(
// hql,
// false,
// Collections.EMPTY_MAP
// );
// assertEquals( 1, plan.getTranslators().length );
// final QueryTranslator translator = plan.getTranslators()[0];
// final String generatedSql = translator.getSQLString();
//
// int sub1JoinColumnIndex = generatedSql.indexOf( ".base_sub_1" );
// assertNotEquals(
// "Generated SQL doesn't contain a join for 'base' with 'PolymorphicSub1' via 'base_sub_1':\n" + generatedSql,
// -1,
// sub1JoinColumnIndex
// );
// int sub2JoinColumnIndex = generatedSql.indexOf( ".base_sub_2" );
// assertNotEquals(
// "Generated SQL doesn't contain a join for 'base' with 'PolymorphicSub2' via 'base_sub_2':\n" + generatedSql,
// -1,
// sub2JoinColumnIndex
// );
private void testMultiJoinAddition(SessionFactoryScope scope, String joinColumnBase, String hql) {
SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector();
scope.inTransaction(
s -> {
sqlStatementInterceptor.clear();
s.createQuery( hql ).getResultList();
sqlStatementInterceptor.assertExecutedCount( 1 );
final String generatedSql = sqlStatementInterceptor.getSqlQueries().get( 0 );
int sub1JoinColumnIndex = generatedSql.indexOf( "." + joinColumnBase );
Assertions.assertNotEquals(
-1,
sub1JoinColumnIndex,
"Generated SQL doesn't contain a join for 'base' via '" + joinColumnBase + "':\n" + generatedSql
);
}
);
}
@MappedSuperclass
@ -466,13 +458,12 @@ public class MultiInheritanceImplicitDowncastTest {
}
@MappedSuperclass
public abstract static class PolymorphicPropertyMapBase<T extends PolymorphicBase, E extends BaseEmbeddable> extends
PolymorphicPropertyBase {
public abstract static class PolymorphicPropertyMapBase<T extends PolymorphicBase> extends PolymorphicPropertyBase {
private static final long serialVersionUID = 1L;
private T base;
private E baseEmbeddable;
private Set<T> bases;
public PolymorphicPropertyMapBase() {
}
@ -486,21 +477,22 @@ public class MultiInheritanceImplicitDowncastTest {
this.base = base;
}
@Embedded
public E getBaseEmbeddable() {
return baseEmbeddable;
@OneToMany
public Set<T> getBases() {
return bases;
}
public void setBaseEmbeddable(E baseEmbeddable) {
this.baseEmbeddable = baseEmbeddable;
public void setBases(Set<T> bases) {
this.bases = bases;
}
}
@Entity(name = "PolymorphicPropertySub1")
@AssociationOverrides({
@AssociationOverride(name = "base", joinColumns = @JoinColumn(name = "base_sub_1"))
})
public static class PolymorphicPropertySub1 extends PolymorphicPropertyMapBase<PolymorphicSub1, Embeddable1> {
public static class PolymorphicPropertySub1 extends PolymorphicPropertyMapBase<PolymorphicSub1> {
private static final long serialVersionUID = 1L;
public PolymorphicPropertySub1() {
@ -511,7 +503,7 @@ public class MultiInheritanceImplicitDowncastTest {
@AssociationOverrides({
@AssociationOverride(name = "base", joinColumns = @JoinColumn(name = "base_sub_2"))
})
public static class PolymorphicPropertySub2 extends PolymorphicPropertyMapBase<PolymorphicSub2, Embeddable2> {
public static class PolymorphicPropertySub2 extends PolymorphicPropertyMapBase<PolymorphicSub2> {
private static final long serialVersionUID = 1L;
public PolymorphicPropertySub2() {

View File

@ -16,7 +16,6 @@ import org.hibernate.Hibernate;
import org.hibernate.query.Query;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.NotImplementedYet;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
@ -47,7 +46,6 @@ public class JoinedSubclassTest {
}
@Test
@NotImplementedYet( strict = false )
public void testJoinedSubclass(SessionFactoryScope scope) {
scope.inTransaction( s -> {
@ -76,8 +74,6 @@ public class JoinedSubclassTest {
s.save( mark );
s.save( joe );
assertEquals( s.createQuery( "from java.lang.Object" ).list().size(), 0 );
assertEquals( s.createQuery( "from Person" ).list().size(), 3 );
assertEquals( s.createQuery( "from Person p where p.class = Customer" ).list().size(), 1 );
assertEquals( s.createQuery( "from Person p where p.class = Person" ).list().size(), 1 );