HHH-18125 Support for `type()` and `treat()` operators for Embeddables

This commit is contained in:
Marco Belladelli 2024-05-13 12:46:24 +02:00
parent 11384ab917
commit 2e9acf1ded
83 changed files with 1786 additions and 520 deletions

View File

@ -543,8 +543,8 @@ The following special functions make it possible to discover or narrow expressio
|===
| Special function | Purpose | Signature | JPA standard
| `type()` | The (concrete) entity type | `type(e)` | ✔
| `treat()` | Narrow an entity type | `treat(e as Entity)` | ✔
| `type()` | The (concrete) entity or embeddable type | `type(e)` | ✔
| `treat()` | Narrow an entity or embeddable type | `treat(e as Entity)` | ✔
| `cast()` | Narrow a basic type | `cast(x as Type)` | ✖
| `str()` | Cast to a string | `str(x)` | ✖
|===
@ -555,7 +555,7 @@ Let's see what these functions do.
[discrete]
===== Evaluating an entity type
The function `type()`, applied to an identification variable or entity-valued path expression, evaluates to the concrete type, that is, the Java `Class`, of the referenced entity.
The function `type()`, applied to an identification variable or to an entity-valued or embeddable-valued path expression, evaluates to the concrete type, that is, the Java `Class`, of the referenced entity or embeddable.
This is mainly useful when dealing with entity inheritance hierarchies.
[[entity-type-exp-example]]
@ -571,7 +571,7 @@ where type(payment) = CreditCardPayment
===== Narrowing an entity type
The function `treat()` may be used to narrow the type of an identification variable.
This is useful when dealing with entity inheritance hierarchies.
This is useful when dealing with entity or embeddable inheritance hierarchies.
[[treat-example]]
[source, hql]

View File

@ -395,7 +395,7 @@ It's possible to customize the discriminator column mapping:
* For the whole `@Embeddable` type, by using `@DiscriminatorColumn` or `@DiscriminatorFormula` on the *root* class of the inheritance hierarchy
(NOTE: if using the same inheritance-enabled embeddable type for two different properties in the same entity mapping,
this will cause a column name conflict);
* For a specific `@Embedded` property, by using the `@AttributeOverride` annotation with the special name `{discriminator}`.
* For a specific `@Embedded` property, by using the `@AttributeOverride` annotation with the special name `+{discriminator}+`.
Finally, to specify custom discriminator values for each subtype one can annotate the inheritance hierarchy's
classes with `@DiscriminatorValue`.
@ -407,6 +407,8 @@ Embeddable inheritance is *NOT* supported for `@EmbeddedId`, embeddable types us
and embedded properties using a custom `@CompositeType`.
====
Note that the `type()` and `treat()` functions are also supported for embeddable inheritance, see <<chapters/jndi/QueryLanguage.adoc#hql-functions-typecasts,types and typecasts>>.
[[embeddable-inheritance-example]]
.Example mapping of an embeddable inheritance hierarchy
[source,java]

View File

@ -750,8 +750,8 @@ The following special functions make it possible to discover or narrow expressio
|===
| Special function | Purpose | Signature | JPA standard
| `type()` | The (concrete) entity name | `type(e)` | &check;
| `treat()` | Narrow an entity type | `treat(e as Entity)` | &check;
| `type()` | The (concrete) entity or embeddable type | `type(e)` | &check;
| `treat()` | Narrow an entity or embeddable type | `treat(e as Entity)` | &check;
| `cast()` | Narrow a basic type | `cast(x as Type)` | &cross;
| `str()` | Cast to a string | `str(x)` | &cross;
|===
@ -761,7 +761,7 @@ Let's see what these functions do.
[[hql-function-type]]
===== `type()`
The function `type()`, applied to an identification variable, evaluates to the entity name of the referenced entity.
The function `type()`, applied to an identification variable or to an entity-valued or embeddable-valued path expression, evaluates to the concrete type, that is, the Java `Class`, of the referenced entity or embeddable.
This is mainly useful when dealing with entity inheritance hierarchies.
[[hql-entity-type-exp-example]]
@ -777,7 +777,7 @@ include::{example-dir-hql}/HQLTest.java[tags=hql-entity-type-exp-example]
===== `treat()`
The function `treat()` may be used to narrow the type of an identification variable.
This is useful when dealing with entity inheritance hierarchies.
This is useful when dealing with entity or embeddable inheritance hierarchies.
[[hql-treat-example]]
====

View File

@ -18,6 +18,7 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import org.hibernate.AssertionFailure;
import org.hibernate.DuplicateMappingException;
@ -95,6 +96,7 @@ import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.mapping.DiscriminatorType;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.query.named.NamedObjectRepository;
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
@ -136,6 +138,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
private final List<Component> composites = new ArrayList<>();
private final Map<Class<?>, Component> genericComponentsMap = new HashMap<>();
private final Map<XClass, List<XClass>> embeddableSubtypes = new HashMap<>();
private final Map<Class<?>, DiscriminatorType<?>> embeddableDiscriminatorTypesMap = new HashMap<>();
private final Map<String,Collection> collectionBindingMap = new HashMap<>();
private final Map<String, FilterDefinition> filterDefinitionMap = new HashMap<>();
@ -296,6 +299,13 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
return subclasses != null ? subclasses : List.of();
}
@Override
public DiscriminatorType<?> resolveEmbeddableDiscriminatorType(
Class<?> embeddableClass,
Supplier<DiscriminatorType<?>> supplier) {
return embeddableDiscriminatorTypesMap.computeIfAbsent( embeddableClass, k -> supplier.get() );
}
@Override
public SessionFactoryBuilder getSessionFactoryBuilder() {
throw new UnsupportedOperationException(
@ -2072,6 +2082,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
entityBindingMap,
composites,
genericComponentsMap,
embeddableDiscriminatorTypesMap,
mappedSuperClasses,
collectionBindingMap,
typeDefRegistry.copyRegistrationMap(),

View File

@ -16,6 +16,7 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import org.hibernate.HibernateException;
@ -60,6 +61,7 @@ import org.hibernate.mapping.Property;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.UserDefinedObjectType;
import org.hibernate.mapping.UserDefinedType;
import org.hibernate.metamodel.mapping.DiscriminatorType;
import org.hibernate.procedure.spi.NamedCallableQueryMemento;
import org.hibernate.query.internal.NamedObjectRepositoryImpl;
import org.hibernate.query.named.NamedObjectRepository;
@ -91,6 +93,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
private final Map<String,PersistentClass> entityBindingMap;
private final List<Component> composites;
private final Map<Class<?>, Component> genericComponentsMap;
private final Map<Class<?>, DiscriminatorType<?>> embeddableDiscriminatorTypesMap;
private final Map<Class<?>, MappedSuperclass> mappedSuperclassMap;
private final Map<String,Collection> collectionBindingMap;
private final Map<String, TypeDefinition> typeDefinitionMap;
@ -112,6 +115,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
Map<String, PersistentClass> entityBindingMap,
List<Component> composites,
Map<Class<?>, Component> genericComponentsMap,
Map<Class<?>, DiscriminatorType<?>> embeddableDiscriminatorTypesMap,
Map<Class<?>, MappedSuperclass> mappedSuperclassMap,
Map<String, Collection> collectionBindingMap,
Map<String, TypeDefinition> typeDefinitionMap,
@ -132,6 +136,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
this.entityBindingMap = entityBindingMap;
this.composites = composites;
this.genericComponentsMap = genericComponentsMap;
this.embeddableDiscriminatorTypesMap = embeddableDiscriminatorTypesMap;
this.mappedSuperclassMap = mappedSuperclassMap;
this.collectionBindingMap = collectionBindingMap;
this.typeDefinitionMap = typeDefinitionMap;
@ -569,6 +574,13 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
return genericComponentsMap.get( componentClass );
}
@Override
public DiscriminatorType<?> resolveEmbeddableDiscriminatorType(
Class<?> embeddableClass,
Supplier<DiscriminatorType<?>> supplier) {
return embeddableDiscriminatorTypesMap.computeIfAbsent( embeddableClass, k -> supplier.get() );
}
@Override
public org.hibernate.type.Type getIdentifierType(String entityName) throws MappingException {
final PersistentClass pc = entityBindingMap.get( entityName );
@ -664,4 +676,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
return genericComponentsMap;
}
public Map<Class<?>, DiscriminatorType<?>> getEmbeddableDiscriminatorTypesMap() {
return embeddableDiscriminatorTypesMap;
}
}

View File

@ -75,6 +75,7 @@ import static org.hibernate.boot.model.internal.GeneratorBinder.buildIdGenerator
import static org.hibernate.boot.model.internal.InheritanceState.getInheritanceStateOfSuperEntity;
import static org.hibernate.boot.model.internal.InheritanceState.getSuperclassInheritanceState;
import static org.hibernate.internal.CoreLogging.messageLogger;
import static org.hibernate.internal.util.StringHelper.unqualify;
import static org.hibernate.mapping.MetadataSource.ANNOTATIONS;
/**
@ -404,7 +405,7 @@ public final class AnnotationBinder {
private static void handleImport(XClass annotatedClass, MetadataBuildingContext context) {
if ( annotatedClass.isAnnotationPresent( Imported.class ) ) {
String qualifiedName = annotatedClass.getName();
String name = StringHelper.unqualify( qualifiedName );
String name = unqualify( qualifiedName );
String rename = annotatedClass.getAnnotation( Imported.class ).rename();
context.getMetadataCollector().addImport( rename.isEmpty() ? name : rename, qualifiedName );
}
@ -693,6 +694,11 @@ public final class AnnotationBinder {
getSuperclassInheritanceState( clazz, inheritanceStatePerClass );
final InheritanceState state =
new InheritanceState( clazz, inheritanceStatePerClass, buildingContext );
final AnnotatedClassType classType = buildingContext.getMetadataCollector().getClassType( clazz );
if ( classType == EMBEDDABLE && !clazz.isAnnotationPresent( Imported.class ) ) {
final String className = clazz.getName();
buildingContext.getMetadataCollector().addImport( unqualify( className ), className );
}
if ( superclassState != null ) {
//the classes are ordered thus preventing an NPE
superclassState.setHasSiblings( true );
@ -700,7 +706,7 @@ public final class AnnotationBinder {
getInheritanceStateOfSuperEntity( clazz, inheritanceStatePerClass );
if ( superEntityState != null ) {
state.setHasParents( true );
if ( buildingContext.getMetadataCollector().getClassType( clazz ) == EMBEDDABLE ) {
if ( classType == EMBEDDABLE ) {
buildingContext.getMetadataCollector().registerEmbeddableSubclass(
superEntityState.getClazz(),
clazz
@ -712,7 +718,7 @@ public final class AnnotationBinder {
state.setType( superclassState.getType() );
}
}
switch ( buildingContext.getMetadataCollector().getClassType( clazz ) ) {
switch ( classType ) {
case ENTITY:
case MAPPED_SUPERCLASS:
case EMBEDDABLE:

View File

@ -11,6 +11,7 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.hibernate.MappingException;
import org.hibernate.SessionFactory;
@ -30,6 +31,7 @@ import org.hibernate.mapping.FetchProfile;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Table;
import org.hibernate.metamodel.mapping.DiscriminatorType;
import org.hibernate.query.named.NamedObjectRepository;
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
@ -249,6 +251,13 @@ public abstract class AbstractDelegatingMetadata implements MetadataImplementor
return delegate().getGenericComponent( componentClass );
}
@Override
public DiscriminatorType<?> resolveEmbeddableDiscriminatorType(
Class<?> embeddableClass,
Supplier<DiscriminatorType<?>> supplier) {
return delegate().resolveEmbeddableDiscriminatorType( embeddableClass, supplier );
}
@Override
public NamedObjectRepository buildNamedQueryRepository(SessionFactoryImplementor sessionFactory) {
return delegate().buildNamedQueryRepository( sessionFactory );

View File

@ -8,6 +8,7 @@ package org.hibernate.boot.spi;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.hibernate.Incubating;
import org.hibernate.MappingException;
@ -15,6 +16,7 @@ import org.hibernate.boot.Metadata;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.metamodel.mapping.DiscriminatorType;
import org.hibernate.query.named.NamedObjectRepository;
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
import org.hibernate.type.spi.TypeConfiguration;
@ -58,4 +60,6 @@ public interface MetadataImplementor extends Metadata {
void visitRegisteredComponents(Consumer<Component> consumer);
Component getGenericComponent(Class<?> componentClass);
DiscriminatorType<?> resolveEmbeddableDiscriminatorType(Class<?> embeddableClass, Supplier<DiscriminatorType<?>> supplier);
}

View File

@ -136,8 +136,8 @@ public class StructHelper {
instantiator = representationStrategy.getInstantiator();
}
else {
// the discriminator here is the composite class name because it gets converted to the domain type when extracted
instantiator = representationStrategy.getInstantiatorForClass( (String) attributeValues.getDiscriminator() );
// the discriminator here is the composite class because it gets converted to the domain type when extracted
instantiator = representationStrategy.getInstantiatorForClass( ( (Class<?>) attributeValues.getDiscriminator() ).getName() );
}
return instantiator.instantiate( attributeValues, sessionFactory );
}

View File

@ -35,6 +35,7 @@ import org.hibernate.metamodel.UnsupportedMappingException;
import org.hibernate.metamodel.ValueClassification;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.CompositeIdentifierMapping;
import org.hibernate.metamodel.mapping.DiscriminatorType;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
@ -268,7 +269,7 @@ public class AttributeFactory {
false,
context.getJpaMetamodel()
);
context.registerEmbeddableType( embeddableType, component);
context.registerEmbeddableType( embeddableType, component );
if ( component.isPolymorphic() ) {
final java.util.Collection<String> embeddableSubclasses = component.getDiscriminatorValues().values();
@ -283,12 +284,14 @@ public class AttributeFactory {
continue;
}
final Class<?> subclass = cls.classForName( subclassName );
context.registerEmbeddableType( new EmbeddableTypeImpl<>(
final EmbeddableTypeImpl<?> subType = new EmbeddableTypeImpl<>(
context.getJavaTypeRegistry().resolveManagedTypeDescriptor( subclass ),
domainTypes.get( component.getSuperclass( subclassName ) ),
false,
context.getJpaMetamodel()
), component );
);
domainTypes.put( subclassName, subType );
context.registerEmbeddableType( subType, component );
}
}

View File

@ -7,7 +7,6 @@
package org.hibernate.metamodel.internal;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Locale;

View File

@ -47,7 +47,7 @@ public class DefaultDiscriminatorConverter<O,R> extends DiscriminatorConverter<O
JavaType<O> domainJavaType,
JavaType<R> relationalJavaType,
MappingMetamodelImplementor mappingMetamodel) {
super( discriminatorRole, domainJavaType, relationalJavaType );
super( discriminatorRole.getFullPath(), domainJavaType, relationalJavaType );
this.mappingMetamodel = mappingMetamodel;
}

View File

@ -7,7 +7,6 @@
package org.hibernate.metamodel.mapping;
import org.hibernate.metamodel.RepresentationMode;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
import org.hibernate.type.descriptor.java.JavaType;
@ -20,21 +19,21 @@ import java.util.function.Function;
*/
public abstract class DiscriminatorConverter<O,R> implements BasicValueConverter<O,R> {
private final NavigableRole discriminatorRole;
private final String discriminatorName;
private final JavaType<O> domainJavaType;
private final JavaType<R> relationalJavaType;
public DiscriminatorConverter(
NavigableRole discriminatorRole,
String discriminatorName,
JavaType<O> domainJavaType,
JavaType<R> relationalJavaType) {
this.discriminatorRole = discriminatorRole;
this.discriminatorName = discriminatorName;
this.domainJavaType = domainJavaType;
this.relationalJavaType = relationalJavaType;
}
public NavigableRole getNavigableRole() {
return discriminatorRole;
public String getDiscriminatorName() {
return discriminatorName;
}
@Override
@ -95,7 +94,7 @@ public abstract class DiscriminatorConverter<O,R> implements BasicValueConverter
@Override
public String toString() {
return "DiscriminatorConverter(" + discriminatorRole.getFullPath() + ")";
return "DiscriminatorConverter(" + discriminatorName + ")";
}
public abstract void forEachValueDetail(Consumer<DiscriminatorValueDetails> consumer);

View File

@ -15,6 +15,8 @@ import java.util.function.Function;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.internal.EmbeddableDiscriminatorValueDetailsImpl;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.type.BasicType;
@ -29,32 +31,34 @@ import org.hibernate.type.descriptor.java.JavaType;
*/
public class EmbeddableDiscriminatorConverter<O, R> extends DiscriminatorConverter<O, R> {
public static <O, R> EmbeddableDiscriminatorConverter<O, R> fromValueMappings(
NavigableRole role,
String discriminatedType,
JavaType<O> domainJavaType,
BasicType<R> underlyingJdbcMapping,
Map<Object, String> valueMappings) {
final List<DiscriminatorValueDetails> valueDetailsList = new ArrayList<>( valueMappings.size() );
Map<Object, String> valueMappings,
SessionFactoryImplementor sessionFactory) {
final List<EmbeddableDiscriminatorValueDetailsImpl> valueDetailsList = new ArrayList<>( valueMappings.size() );
final ClassLoaderService cls = sessionFactory.getServiceRegistry().requireService( ClassLoaderService.class );
valueMappings.forEach( (value, embeddableClassName) -> valueDetailsList.add( new EmbeddableDiscriminatorValueDetailsImpl(
value,
embeddableClassName
cls.classForName( embeddableClassName )
) ) );
return new EmbeddableDiscriminatorConverter<>(
role,
discriminatedType,
domainJavaType,
underlyingJdbcMapping.getJavaTypeDescriptor(),
valueDetailsList
);
}
private final Map<Object, DiscriminatorValueDetails> discriminatorValueToDetailsMap;
private final Map<String, DiscriminatorValueDetails> embeddableClassNameToDetailsMap;
private final Map<Object, EmbeddableDiscriminatorValueDetailsImpl> discriminatorValueToDetailsMap;
private final Map<String, EmbeddableDiscriminatorValueDetailsImpl> embeddableClassNameToDetailsMap;
public EmbeddableDiscriminatorConverter(
NavigableRole discriminatorRole,
String discriminatorName,
JavaType<O> domainJavaType,
JavaType<R> relationalJavaType,
List<DiscriminatorValueDetails> valueMappings) {
super( discriminatorRole, domainJavaType, relationalJavaType );
List<EmbeddableDiscriminatorValueDetailsImpl> valueMappings) {
super( discriminatorName, domainJavaType, relationalJavaType );
this.discriminatorValueToDetailsMap = new HashMap<>( valueMappings.size() );
this.embeddableClassNameToDetailsMap = new HashMap<>( valueMappings.size() );
@ -68,32 +72,18 @@ public class EmbeddableDiscriminatorConverter<O, R> extends DiscriminatorConvert
public O toDomainValue(R relationalForm) {
assert relationalForm == null || getRelationalJavaType().isInstance( relationalForm );
final DiscriminatorValueDetails matchingValueDetails = getDetailsForDiscriminatorValue( relationalForm );
final EmbeddableDiscriminatorValueDetailsImpl matchingValueDetails = getDetailsForDiscriminatorValue( relationalForm );
if ( matchingValueDetails == null ) {
throw new IllegalStateException( "Could not resolve discriminator value" );
}
//noinspection unchecked
return (O) matchingValueDetails.getIndicatedEntityName();
return (O) matchingValueDetails.getEmbeddableClass();
}
@Override
public R toRelationalValue(O domainForm) {
assert domainForm == null || domainForm instanceof String;
if ( domainForm == null ) {
return null;
}
final String embeddableClassName = (String) domainForm;
//noinspection unchecked
return (R) getDetailsForEntityName( embeddableClassName ).getValue();
}
@Override
public DiscriminatorValueDetails getDetailsForDiscriminatorValue(Object value) {
final DiscriminatorValueDetails valueMatch = discriminatorValueToDetailsMap.get( value );
public EmbeddableDiscriminatorValueDetailsImpl getDetailsForDiscriminatorValue(Object value) {
final EmbeddableDiscriminatorValueDetailsImpl valueMatch = discriminatorValueToDetailsMap.get( value );
if ( valueMatch != null ) {
return valueMatch;
}

View File

@ -72,7 +72,7 @@ public class MappedDiscriminatorConverter<O,R> extends DiscriminatorConverter<O,
JavaType<O> domainJavaType,
JavaType<R> relationalJavaType,
List<DiscriminatorValueDetails> valueMappings) {
super( discriminatorRole, domainJavaType, relationalJavaType );
super( discriminatorRole.getFullPath(), domainJavaType, relationalJavaType );
this.discriminatorValueToEntityNameMap = CollectionHelper.concurrentMap( valueMappings.size() );
this.entityNameToDiscriminatorValueMap = CollectionHelper.concurrentMap( valueMappings.size() );

View File

@ -27,8 +27,8 @@ public class DiscriminatorTypeImpl<O> extends ConvertedBasicTypeImpl<O> implemen
BasicType<?> underlyingJdbcMapping,
DiscriminatorConverter<O,?> discriminatorValueConverter) {
super(
discriminatorValueConverter.getNavigableRole().getFullPath(),
"Discriminator type " + discriminatorValueConverter.getNavigableRole().getFullPath(),
discriminatorValueConverter.getDiscriminatorName(),
"Discriminator type " + discriminatorValueConverter.getDiscriminatorName(),
underlyingJdbcMapping.getJdbcType(),
discriminatorValueConverter
);

View File

@ -20,11 +20,15 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
*/
public class EmbeddableDiscriminatorValueDetailsImpl implements DiscriminatorValueDetails {
final Object value;
final String embeddableClassName;
final Class<?> embeddableClass;
public EmbeddableDiscriminatorValueDetailsImpl(Object value, String embeddableClassName) {
public EmbeddableDiscriminatorValueDetailsImpl(Object value, Class<?> embeddableClass) {
this.value = value;
this.embeddableClassName = embeddableClassName;
this.embeddableClass = embeddableClass;
}
public Class<?> getEmbeddableClass() {
return embeddableClass;
}
@Override
@ -34,7 +38,7 @@ public class EmbeddableDiscriminatorValueDetailsImpl implements DiscriminatorVal
@Override
public String getIndicatedEntityName() {
return embeddableClassName;
return embeddableClass.getName();
}
@Override

View File

@ -16,7 +16,6 @@ import java.util.function.Function;
import org.hibernate.MappingException;
import org.hibernate.SharedSessionContract;
import org.hibernate.WrongClassException;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.aggregate.AggregateSupport;
@ -47,6 +46,7 @@ import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SelectableMappings;
@ -54,7 +54,6 @@ import org.hibernate.metamodel.mapping.SelectablePath;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.persister.entity.DiscriminatorHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.property.access.spi.Getter;
import org.hibernate.property.access.spi.PropertyAccess;
@ -83,6 +82,7 @@ import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.spi.CompositeTypeImplementor;
import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.internal.util.StringHelper.qualify;
import static org.hibernate.persister.entity.DiscriminatorHelper.getDiscriminatorType;
import static org.hibernate.type.SqlTypes.JSON;
@ -736,9 +736,12 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
scale = column.getScale();
}
final DiscriminatorType<?> discriminatorType = buildDiscriminatorType(
bootDescriptor,
creationContext
final DiscriminatorType<?> discriminatorType = creationContext.getMetadata().resolveEmbeddableDiscriminatorType(
bootDescriptor.getComponentClass(),
() -> buildDiscriminatorType(
bootDescriptor,
creationContext
)
);
return new ExplicitColumnDiscriminatorMappingImpl(
@ -758,17 +761,18 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
);
}
private DiscriminatorType<?> buildDiscriminatorType(
private static DiscriminatorType<?> buildDiscriminatorType(
Component bootDescriptor,
RuntimeModelCreationContext creationContext) {
final JavaTypeRegistry javaTypeRegistry = creationContext.getSessionFactory().getTypeConfiguration().getJavaTypeRegistry();
final JavaType<String> domainJavaType = javaTypeRegistry.resolveDescriptor( String.class );
final JavaType<String> domainJavaType = javaTypeRegistry.resolveDescriptor( Class.class );
final BasicType<?> discriminatorType = getDiscriminatorType( bootDescriptor );
final DiscriminatorConverter<String, ?> converter = EmbeddableDiscriminatorConverter.fromValueMappings(
getNavigableRole().append( EntityDiscriminatorMapping.DISCRIMINATOR_ROLE_NAME ),
qualify( bootDescriptor.getComponentClassName(), EntityDiscriminatorMapping.DISCRIMINATOR_ROLE_NAME ),
domainJavaType,
discriminatorType,
bootDescriptor.getDiscriminatorValues()
bootDescriptor.getDiscriminatorValues(),
creationContext.getSessionFactory()
);
return new DiscriminatorTypeImpl<>( discriminatorType, converter );
}
@ -882,6 +886,15 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
}
}
@Override
public ModelPart findSubPart(String name, EntityMappingType treatTargetType) {
if ( EntityDiscriminatorMapping.matchesRoleName( name ) ) {
return discriminatorMapping;
}
return super.findSubPart( name, treatTargetType );
}
@Override
public <X, Y> int breakDownJdbcValues(
Object domainValue,

View File

@ -6,8 +6,10 @@
*/
package org.hibernate.metamodel.model.domain;
import org.hibernate.query.BindableType;
import java.util.Collection;
import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.query.sqm.SqmPathSource;
import jakarta.persistence.metamodel.EmbeddableType;
@ -20,5 +22,16 @@ import jakarta.persistence.metamodel.EmbeddableType;
* @author Steve Ebersole
*/
public interface EmbeddableDomainType<J>
extends ManagedDomainType<J>, EmbeddableType<J>, SqmExpressible<J> {
extends TreatableDomainType<J>, EmbeddableType<J>, SqmExpressible<J> {
@Override
default EmbeddableDomainType<J> getSqmType() {
return this;
}
@Override
Collection<? extends EmbeddableDomainType<? extends J>> getSubTypes();
default boolean isPolymorphic() {
return getSuperType() != null || !getSubTypes().isEmpty();
}
}

View File

@ -17,14 +17,14 @@ import org.hibernate.query.sqm.SqmPathSource;
*
* @author Steve Ebersole
*/
public interface EntityDomainType<J> extends IdentifiableDomainType<J>, EntityType<J>, SqmPathSource<J> {
public interface EntityDomainType<J> extends IdentifiableDomainType<J>, EntityType<J>, TreatableDomainType<J> {
String getHibernateEntityName();
@Override
Collection<? extends EntityDomainType<? extends J>> getSubTypes();
@Override
default DomainType<J> getSqmType() {
default EntityDomainType<J> getSqmType() {
return this;
}
}

View File

@ -47,11 +47,21 @@ public interface JpaMetamodel extends Metamodel {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Extended features
/**
* Access to a managed type through its name
*/
<X> ManagedDomainType<X> managedType(String typeName);
/**
* Access to an entity supporting Hibernate's entity-name feature
*/
<X> EntityDomainType<X> entity(String entityName);
/**
* Access to an embeddable type from FQN
*/
<X> EmbeddableDomainType<X> embeddable(String embeddableName);
/**
* Specialized handling for resolving entity-name references in
* an HQL query

View File

@ -37,9 +37,6 @@ public interface ManagedDomainType<J> extends SqmExpressible<J>, DomainType<J>,
/**
* The descriptor of the supertype of this type.
*
* @apiNote we define this here in anticipation of eventually supporting
* embeddable inheritance
*/
ManagedDomainType<? super J> getSuperType();

View File

@ -0,0 +1,19 @@
/*
* 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;
import org.hibernate.query.sqm.SqmPathSource;
/**
* @author Marco Belladelli
*/
public interface TreatableDomainType<J> extends ManagedDomainType<J>, SqmPathSource<J> {
@Override
default DomainType<J> getSqmType() {
return this;
}
}

View File

@ -0,0 +1,46 @@
/*
* 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.DomainType;
import org.hibernate.query.ReturnableType;
import org.hibernate.query.sqm.SqmPathSource;
import static jakarta.persistence.metamodel.Bindable.BindableType.SINGULAR_ATTRIBUTE;
import static org.hibernate.metamodel.mapping.EntityDiscriminatorMapping.DISCRIMINATOR_ROLE_NAME;
/**
* Abstract SqmPathSource implementation for discriminators
*
* @author Steve Ebersole
*/
public abstract class AbstractDiscriminatorSqmPathSource<D> extends AbstractSqmPathSource<D>
implements ReturnableType<D> {
public AbstractDiscriminatorSqmPathSource(DomainType<D> domainType) {
super( DISCRIMINATOR_ROLE_NAME, null, domainType, SINGULAR_ATTRIBUTE );
}
@Override
public SqmPathSource<?> findSubPathSource(String name) {
throw new IllegalStateException( "Entity discriminator cannot be de-referenced" );
}
@Override
public PersistenceType getPersistenceType() {
return PersistenceType.BASIC;
}
@Override
public Class<D> getJavaType() {
return getExpressibleJavaType().getJavaTypeClass();
}
@Override
public DomainType<D> getSqmType() {
return this;
}
}

View File

@ -7,12 +7,17 @@
package org.hibernate.metamodel.model.domain.internal;
import java.io.Serializable;
import java.util.Collection;
import org.hibernate.metamodel.UnsupportedMappingException;
import org.hibernate.metamodel.model.domain.AbstractManagedType;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.PersistentAttribute;
import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.type.descriptor.java.JavaType;
import jakarta.persistence.metamodel.SingularAttribute;
@ -56,4 +61,40 @@ public class EmbeddableTypeImpl<J>
}
return count;
}
@Override
public Collection<? extends EmbeddableDomainType<? extends J>> getSubTypes() {
//noinspection unchecked
return (Collection<? extends EmbeddableDomainType<? extends J>>) super.getSubTypes();
}
@Override
public String getPathName() {
return getTypeName();
}
@Override
public EmbeddableDomainType<J> getSqmPathType() {
return this;
}
@Override
public SqmPathSource<?> findSubPathSource(String name) {
final PersistentAttribute<? super J, ?> attribute = getSqmPathType().findAttribute( name );
if ( attribute != null ) {
return (SqmPathSource<?>) attribute;
}
return (SqmPathSource<?>) findSubTypesAttribute( name );
}
@Override
public SqmPath<J> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) {
throw new UnsupportedMappingException( "EmbeddableType cannot be used to create an SqmPath" );
}
@Override
public BindableType getBindableType() {
return BindableType.SINGULAR_ATTRIBUTE;
}
}

View File

@ -0,0 +1,60 @@
/*
* 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.DiscriminatorSqmPath;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.domain.AbstractSqmPath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.spi.NavigablePath;
/**
* {@link SqmPath} specialization for an embeddable discriminator
*
* @author Marco Belladelli
*/
public class EmbeddedDiscriminatorSqmPath<T> extends AbstractSqmPath<T> implements DiscriminatorSqmPath<T> {
private final EmbeddableDomainType<T> embeddableDomainType;
@SuppressWarnings( { "rawtypes", "unchecked" } )
protected EmbeddedDiscriminatorSqmPath(
NavigablePath navigablePath,
SqmPathSource referencedPathSource,
SqmPath<?> lhs,
EmbeddableDomainType embeddableDomainType,
NodeBuilder nodeBuilder) {
super( navigablePath, referencedPathSource, lhs, nodeBuilder );
this.embeddableDomainType = embeddableDomainType;
}
@Override
public EmbeddableDomainType<T> getExpressible() {
return embeddableDomainType;
}
@Override
public EmbeddedDiscriminatorSqmPath<T> copy(SqmCopyContext context) {
final EmbeddedDiscriminatorSqmPath<T> existing = context.getCopy( this );
if ( existing != null ) {
return existing;
}
//noinspection unchecked
return context.registerCopy(
this,
(EmbeddedDiscriminatorSqmPath<T>) getLhs().copy( context ).type()
);
}
@Override
public <X> X accept(SemanticQueryWalker<X> walker) {
return walker.visitDiscriminatorPath( this );
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.EmbeddableDomainType;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.domain.SqmPath;
/**
* SqmPathSource implementation for embeddable discriminator
*
* @author Marco Belladelli
*/
public class EmbeddedDiscriminatorSqmPathSource<D> extends AbstractDiscriminatorSqmPathSource<D> {
private final EmbeddableDomainType<D> embeddableDomainType;
public EmbeddedDiscriminatorSqmPathSource(EmbeddableDomainType<D> embeddableDomainType) {
super( embeddableDomainType );
this.embeddableDomainType = embeddableDomainType;
}
@Override
public SqmPath<D> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) {
return new EmbeddedDiscriminatorSqmPath<>(
PathHelper.append( lhs, this, intermediatePathSource ),
pathModel,
lhs,
embeddableDomainType,
lhs.nodeBuilder()
);
}
}

View File

@ -14,6 +14,8 @@ import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import static org.hibernate.metamodel.mapping.EntityDiscriminatorMapping.DISCRIMINATOR_ROLE_NAME;
/**
* @author Steve Ebersole
*/
@ -21,6 +23,7 @@ public class EmbeddedSqmPathSource<J>
extends AbstractSqmPathSource<J>
implements CompositeSqmPathSource<J> {
private final boolean isGeneric;
private final EmbeddedDiscriminatorSqmPathSource<?> discriminatorPathSource;
public EmbeddedSqmPathSource(
String localPathName,
@ -30,6 +33,12 @@ public class EmbeddedSqmPathSource<J>
boolean isGeneric) {
super( localPathName, pathModel, domainType, jpaBindableType );
this.isGeneric = isGeneric;
if ( domainType.isPolymorphic() ) {
discriminatorPathSource = new EmbeddedDiscriminatorSqmPathSource<>( domainType );
}
else {
discriminatorPathSource = null;
}
}
@Override
@ -38,18 +47,17 @@ public class EmbeddedSqmPathSource<J>
}
@Override
public SqmPathSource<?> findSubPathSource(String name, JpaMetamodelImplementor metamodel) {
final PersistentAttribute<? super J, ?> attribute = getSqmPathType().findAttribute( name );
if ( attribute != null ) {
return (SqmPathSource<?>) attribute;
public SqmPathSource<?> findSubPathSource(String name) {
final SqmPathSource<?> subPathSource = getSqmPathType().findSubPathSource( name );
if ( subPathSource != null ) {
return subPathSource;
}
return (SqmPathSource<?>) getSqmPathType().findSubTypesAttribute( name );
}
if ( name.equals( DISCRIMINATOR_ROLE_NAME ) && discriminatorPathSource != null ) {
return discriminatorPathSource;
}
@Override
public SqmPathSource<?> findSubPathSource(String name) {
return (SqmPathSource<?>) getSqmPathType().findAttribute( name );
return null;
}
@Override

View File

@ -49,8 +49,8 @@ public class EntityDiscriminatorSqmPath<T> extends AbstractSqmPath<T> implements
}
@Override
public DiscriminatorSqmPathSource getExpressible() {
return (DiscriminatorSqmPathSource) getNodeType();
public EntityDiscriminatorSqmPathSource getExpressible() {
return (EntityDiscriminatorSqmPathSource) getNodeType();
}
@Override

View File

@ -9,28 +9,23 @@ package org.hibernate.metamodel.model.domain.internal;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.ReturnableType;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import static jakarta.persistence.metamodel.Bindable.BindableType.SINGULAR_ATTRIBUTE;
import static org.hibernate.metamodel.mapping.EntityDiscriminatorMapping.DISCRIMINATOR_ROLE_NAME;
/**
* SqmPathSource implementation for entity discriminator
*
* @author Steve Ebersole
*/
public class DiscriminatorSqmPathSource<D> extends AbstractSqmPathSource<D>
implements ReturnableType<D> {
public class EntityDiscriminatorSqmPathSource<D> extends AbstractDiscriminatorSqmPathSource<D> {
private final EntityDomainType<?> entityDomainType;
private final EntityMappingType entityMapping;
public DiscriminatorSqmPathSource(
public EntityDiscriminatorSqmPathSource(
DomainType<D> discriminatorValueType,
EntityDomainType<?> entityDomainType,
EntityMappingType entityMapping) {
super( DISCRIMINATOR_ROLE_NAME, null, discriminatorValueType, SINGULAR_ATTRIBUTE );
super( discriminatorValueType );
this.entityDomainType = entityDomainType;
this.entityMapping = entityMapping;
}
@ -54,24 +49,4 @@ public class DiscriminatorSqmPathSource<D> extends AbstractSqmPathSource<D>
lhs.nodeBuilder()
);
}
@Override
public SqmPathSource<?> findSubPathSource(String name) {
throw new IllegalStateException( "Entity discriminator cannot be de-referenced" );
}
@Override
public PersistenceType getPersistenceType() {
return PersistenceType.BASIC;
}
@Override
public Class<D> getJavaType() {
return getExpressibleJavaType().getJavaTypeClass();
}
@Override
public DomainType<D> getSqmType() {
return this;
}
}

View File

@ -85,7 +85,7 @@ public class EntityTypeImpl<J>
.resolve( StandardBasicTypes.STRING );
}
this.discriminatorPathSource = discriminatorType == null ? null : new DiscriminatorSqmPathSource(
this.discriminatorPathSource = discriminatorType == null ? null : new EntityDiscriminatorSqmPathSource(
discriminatorType,
this,
entityDescriptor

View File

@ -10,6 +10,7 @@ import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -17,6 +18,7 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.hibernate.boot.model.NamedEntityGraphDefinition;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
@ -88,10 +90,9 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
private final MappingMetamodel mappingMetamodel;
private final ServiceRegistry serviceRegistry;
private final Map<String, EntityDomainType<?>> jpaEntityTypeMap = new TreeMap<>(); // Need ordering for deterministic implementers list in SqmPolymorphicRootDescriptor
private final Map<Class<?>, ManagedDomainType<?>> jpaManagedTypeMap = new HashMap<>();
private final Set<ManagedDomainType<?>> jpaManagedTypes = new HashSet<>();
private final Set<EmbeddableDomainType<?>> jpaEmbeddables = new HashSet<>();
private final Map<String, ManagedDomainType<?>> managedTypeByName = new TreeMap<>();
private final Map<Class<?>, ManagedDomainType<?>> managedTypeByClass = new HashMap<>();
private JpaMetaModelPopulationSetting jpaMetaModelPopulationSetting;
private final Map<String, Set<String>> allowedEnumLiteralTexts = new HashMap<>();
private final transient Map<String, RootGraphImplementor<?>> entityGraphMap = new ConcurrentHashMap<>();
@ -129,9 +130,35 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
}
@Override
public <X> EntityDomainType<X> entity(String entityName) {
public <X> ManagedDomainType<X> managedType(String typeName) {
//noinspection unchecked
return entityName==null ? null : (EntityDomainType<X>) jpaEntityTypeMap.get( entityName );
return typeName == null ? null : (ManagedDomainType<X>) managedTypeByName.get( typeName );
}
@Override
public <X> EntityDomainType<X> entity(String entityName) {
if ( entityName == null ) {
return null;
}
final ManagedDomainType<?> managedType = managedTypeByName.get( entityName );
if ( !( managedType instanceof EntityDomainType<?> ) ) {
return null;
}
//noinspection unchecked
return (EntityDomainType<X>) managedType;
}
@Override
public <X> EmbeddableDomainType<X> embeddable(String embeddableName) {
if ( embeddableName == null ) {
return null;
}
final ManagedDomainType<?> managedType = managedTypeByName.get( embeddableName );
if ( !( managedType instanceof EmbeddableDomainType<?> ) ) {
return null;
}
//noinspection unchecked
return (EmbeddableDomainType<X>) managedType;
}
@Override
@ -173,13 +200,13 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
@Override
public <X> ManagedDomainType<X> findManagedType(Class<X> cls) {
//noinspection unchecked
return (ManagedDomainType<X>) jpaManagedTypeMap.get( cls );
return (ManagedDomainType<X>) managedTypeByClass.get( cls );
}
@Override
public <X> EntityDomainType<X> findEntityType(Class<X> cls) {
final ManagedType<?> type = jpaManagedTypeMap.get( cls );
if ( !( type instanceof EntityType<?> ) ) {
final ManagedType<?> type = managedTypeByClass.get( cls );
if ( !( type instanceof EntityDomainType<?> ) ) {
return null;
}
//noinspection unchecked
@ -188,7 +215,7 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
@Override
public <X> ManagedDomainType<X> managedType(Class<X> cls) {
final ManagedType<?> type = jpaManagedTypeMap.get( cls );
final ManagedType<?> type = managedTypeByClass.get( cls );
if ( type == null ) {
// per JPA
throw new IllegalArgumentException( "Not a managed type: " + cls );
@ -200,7 +227,7 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
@Override
public <X> EntityDomainType<X> entity(Class<X> cls) {
final ManagedType<?> type = jpaManagedTypeMap.get( cls );
final ManagedType<?> type = managedTypeByClass.get( cls );
if ( !( type instanceof EntityDomainType<?> ) ) {
throw new IllegalArgumentException( "Not an entity: " + cls.getName() );
}
@ -210,7 +237,7 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
@Override
public <X> EmbeddableDomainType<X> embeddable(Class<X> cls) {
final ManagedType<?> type = jpaManagedTypeMap.get( cls );
final ManagedType<?> type = managedTypeByClass.get( cls );
if ( !( type instanceof EmbeddableDomainType<?> ) ) {
throw new IllegalArgumentException( "Not an embeddable: " + cls.getName() );
}
@ -218,25 +245,39 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
return (EmbeddableDomainType<X>) type;
}
private Collection<ManagedDomainType<?>> getAllManagedTypes() {
switch ( jpaMetaModelPopulationSetting ) {
case IGNORE_UNSUPPORTED:
return managedTypeByClass.values();
case ENABLED:
return managedTypeByName.values();
case DISABLED:
return Collections.emptySet();
default:
// should never happen
throw new AssertionError();
}
}
@Override
public Set<ManagedType<?>> getManagedTypes() {
return new HashSet<>( jpaManagedTypes );
return new HashSet<>( getAllManagedTypes() );
}
@Override
public Set<EntityType<?>> getEntities() {
final Set<EntityType<?>> entityTypes = new HashSet<>( jpaEntityTypeMap.size() );
for ( ManagedDomainType<?> value : jpaManagedTypes ) {
if ( value instanceof EntityType<?> ) {
entityTypes.add( (EntityType<?>) value );
}
}
return entityTypes;
return getAllManagedTypes().stream()
.filter( EntityType.class::isInstance )
.map( t -> (EntityType<?>) t )
.collect( Collectors.toSet() );
}
@Override
public Set<EmbeddableType<?>> getEmbeddables() {
return new HashSet<>( jpaEmbeddables );
return getAllManagedTypes().stream()
.filter( EmbeddableType.class::isInstance )
.map( t -> (EmbeddableType<?>) t )
.collect( Collectors.toSet() );
}
@Override
@ -454,9 +495,9 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
public <T> EntityDomainType<T> resolveEntityReference(Class<T> javaType) {
// try the incoming Java type as a "strict" entity reference
{
final EntityDomainType<?> descriptor = jpaEntityTypeMap.get( javaType.getName() );
if ( descriptor != null ) {
return (EntityDomainType<T>) descriptor;
final ManagedDomainType<?> managedType = managedTypeByClass.get( javaType );
if ( managedType instanceof EntityDomainType<?> ) {
return (EntityDomainType<T>) managedType;
}
}
@ -464,7 +505,7 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
{
final String proxyEntityName = entityProxyInterfaceMap.get( javaType );
if ( proxyEntityName != null ) {
return (EntityDomainType<T>) jpaEntityTypeMap.get( proxyEntityName );
return entity( proxyEntityName );
}
}
@ -478,9 +519,12 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
// create a set of descriptors that should be used to build the polymorphic EntityDomainType
final Set<EntityDomainType<? extends T>> matchingDescriptors = new HashSet<>();
for ( EntityDomainType<?> entityDomainType : jpaEntityTypeMap.values() ) {
for ( ManagedDomainType<?> managedType : managedTypeByName.values() ) {
if ( managedType.getPersistenceType() != Type.PersistenceType.ENTITY ) {
continue;
}
// see if we should add `entityDomainType` as one of the matching-descriptors.
if ( javaType.isAssignableFrom( entityDomainType.getJavaType() ) ) {
if ( javaType.isAssignableFrom( managedType.getJavaType() ) ) {
// the queried type is assignable from the type of the current entity-type
// we should add it to the collecting set of matching descriptors. it should
// be added aside from a few cases...
@ -488,7 +532,7 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
// it should not be added if its direct super (if one) is defined without
// explicit-polymorphism. The super itself will get added and the initializers
// for entity mappings already handle loading subtypes - adding it would be redundant
final ManagedDomainType<?> superType = entityDomainType.getSuperType();
final ManagedDomainType<?> superType = managedType.getSuperType();
if ( superType != null
&& superType.getPersistenceType() == Type.PersistenceType.ENTITY
&& javaType.isAssignableFrom( superType.getJavaType() ) ) {
@ -501,13 +545,13 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
// it should not be added if it is mapped with explicit polymorphism itself
final EntityMappingType entityPersister = getMappingMetamodel()
.getEntityDescriptor( entityDomainType.getHibernateEntityName() );
.getEntityDescriptor( managedType.getTypeName() );
if ( entityPersister.isExplicitPolymorphism() ) {
continue;
}
// aside from these special cases, add it
matchingDescriptors.add( (EntityDomainType<? extends T>) entityDomainType );
matchingDescriptors.add( (EntityDomainType<? extends T>) managedType );
}
}
@ -558,55 +602,27 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
context.wrapUp();
for ( Map.Entry<String, IdentifiableDomainType<?>> entry : context.getIdentifiableTypesByName().entrySet() ) {
if ( entry.getValue() instanceof EntityDomainType<?> ) {
this.jpaEntityTypeMap.put( entry.getKey(), (EntityDomainType<?>) entry.getValue() );
}
}
this.jpaMetaModelPopulationSetting = jpaMetaModelPopulationSetting;
this.jpaManagedTypeMap.putAll( context.getEntityTypeMap() );
this.jpaManagedTypeMap.putAll( context.getMappedSuperclassTypeMap() );
switch ( jpaMetaModelPopulationSetting ) {
case IGNORE_UNSUPPORTED:
this.jpaManagedTypes.addAll( context.getEntityTypeMap().values() );
this.jpaManagedTypes.addAll( context.getMappedSuperclassTypeMap().values() );
break;
case ENABLED:
this.jpaManagedTypes.addAll( context.getIdentifiableTypesByName().values() );
break;
}
// Identifiable types (Entities and MappedSuperclasses)
this.managedTypeByName.putAll( context.getIdentifiableTypesByName() );
this.managedTypeByClass.putAll( context.getEntityTypeMap() );
this.managedTypeByClass.putAll( context.getMappedSuperclassTypeMap() );
// Embeddable types
int mapEmbeddables = 0;
for ( EmbeddableDomainType<?> embeddable : context.getEmbeddableTypeSet() ) {
// Do not register the embeddable types for id classes
if ( embeddable.getExpressibleJavaType() instanceof EntityJavaType<?> ) {
continue;
}
switch ( jpaMetaModelPopulationSetting ) {
case IGNORE_UNSUPPORTED:
if ( embeddable.getJavaType() != null && embeddable.getJavaType() != Map.class ) {
this.jpaEmbeddables.add( embeddable );
this.jpaManagedTypes.add( embeddable );
if ( !( embeddable.getExpressibleJavaType() instanceof EntityJavaType<?> ) ) {
this.jpaManagedTypeMap.put( embeddable.getJavaType(), embeddable );
}
}
break;
case ENABLED:
this.jpaEmbeddables.add( embeddable );
this.jpaManagedTypes.add( embeddable );
if ( embeddable.getJavaType() != null
&& !( embeddable.getExpressibleJavaType() instanceof EntityJavaType<?> ) ) {
this.jpaManagedTypeMap.put( embeddable.getJavaType(), embeddable );
}
break;
case DISABLED:
if ( embeddable.getJavaType() == null ) {
throw new UnsupportedOperationException( "ANY not supported" );
}
if ( !( embeddable.getExpressibleJavaType() instanceof EntityJavaType<?> ) ) {
this.jpaManagedTypeMap.put( embeddable.getJavaType(), embeddable );
}
break;
final Class<?> embeddableClass = embeddable.getJavaType();
if ( embeddableClass != Map.class ) {
this.managedTypeByClass.put( embeddable.getJavaType(), embeddable );
this.managedTypeByName.put( embeddable.getTypeName(), embeddable );
}
else {
this.managedTypeByName.put( "dynamic-embeddable-" + mapEmbeddables++, embeddable );
}
}

View File

@ -489,11 +489,21 @@ public class MappingMetamodelImpl extends QueryParameterBindingTypeResolverImpl
return jpaMetamodel.getEmbeddables();
}
@Override
public <X> ManagedDomainType<X> managedType(String typeName) {
return jpaMetamodel.managedType( typeName );
}
@Override
public <X> EntityDomainType<X> entity(String entityName) {
return jpaMetamodel.entity( entityName );
}
@Override
public <X> EmbeddableDomainType<X> embeddable(String embeddableName) {
return jpaMetamodel.embeddable( embeddableName );
}
@Override
public <X> EntityDomainType<X> getHqlEntityReference(String entityName) {
return jpaMetamodel.getHqlEntityReference( entityName );

View File

@ -97,7 +97,7 @@ public class DiscriminatorHelper {
);
}
private static <T> String jdbcLiteral(
public static <T> String jdbcLiteral(
T value,
JdbcLiteralFormatter<T> formatter,
Dialect dialect) {

View File

@ -9,7 +9,9 @@ package org.hibernate.query.hql.internal;
import java.lang.reflect.Field;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor;
import org.hibernate.query.SemanticException;
import org.hibernate.query.hql.HqlLogging;
@ -24,6 +26,7 @@ import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.expression.SqmEnumLiteral;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.expression.SqmFieldLiteral;
import org.hibernate.query.sqm.tree.expression.SqmLiteralEmbeddableType;
import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType;
import org.hibernate.query.sqm.tree.from.SqmFrom;
import org.hibernate.type.descriptor.java.EnumJavaType;
@ -93,9 +96,16 @@ public class BasicDotIdentifierConsumer implements DotIdentifierConsumer {
}
@Override
public void consumeTreat(String entityName, boolean isTerminal) {
final SqmPath<?> path = (SqmPath<?>) currentPart;
currentPart = path.treatAs( creationState.getCreationContext().getJpaMetamodel().entity( entityName ) );
public void consumeTreat(String importableName, boolean isTerminal) {
final SqmPath<?> sqmPath = (SqmPath<?>) currentPart;
currentPart = sqmPath.treatAs( treatTarget( importableName ) );
}
private <T> Class<T> treatTarget(String typeName) {
final ManagedDomainType<T> managedType = creationState.getCreationContext()
.getJpaMetamodel()
.managedType( typeName );
return managedType.getJavaType();
}
protected void reset() {
@ -181,9 +191,12 @@ public class BasicDotIdentifierConsumer implements DotIdentifierConsumer {
final String importableName = jpaMetamodel.qualifyImportableName( path );
final NodeBuilder nodeBuilder = creationContext.getNodeBuilder();
if ( importableName != null ) {
final EntityDomainType<?> entityDomainType = jpaMetamodel.entity( importableName );
if ( entityDomainType != null ) {
return new SqmLiteralEntityType<>( entityDomainType, nodeBuilder );
final ManagedDomainType<?> managedType = jpaMetamodel.managedType( importableName );
if ( managedType instanceof EntityDomainType<?> ) {
return new SqmLiteralEntityType<>( ( (EntityDomainType<?>) managedType ), nodeBuilder );
}
else if ( managedType instanceof EmbeddableDomainType<?> ) {
return new SqmLiteralEmbeddableType<>( ( (EmbeddableDomainType<?>) managedType ), nodeBuilder );
}
}

View File

@ -7,6 +7,7 @@
package org.hibernate.query.hql.internal;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.query.PathException;
import org.hibernate.query.SemanticException;
import org.hibernate.query.hql.spi.DotIdentifierConsumer;
@ -239,7 +240,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
private interface ConsumerDelegate {
void consumeIdentifier(String identifier, boolean isTerminal, boolean allowReuse);
void consumeTreat(String entityName, boolean isTerminal);
void consumeTreat(String typeName, boolean isTerminal);
SemanticPathPart getConsumedPart();
}
@ -280,20 +281,23 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
}
@Override
public void consumeTreat(String entityName, boolean isTerminal) {
public void consumeTreat(String typeName, boolean isTerminal) {
if ( isTerminal ) {
currentPath = fetch
? ( (SqmAttributeJoin<?, ?>) currentPath ).treatAs( treatTarget( entityName ), alias, true )
: currentPath.treatAs( treatTarget( entityName ), alias );
? ( (SqmAttributeJoin<?, ?>) currentPath ).treatAs( treatTarget( typeName ), alias, true )
: currentPath.treatAs( treatTarget( typeName ), alias );
}
else {
currentPath = currentPath.treatAs( treatTarget( entityName ) );
currentPath = currentPath.treatAs( treatTarget( typeName ) );
}
creationState.getCurrentProcessingState().getPathRegistry().register( currentPath );
}
private <T> EntityDomainType<T> treatTarget(String entityName) {
return creationState.getCreationContext().getJpaMetamodel().entity(entityName);
private <T> Class<T> treatTarget(String typeName) {
final ManagedDomainType<T> managedType = creationState.getCreationContext()
.getJpaMetamodel()
.managedType( typeName );
return managedType.getJavaType();
}
@Override
@ -364,7 +368,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
}
@Override
public void consumeTreat(String entityName, boolean isTerminal) {
public void consumeTreat(String typeName, boolean isTerminal) {
throw new UnsupportedOperationException();
}

View File

@ -5481,14 +5481,13 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
consumeManagedTypeReference( ctx.path() );
final String treatTargetName = ctx.simplePath().getText();
final String treatTargetEntityName =
getCreationContext().getJpaMetamodel().qualifyImportableName( treatTargetName );
if ( treatTargetEntityName == null ) {
final String importableName = getCreationContext().getJpaMetamodel().qualifyImportableName( treatTargetName );
if ( importableName == null ) {
throw new SemanticException( "Could not resolve treat target type '" + treatTargetName + "'", query );
}
final boolean hasContinuation = ctx.getChildCount() == 7;
consumer.consumeTreat( treatTargetEntityName, !hasContinuation );
consumer.consumeTreat( importableName, !hasContinuation );
SqmPath<?> result = (SqmPath<?>) consumer.getConsumedPart();
if ( hasContinuation ) {

View File

@ -8,8 +8,8 @@ package org.hibernate.query.sqm;
import java.util.List;
import org.hibernate.metamodel.model.domain.DiscriminatorSqmPath;
import org.hibernate.metamodel.model.domain.internal.AnyDiscriminatorSqmPath;
import org.hibernate.metamodel.model.domain.internal.EntityDiscriminatorSqmPath;
import org.hibernate.query.sqm.tree.cte.SqmCteContainer;
import org.hibernate.query.sqm.tree.cte.SqmCteStatement;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
@ -67,6 +67,7 @@ import org.hibernate.query.sqm.tree.expression.SqmFormat;
import org.hibernate.query.sqm.tree.expression.SqmFunction;
import org.hibernate.query.sqm.tree.expression.SqmHqlNumericLiteral;
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.query.sqm.tree.expression.SqmLiteralEmbeddableType;
import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType;
import org.hibernate.query.sqm.tree.expression.SqmModifiedSubQueryExpression;
import org.hibernate.query.sqm.tree.expression.SqmNamedParameter;
@ -238,7 +239,7 @@ public interface SemanticQueryWalker<T> {
T visitFkExpression(SqmFkExpression<?> fkExpression);
T visitDiscriminatorPath(EntityDiscriminatorSqmPath sqmPath);
T visitDiscriminatorPath(DiscriminatorSqmPath<?> sqmPath);
T visitIndexedPluralAccessPath(SqmIndexedCollectionAccessPath<?> path);
@ -320,6 +321,8 @@ public interface SemanticQueryWalker<T> {
T visitEntityTypeLiteralExpression(SqmLiteralEntityType<?> expression);
T visitEmbeddableTypeLiteralExpression(SqmLiteralEmbeddableType<?> expression);
T visitAnyDiscriminatorTypeExpression(AnyDiscriminatorSqmPath<?> expression);
T visitAnyDiscriminatorTypeValueExpression(SqmAnyDiscriminatorValue<?> expression);

View File

@ -21,6 +21,7 @@ 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.ManagedDomainType;
import org.hibernate.metamodel.model.domain.MappedSuperclassDomainType;
import org.hibernate.metamodel.model.domain.internal.AnyMappingSqmPathSource;
import org.hibernate.metamodel.model.domain.internal.BasicSqmPathSource;
@ -35,7 +36,6 @@ import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.cte.SqmCteTable;
import org.hibernate.query.sqm.tree.domain.AbstractSqmSpecificPluralPartPath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.tree.from.TableGroup;
@ -43,6 +43,7 @@ import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaType;
import jakarta.persistence.metamodel.Bindable;
import jakarta.persistence.metamodel.Type;
/**
* Helper for dealing with Hibernate's "mapping model" while processing an SQM which is defined
@ -180,17 +181,21 @@ public class SqmMappingModelHelper {
if ( sqmPath instanceof SqmTreatedPath<?, ?> ) {
final SqmTreatedPath<?, ?> treatedPath = (SqmTreatedPath<?, ?>) sqmPath;
final EntityDomainType<?> treatTargetType = treatedPath.getTreatTarget();
return domainModel.findEntityDescriptor( treatTargetType.getHibernateEntityName() );
final ManagedDomainType<?> treatTarget = treatedPath.getTreatTarget();
if ( treatTarget.getPersistenceType() == Type.PersistenceType.ENTITY ) {
final EntityDomainType<?> treatTargetType = (EntityDomainType<?>) treatTarget;
return domainModel.findEntityDescriptor( treatTargetType.getHibernateEntityName() );
}
}
// see if the LHS is treated
if ( sqmPath.getLhs() instanceof SqmTreatedPath<?, ?> ) {
final SqmTreatedPath<?, ?> treatedPath = (SqmTreatedPath<?, ?>) sqmPath.getLhs();
final EntityDomainType<?> treatTargetType = treatedPath.getTreatTarget();
final EntityPersister container = domainModel.findEntityDescriptor( treatTargetType.getHibernateEntityName() );
return container.findSubPart( sqmPath.getNavigablePath().getLocalName(), container );
final ManagedDomainType<?> treatTarget = treatedPath.getTreatTarget();
if ( treatTarget.getPersistenceType() == Type.PersistenceType.ENTITY ) {
final EntityPersister container = domainModel.findEntityDescriptor( treatTarget.getTypeName() );
return container.findSubPart( sqmPath.getNavigablePath().getLocalName(), container );
}
}
// Plural path parts are not joined and thus also have no table group
@ -253,9 +258,15 @@ public class SqmMappingModelHelper {
SqmPath<?> sqmPath,
SqmToSqlAstConverter converter) {
final SqmPath<?> parentPath = sqmPath.getLhs();
if ( parentPath instanceof SqmTreatedPath ) {
if ( parentPath instanceof SqmTreatedPath<?, ?> ) {
final SqmTreatedPath<?, ?> treatedPath = (SqmTreatedPath<?, ?>) parentPath;
return resolveEntityPersister( treatedPath.getTreatTarget(), converter.getCreationContext().getSessionFactory() );
final ManagedDomainType<?> treatTarget = treatedPath.getTreatTarget();
if ( treatTarget.getPersistenceType() == Type.PersistenceType.ENTITY ) {
return resolveEntityPersister(
( (EntityDomainType<?>) treatTarget ),
converter.getCreationContext().getSessionFactory()
);
}
}
return null;

View File

@ -9,8 +9,8 @@ package org.hibernate.query.sqm.internal;
import java.util.List;
import java.util.Locale;
import org.hibernate.metamodel.model.domain.DiscriminatorSqmPath;
import org.hibernate.metamodel.model.domain.internal.AnyDiscriminatorSqmPath;
import org.hibernate.metamodel.model.domain.internal.EntityDiscriminatorSqmPath;
import org.hibernate.query.QueryLogging;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.tree.SqmStatement;
@ -56,6 +56,7 @@ import org.hibernate.query.sqm.tree.expression.SqmFormat;
import org.hibernate.query.sqm.tree.expression.SqmFunction;
import org.hibernate.query.sqm.tree.expression.SqmHqlNumericLiteral;
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.query.sqm.tree.expression.SqmLiteralEmbeddableType;
import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType;
import org.hibernate.query.sqm.tree.expression.SqmModifiedSubQueryExpression;
import org.hibernate.query.sqm.tree.expression.SqmNamedParameter;
@ -723,7 +724,7 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
}
@Override
public Object visitDiscriminatorPath(EntityDiscriminatorSqmPath sqmPath) {
public Object visitDiscriminatorPath(DiscriminatorSqmPath<?> sqmPath) {
logWithIndentation( "-> [discriminator-path] - `%s`", sqmPath.getNavigablePath() );
return null;
@ -807,6 +808,11 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
return null;
}
@Override
public Object visitEmbeddableTypeLiteralExpression(SqmLiteralEmbeddableType<?> expression) {
return null;
}
@Override
public Object visitParameterizedEntityTypeExpression(SqmParameterizedEntityType expression) {
return null;

View File

@ -7,13 +7,14 @@
package org.hibernate.query.sqm.internal;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.metamodel.EmbeddableType;
import jakarta.persistence.metamodel.EntityType;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.TupleType;
import org.hibernate.metamodel.model.domain.internal.DiscriminatorSqmPathSource;
import org.hibernate.metamodel.model.domain.internal.EntityDiscriminatorSqmPathSource;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.SemanticException;
import org.hibernate.query.sqm.BinaryArithmeticOperator;
@ -128,8 +129,8 @@ public class TypecheckUtil {
// for embeddables, the embeddable class must match exactly
final EmbeddableType<?> lhsEmbeddable = getEmbeddableType( lhsDomainType );
final EmbeddableType<?> rhsEmbeddable = getEmbeddableType( rhsDomainType );
final EmbeddableDomainType<?> lhsEmbeddable = getEmbeddableType( lhsDomainType );
final EmbeddableDomainType<?> rhsEmbeddable = getEmbeddableType( rhsDomainType );
if ( lhsEmbeddable != null && rhsEmbeddable != null ) {
return areEmbeddableTypesComparable( lhsEmbeddable, rhsEmbeddable );
}
@ -157,11 +158,11 @@ public class TypecheckUtil {
// entities can be compared to discriminators if they belong to
// the same inheritance hierarchy
if ( lhsDomainType instanceof DiscriminatorSqmPathSource ) {
return isDiscriminatorTypeComparable( (DiscriminatorSqmPathSource<?>) lhsDomainType, rhsDomainType, factory );
if ( lhsDomainType instanceof EntityDiscriminatorSqmPathSource ) {
return isDiscriminatorTypeComparable( (EntityDiscriminatorSqmPathSource<?>) lhsDomainType, rhsDomainType, factory );
}
if ( rhsDomainType instanceof DiscriminatorSqmPathSource ) {
return isDiscriminatorTypeComparable( (DiscriminatorSqmPathSource<?>) rhsDomainType, lhsDomainType, factory );
if ( rhsDomainType instanceof EntityDiscriminatorSqmPathSource ) {
return isDiscriminatorTypeComparable( (EntityDiscriminatorSqmPathSource<?>) rhsDomainType, lhsDomainType, factory );
}
// Treat the expressions as comparable if they belong to the same
@ -197,15 +198,26 @@ public class TypecheckUtil {
return false;
}
private static EmbeddableType<?> getEmbeddableType(SqmExpressible<?> expressible) {
return expressible instanceof EmbeddableType<?> ? (EmbeddableType<?>) expressible : null;
private static EmbeddableDomainType<?> getEmbeddableType(SqmExpressible<?> expressible) {
return expressible instanceof EmbeddableDomainType<?> ? (EmbeddableDomainType<?>) expressible : null;
}
private static boolean areEmbeddableTypesComparable(
EmbeddableType<?> lhsType,
EmbeddableType<?> rhsType) {
// no polymorphism for embeddable types
return rhsType.getJavaType() == lhsType.getJavaType();
EmbeddableDomainType<?> lhsType,
EmbeddableDomainType<?> rhsType) {
if ( rhsType.getJavaType() == lhsType.getJavaType() ) {
return true;
}
return lhsType.isPolymorphic() && getRootEmbeddableType( lhsType ) == getRootEmbeddableType( rhsType );
}
private static ManagedDomainType<?> getRootEmbeddableType(EmbeddableDomainType<?> embeddableType) {
ManagedDomainType<?> rootType = embeddableType;
while ( rootType.getSuperType() != null ) {
rootType = rootType.getSuperType();
}
return rootType;
}
private static boolean areTupleTypesComparable(
@ -234,7 +246,7 @@ public class TypecheckUtil {
}
private static boolean isDiscriminatorTypeComparable(
DiscriminatorSqmPathSource<?> lhsDiscriminator, SqmExpressible<?> rhsType,
EntityDiscriminatorSqmPathSource<?> lhsDiscriminator, SqmExpressible<?> rhsType,
SessionFactoryImplementor factory) {
String entityName = lhsDiscriminator.getEntityDomainType().getHibernateEntityName();
EntityPersister lhsEntity = factory.getMappingMetamodel().getEntityDescriptor( entityName );
@ -243,8 +255,8 @@ public class TypecheckUtil {
EntityPersister rhsEntity = getEntityDescriptor( factory, rhsEntityName );
return lhsEntity.getRootEntityName().equals( rhsEntity.getRootEntityName() );
}
else if ( rhsType instanceof DiscriminatorSqmPathSource ) {
DiscriminatorSqmPathSource<?> discriminator = (DiscriminatorSqmPathSource<?>) rhsType;
else if ( rhsType instanceof EntityDiscriminatorSqmPathSource ) {
EntityDiscriminatorSqmPathSource<?> discriminator = (EntityDiscriminatorSqmPathSource<?>) rhsType;
String rhsEntityName = discriminator.getEntityDomainType().getHibernateEntityName();
EntityPersister rhsEntity = factory.getMappingMetamodel().getEntityDescriptor( rhsEntityName );
return rhsEntity.getRootEntityName().equals( lhsEntity.getRootEntityName() );

View File

@ -8,8 +8,8 @@ package org.hibernate.query.sqm.spi;
import java.util.List;
import org.hibernate.metamodel.model.domain.DiscriminatorSqmPath;
import org.hibernate.metamodel.model.domain.internal.AnyDiscriminatorSqmPath;
import org.hibernate.metamodel.model.domain.internal.EntityDiscriminatorSqmPath;
import org.hibernate.query.sqm.InterpretationException;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.tree.SqmVisitableNode;
@ -57,6 +57,7 @@ import org.hibernate.query.sqm.tree.expression.SqmFormat;
import org.hibernate.query.sqm.tree.expression.SqmFunction;
import org.hibernate.query.sqm.tree.expression.SqmHqlNumericLiteral;
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.query.sqm.tree.expression.SqmLiteralEmbeddableType;
import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType;
import org.hibernate.query.sqm.tree.expression.SqmModifiedSubQueryExpression;
import org.hibernate.query.sqm.tree.expression.SqmNamedParameter;
@ -468,7 +469,7 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker<Obj
}
@Override
public Object visitDiscriminatorPath(EntityDiscriminatorSqmPath path) {
public Object visitDiscriminatorPath(DiscriminatorSqmPath<?> path) {
return path;
}
@ -719,6 +720,11 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker<Obj
return expression;
}
@Override
public Object visitEmbeddableTypeLiteralExpression(SqmLiteralEmbeddableType<?> expression) {
return expression;
}
@Override
public Object visitAnyDiscriminatorTypeExpression(AnyDiscriminatorSqmPath<?> expression) {
return expression;

View File

@ -74,8 +74,10 @@ import org.hibernate.metamodel.mapping.internal.SqlTypedMappingImpl;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.metamodel.mapping.ordering.OrderByFragment;
import org.hibernate.metamodel.model.domain.BasicDomainType;
import org.hibernate.metamodel.model.domain.DiscriminatorSqmPath;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.PersistentAttribute;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.metamodel.model.domain.internal.AnyDiscriminatorSqmPath;
@ -198,6 +200,7 @@ import org.hibernate.query.sqm.tree.expression.SqmFunction;
import org.hibernate.query.sqm.tree.expression.SqmHqlNumericLiteral;
import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper;
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.query.sqm.tree.expression.SqmLiteralEmbeddableType;
import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType;
import org.hibernate.query.sqm.tree.expression.SqmLiteralNull;
import org.hibernate.query.sqm.tree.expression.SqmModifiedSubQueryExpression;
@ -301,6 +304,7 @@ import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Distinct;
import org.hibernate.sql.ast.tree.expression.Duration;
import org.hibernate.sql.ast.tree.expression.DurationUnit;
import org.hibernate.sql.ast.tree.expression.EmbeddableTypeLiteral;
import org.hibernate.sql.ast.tree.expression.EntityTypeLiteral;
import org.hibernate.sql.ast.tree.expression.Every;
import org.hibernate.sql.ast.tree.expression.Expression;
@ -424,6 +428,7 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import static jakarta.persistence.metamodel.Type.PersistenceType.ENTITY;
import static java.util.Collections.singletonList;
import static org.hibernate.boot.model.internal.SoftDeleteHelper.createNonSoftDeletedRestriction;
import static org.hibernate.generator.EventType.INSERT;
@ -2475,14 +2480,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
private Predicate visitWhereClause(SqmPredicate sqmPredicate) {
if ( sqmPredicate == null ) {
return null;
}
currentClauseStack.push( Clause.WHERE );
inferrableTypeAccessStack.push( () -> null );
try {
return combinePredicates(
(Predicate) sqmPredicate.accept( this ),
sqmPredicate != null ? (Predicate) sqmPredicate.accept( this ) : null,
consumeConjunctTreatTypeRestrictions()
);
}
@ -2494,14 +2496,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
@Override
public Predicate visitHavingClause(SqmPredicate sqmPredicate) {
if ( sqmPredicate == null ) {
return null;
}
currentClauseStack.push( Clause.HAVING );
inferrableTypeAccessStack.push( () -> null );
try {
return combinePredicates(
(Predicate) sqmPredicate.accept( this ),
sqmPredicate != null ? (Predicate) sqmPredicate.accept( this ) : null,
consumeConjunctTreatTypeRestrictions()
);
}
@ -2911,10 +2910,10 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
* If the path is a treat, registers {@link EntityNameUse#TREAT} for all treated subtypes instead.
*/
private void registerEntityNameProjectionUsage(SqmPath<?> projectedPath, TableGroup tableGroup) {
final EntityDomainType<?> treatedType;
final ManagedDomainType<?> treatedType;
if ( projectedPath instanceof SqmTreatedPath<?, ?> ) {
treatedType = ( (SqmTreatedPath<?, ?>) projectedPath ).getTreatTarget();
registerEntityNameUsage( tableGroup, EntityNameUse.TREAT, treatedType.getHibernateEntityName(), true );
registerEntityNameUsage( tableGroup, EntityNameUse.TREAT, treatedType.getTypeName(), true );
if ( projectedPath instanceof SqmFrom<?, ?> ) {
// Register that the TREAT uses for the SqmFrom node may not be downgraded
@ -2926,7 +2925,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
else if ( projectedPath.getNodeType().getSqmPathType() instanceof EntityDomainType<?> ) {
treatedType = (EntityDomainType<?>) projectedPath.getNodeType().getSqmPathType();
registerEntityNameUsage( tableGroup, EntityNameUse.PROJECTION, treatedType.getHibernateEntityName(), true );
registerEntityNameUsage( tableGroup, EntityNameUse.PROJECTION, treatedType.getTypeName(), true );
if ( projectedPath instanceof SqmFrom<?, ?> ) {
// Register that the TREAT uses for the SqmFrom node may not be downgraded
@ -2970,31 +2969,33 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final EntityMappingType parentType;
if ( parentPath instanceof SqmTreatedPath<?, ?> ) {
// A treated attribute usage i.e. `treat(alias as Subtype).attribute = 1`
final ManagedDomainType<?> treatTarget = ( (SqmTreatedPath<?, ?>) parentPath ).getTreatTarget();
if ( treatTarget.getPersistenceType() == ENTITY ) {
parentType = creationContext.getMappingMetamodel().getEntityDescriptor( treatTarget.getTypeName() );
final EntityDomainType<?> treatTarget = ( (SqmTreatedPath<?, ?>) parentPath ).getTreatTarget();
// The following is an optimization to avoid rendering treat conditions into predicates.
// Imagine an HQL predicate like `treat(alias as Subtype).attribute is null or alias.name = '...'`.
// If the `attribute` is basic, we will render a case wrapper around the column expression
// and hence we can safely skip adding the `type(alias) = Subtype and ...` condition to the SQL.
parentType = creationContext.getMappingMetamodel()
.getEntityDescriptor( treatTarget.getHibernateEntityName() );
// The following is an optimization to avoid rendering treat conditions into predicates.
// Imagine an HQL predicate like `treat(alias as Subtype).attribute is null or alias.name = '...'`.
// If the `attribute` is basic, we will render a case wrapper around the column expression
// and hence we can safely skip adding the `type(alias) = Subtype and ...` condition to the SQL.
final ModelPart subPart = parentType.findSubPart( attributeName );
final EntityNameUse entityNameUse;
// We only apply this optimization for basic valued model parts for now
if ( subPart.asBasicValuedModelPart() != null ) {
entityNameUse = EntityNameUse.OPTIONAL_TREAT;
final ModelPart subPart = parentType.findSubPart( attributeName );
final EntityNameUse entityNameUse;
// We only apply this optimization for basic valued model parts for now
if ( subPart.asBasicValuedModelPart() != null ) {
entityNameUse = EntityNameUse.OPTIONAL_TREAT;
}
else {
entityNameUse = EntityNameUse.BASE_TREAT;
}
registerEntityNameUsage(
tableGroup,
entityNameUse,
treatTarget.getTypeName()
);
}
else {
entityNameUse = EntityNameUse.BASE_TREAT;
parentType = entityType;
}
registerEntityNameUsage(
tableGroup,
entityNameUse,
treatTarget.getHibernateEntityName()
);
}
else {
// A simple attribute usage e.g. `alias.attribute = 1`
@ -3047,11 +3048,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
public void registerEntityNameUsage(
TableGroup tableGroup,
EntityNameUse entityNameUse,
String hibernateEntityName) {
String treatTargetTypeName) {
registerEntityNameUsage(
tableGroup,
entityNameUse,
hibernateEntityName,
treatTargetTypeName,
entityNameUse.getKind() == EntityNameUse.UseKind.PROJECTION
);
}
@ -3059,14 +3060,27 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
private void registerEntityNameUsage(
TableGroup tableGroup,
EntityNameUse entityNameUse,
String hibernateEntityName,
String treatTargetTypeName,
boolean projection) {
final AbstractEntityPersister persister = (AbstractEntityPersister) creationContext.getSessionFactory()
.getRuntimeMetamodels()
.getMappingMetamodel()
.findEntityDescriptor( hibernateEntityName );
if ( persister == null || !persister.isPolymorphic() ) {
return;
final AbstractEntityPersister persister;
if ( tableGroup.getModelPart() instanceof EmbeddableValuedModelPart ) {
persister = null;
final EmbeddableDomainType<?> embeddableDomainType = creationContext.getSessionFactory()
.getRuntimeMetamodels()
.getJpaMetamodel()
.embeddable( treatTargetTypeName );
if ( embeddableDomainType == null || !embeddableDomainType.isPolymorphic() ) {
return;
}
}
else {
persister = (AbstractEntityPersister) creationContext.getSessionFactory()
.getRuntimeMetamodels()
.getMappingMetamodel()
.findEntityDescriptor( treatTargetTypeName );
if ( persister == null || !persister.isPolymorphic() ) {
return;
}
}
final TableGroup actualTableGroup;
final EntityNameUse finalEntityNameUse;
@ -3094,10 +3108,15 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
tg -> new HashMap<>( 1 )
);
entityNameUses.compute(
hibernateEntityName,
treatTargetTypeName,
(s, existingUse) -> finalEntityNameUse.stronger( existingUse )
);
if ( persister == null ) {
// No need to do anything else for embeddables
return;
}
// Resolve the table reference for all types which we register an entity name use for.
// Also, force table group initialization for treats when needed to ensure correct cardinality
final EntityNameUse.UseKind useKind = finalEntityNameUse.getKind();
@ -3156,7 +3175,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return false;
}
protected void registerTypeUsage(EntityDiscriminatorSqmPath path) {
protected void registerTypeUsage(DiscriminatorSqmPath<?> path) {
registerTypeUsage( getFromClauseAccess().getTableGroup( path.getNavigablePath().getParent() ) );
}
@ -3172,20 +3191,23 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
// but for `a = 1 and type(..) = A or type(..) = B` we can infer `A, B`
// The OR junction allows to create a union of entity name lists of all sub-predicates
// The AND junction allows to create an intersection of entity name lists of all sub-predicates
final EntityMappingType mappingType = (EntityMappingType) tableGroup.getModelPart().getPartMappingType();
final AbstractEntityPersister persister = (AbstractEntityPersister) mappingType.getEntityPersister();
// Avoid resolving subclass tables for persisters with physical discriminators as we won't need them
if ( persister.getDiscriminatorMapping().hasPhysicalColumn() ) {
return;
}
if ( getCurrentClauseStack().getCurrent() != Clause.WHERE && getCurrentClauseStack().getCurrent() != Clause.HAVING ) {
// Where and having clauses are handled specially with EntityNameUse.FILTER and pruning
registerEntityNameUsage( tableGroup, EntityNameUse.PROJECTION, persister.getEntityName(), true );
}
else {
final int subclassTableSpan = persister.getSubclassTableSpan();
for ( int i = 0; i < subclassTableSpan; i++ ) {
tableGroup.resolveTableReference( null, persister.getSubclassTableName( i ) );
final MappingType partMappingType = tableGroup.getModelPart().getPartMappingType();
if ( partMappingType instanceof EntityMappingType ) {
final EntityMappingType mappingType = (EntityMappingType) partMappingType;
final AbstractEntityPersister persister = (AbstractEntityPersister) mappingType.getEntityPersister();
// Avoid resolving subclass tables for persisters with physical discriminators as we won't need them
if ( persister.getDiscriminatorMapping().hasPhysicalColumn() ) {
return;
}
if ( getCurrentClauseStack().getCurrent() != Clause.WHERE && getCurrentClauseStack().getCurrent() != Clause.HAVING ) {
// Where and having clauses are handled specially with EntityNameUse.FILTER and pruning
registerEntityNameUsage( tableGroup, EntityNameUse.PROJECTION, persister.getEntityName(), true );
}
else {
final int subclassTableSpan = persister.getSubclassTableSpan();
for ( int i = 0; i < subclassTableSpan; i++ ) {
tableGroup.resolveTableReference( null, persister.getSubclassTableName( i ) );
}
}
}
}
@ -3196,16 +3218,19 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
if ( tableGroup.isInitialized() ) {
final Map<String, EntityNameUse> entityNameUses = entry.getValue();
final ModelPartContainer modelPart = tableGroup.getModelPart();
final EntityPersister tableGroupPersister;
final MappingType partMappingType;
if ( modelPart instanceof PluralAttributeMapping ) {
tableGroupPersister = (EntityPersister) ( (PluralAttributeMapping) modelPart )
partMappingType = ( (PluralAttributeMapping) modelPart )
.getElementDescriptor()
.getPartMappingType();
}
else {
tableGroupPersister = (EntityPersister) modelPart.getPartMappingType();
partMappingType = modelPart.getPartMappingType();
}
if ( partMappingType instanceof EntityPersister ) {
( (EntityPersister) partMappingType ).pruneForSubclasses( tableGroup, entityNameUses );
}
tableGroupPersister.pruneForSubclasses( tableGroup, entityNameUses );
}
}
}
@ -3235,7 +3260,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
for ( SqmFrom<?, ?> sqmTreat : sqmTreats ) {
final TableGroup actualTableGroup = getActualTableGroup( lhsTableGroup, sqmTreat );
// We don't know the context yet in which a treat is used, so we have to register base treats and track the usage
registerEntityNameUsage( actualTableGroup, EntityNameUse.BASE_TREAT, ( (SqmTreatedPath<?, ?>) sqmTreat ).getTreatTarget().getHibernateEntityName() );
registerEntityNameUsage( actualTableGroup, EntityNameUse.BASE_TREAT, ( (SqmTreatedPath<?, ?>) sqmTreat ).getTreatTarget().getTypeName() );
consumeExplicitJoins( sqmTreat, actualTableGroup );
}
}
@ -3408,12 +3433,15 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
// Since joins on treated paths will never cause table pruning, we need to add a join condition for the treat
if ( sqmJoin.getLhs() instanceof SqmTreatedPath<?, ?> ) {
final SqmTreatedPath<?, ?> treatedPath = (SqmTreatedPath<?, ?>) sqmJoin.getLhs();
joinForPredicate.applyPredicate(
createTreatTypeRestriction(
treatedPath.getWrappedPath(),
treatedPath.getTreatTarget()
)
);
final ManagedDomainType<?> treatTarget = treatedPath.getTreatTarget();
if ( treatTarget.getPersistenceType() == ENTITY ) {
joinForPredicate.applyPredicate(
createTreatTypeRestriction(
treatedPath.getWrappedPath(),
(EntityDomainType<?>) treatTarget
)
);
}
}
if ( transitive ) {
@ -3681,11 +3709,14 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
Consumer<TableGroup> implicitJoinChecker) {
final SqmPath<?> sqmPath = (SqmPath<?>) path;
final SqmPath<?> parentPath;
final boolean treated;
if ( sqmPath instanceof SqmTreatedPath<?, ?> ) {
parentPath = ( (SqmTreatedPath<?, ?>) sqmPath ).getWrappedPath();
treated = true;
}
else {
parentPath = sqmPath.getLhs();
treated = false;
}
if ( parentPath == null ) {
if ( sqmPath instanceof SqmFunctionPath<?> ) {
@ -3730,10 +3761,13 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
if ( newTableGroup != null ) {
implicitJoinChecker.accept( newTableGroup );
registerPathAttributeEntityNameUsage( sqmPath, newTableGroup );
if ( treated ) {
fromClauseIndex.register( sqmPath, newTableGroup );
}
}
return newTableGroup;
}
else if ( sqmPath instanceof SqmTreatedPath<?, ?> ) {
else if ( treated ) {
fromClauseIndex.register( sqmPath, parentTableGroup );
}
@ -3827,8 +3861,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final FromClauseIndex fromClauseIndex = getFromClauseIndex();
final ModelPart subPart = parentTableGroup.getModelPart().findSubPart(
joinedPath.getReferencedPathSource().getPathName(),
lhsPath instanceof SqmTreatedPath
? resolveEntityPersister( ( (SqmTreatedPath<?, ?>) lhsPath ).getTreatTarget() )
lhsPath instanceof SqmTreatedPath<?, ?> && ( (SqmTreatedPath<?, ?>) lhsPath ).getTreatTarget().getPersistenceType() == ENTITY
? resolveEntityPersister( (EntityDomainType<?>) ( (SqmTreatedPath<?, ?>) lhsPath ).getTreatTarget() )
: null
);
@ -4059,7 +4093,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
throw new InterpretationException( "SqmEntityJoin not yet resolved to TableGroup" );
}
private Expression visitTableGroup(TableGroup tableGroup, SqmFrom<?, ?> path) {
private Expression visitTableGroup(TableGroup tableGroup, SqmPath<?> path) {
final ModelPartContainer tableGroupModelPart = tableGroup.getModelPart();
final ModelPart actualModelPart;
@ -4213,11 +4247,12 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
final EntityMappingType treatedMapping;
if ( path instanceof SqmTreatedPath ) {
if ( path instanceof SqmTreatedPath<?, ?> && ( (SqmTreatedPath<?, ?>) path ).getTreatTarget().getPersistenceType() == ENTITY ) {
final ManagedDomainType<?> treatTarget = ( (SqmTreatedPath<?, ?>) path ).getTreatTarget();
treatedMapping = creationContext.getSessionFactory()
.getRuntimeMetamodels()
.getMappingMetamodel()
.findEntityDescriptor( ( (SqmTreatedPath<?,?>) path ).getTreatTarget().getHibernateEntityName() );
.findEntityDescriptor( treatTarget.getTypeName() );
}
else {
treatedMapping = interpretationModelPart.getEntityMappingType();
@ -4501,7 +4536,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
@Override
public Object visitDiscriminatorPath(EntityDiscriminatorSqmPath sqmPath) {
public Object visitDiscriminatorPath(DiscriminatorSqmPath<?> sqmPath) {
return prepareReusablePath(
sqmPath,
() -> {
@ -4574,7 +4609,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final TableGroup resolved = getFromClauseAccess().findTableGroup( sqmTreatedPath.getNavigablePath() );
if ( resolved != null ) {
log.tracef( "SqmTreatedPath [%s] resolved to existing TableGroup [%s]", sqmTreatedPath, resolved );
return visitTableGroup( resolved, (SqmFrom<?, ?>) sqmTreatedPath );
return visitTableGroup( resolved, sqmTreatedPath );
}
throw new InterpretationException( "SqmTreatedPath not yet resolved to TableGroup" );
@ -5152,13 +5187,20 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
if ( lhs instanceof SqmTreatedPath<?, ?> ) {
final SqmTreatedPath<?, ?> treatedPath = (SqmTreatedPath<?, ?>) lhs;
final Class<?> treatTargetJavaType = treatedPath.getTreatTarget().getJavaType();
final ManagedDomainType<?> treatTarget = treatedPath.getTreatTarget();
final Class<?> treatTargetJavaType = treatTarget.getJavaType();
final SqmPath<?> wrappedPath = treatedPath.getWrappedPath();
final Class<?> originalJavaType = wrappedPath.getJavaType();
if ( treatTargetJavaType.isAssignableFrom( originalJavaType ) ) {
// Treating a node to a super type can be ignored
return expression;
}
if ( treatTarget instanceof EmbeddableDomainType<?> ) {
// For embedded treats we simply register a TREAT use
final TableGroup tableGroup = getFromClauseIndex().findTableGroup( wrappedPath.getNavigablePath() );
registerEntityNameUsage( tableGroup, EntityNameUse.TREAT, treatTarget.getTypeName(), false );
return expression;
}
if ( !( expression.getExpressionType() instanceof BasicValuedMapping ) ) {
// A case wrapper for non-basic paths is not possible,
// because a case expression must return a scalar value.
@ -5167,9 +5209,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
// by registering the treat into tableGroupEntityNameUses.
// Joins don't need the restriction as it will be embedded into
// the joined table group itself by #pruneTableGroupJoins
final String treatedName = treatedPath.getTreatTarget().getHibernateEntityName();
final TableGroup tableGroup = getFromClauseIndex().findTableGroup( wrappedPath.getNavigablePath() );
registerEntityNameUsage( tableGroup, EntityNameUse.TREAT, treatedName );
registerEntityNameUsage( tableGroup, EntityNameUse.TREAT, treatTarget.getTypeName(), false );
}
return expression;
}
@ -5182,7 +5223,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
// Only need a case expression around the basic valued path for the parent treat expression
// if the column of the basic valued path is shared between subclasses
if ( persister.isSharedColumn( basicPath.getColumnReference().getColumnExpression() ) ) {
return createCaseExpression( wrappedPath, treatedPath.getTreatTarget(), expression );
return createCaseExpression( wrappedPath, (EntityDomainType<?>) treatTarget, expression );
}
}
return expression;
@ -5219,40 +5260,67 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
Predicate predicate = null;
for ( Map.Entry<TableGroup, Map<String, EntityNameUse>> entry : conjunctTreatUsages.entrySet() ) {
final TableGroup tableGroup = entry.getKey();
final Set<String> entityNames = determineEntityNamesForTreatTypeRestriction(
(EntityMappingType) tableGroup.getModelPart().getPartMappingType(),
entry.getValue()
);
if ( entityNames.isEmpty() ) {
continue;
}
final ModelPartContainer modelPart = tableGroup.getModelPart();
final Set<String> typeNames;
final EntityMappingType entityMapping;
if ( modelPart instanceof EntityValuedModelPart ) {
final EmbeddableMappingType embeddableMapping;
if ( modelPart instanceof PluralAttributeMapping ) {
entityMapping = (EntityMappingType) ( (PluralAttributeMapping) modelPart ).getElementDescriptor()
.getPartMappingType();
embeddableMapping = null;
}
else if ( modelPart instanceof EntityValuedModelPart ) {
entityMapping = ( (EntityValuedModelPart) modelPart ).getEntityMappingType();
embeddableMapping = null;
}
else if ( modelPart instanceof EmbeddableValuedModelPart ) {
embeddableMapping = ( (EmbeddableValuedModelPart) modelPart ).getEmbeddableTypeDescriptor();
entityMapping = null;
}
else {
entityMapping = (EntityMappingType) ( (PluralAttributeMapping) modelPart ).getElementDescriptor().getPartMappingType();
throw new IllegalStateException( "Unrecognized model part for treated table group: " + tableGroup );
}
final DiscriminatorPathInterpretation<?> typeExpression;
if ( entityMapping != null ) {
typeNames = determineEntityNamesForTreatTypeRestriction( entityMapping, entry.getValue() );
if ( typeNames.isEmpty() ) {
continue;
}
typeExpression = new DiscriminatorPathInterpretation<>(
tableGroup.getNavigablePath().append( EntityDiscriminatorMapping.DISCRIMINATOR_ROLE_NAME ),
entityMapping,
tableGroup,
this
);
registerTypeUsage( tableGroup );
}
else {
assert embeddableMapping != null;
typeNames = determineEmbeddableNamesForTreatTypeRestriction( embeddableMapping, entry.getValue() );
if ( typeNames.isEmpty() ) {
continue;
}
typeExpression = new DiscriminatorPathInterpretation<>(
tableGroup.getNavigablePath().append( EntityDiscriminatorMapping.DISCRIMINATOR_ROLE_NAME ),
embeddableMapping.getDiscriminatorMapping(),
tableGroup,
this
);
}
final DiscriminatorPathInterpretation<?> typeExpression = new DiscriminatorPathInterpretation<>(
tableGroup.getNavigablePath().append( EntityDiscriminatorMapping.DISCRIMINATOR_ROLE_NAME ),
entityMapping,
tableGroup,
this
);
// We need to check if this is a treated left or full join, which case we should
// allow null discriminator values to maintain correct semantics
final TableGroupJoin join = getParentTableGroupJoin( tableGroup );
final boolean allowNulls = join != null && ( join.getJoinType() == SqlAstJoinType.LEFT || join.getJoinType() == SqlAstJoinType.FULL );
registerTypeUsage( tableGroup );
predicate = combinePredicates(
predicate,
createTreatTypeRestriction(
typeExpression,
entityNames,
allowNulls
typeNames,
allowNulls,
entityMapping != null
)
);
}
@ -5334,6 +5402,22 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return entityNames;
}
private Set<String> determineEmbeddableNamesForTreatTypeRestriction(
EmbeddableMappingType embeddableMappingType,
Map<String, EntityNameUse> entityNameUses) {
final EmbeddableDomainType<?> embeddableDomainType = creationContext.getSessionFactory()
.getRuntimeMetamodels()
.getJpaMetamodel()
.embeddable( embeddableMappingType.getJavaType().getJavaTypeClass() );
final Set<String> entityNameUsesSet = new HashSet<>( entityNameUses.keySet() );
ManagedDomainType<?> superType = embeddableDomainType;
while ( superType != null ) {
entityNameUsesSet.remove( superType.getTypeName() );
superType = superType.getSuperType();
}
return entityNameUsesSet;
}
private Predicate createTreatTypeRestriction(SqmPath<?> lhs, EntityDomainType<?> treatTarget) {
final AbstractEntityPersister entityDescriptor = (AbstractEntityPersister) domainModel.findEntityDescriptor( treatTarget.getHibernateEntityName() );
if ( entityDescriptor.isPolymorphic() && lhs.getNodeType() != treatTarget ) {
@ -5351,26 +5435,28 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return createTreatTypeRestriction(
DiscriminatorPathInterpretation.from( discriminatorSqmPath, this ),
subclassEntityNames,
false
false,
true
);
}
private Predicate createTreatTypeRestriction(
Expression typeExpression,
Set<String> subclassEntityNames,
boolean allowNulls) {
SqmPathInterpretation<?> typeExpression,
Set<String> subtypeNames,
boolean allowNulls,
boolean entity) {
final Predicate discriminatorPredicate;
if ( subclassEntityNames.size() == 1 ) {
if ( subtypeNames.size() == 1 ) {
discriminatorPredicate = new ComparisonPredicate(
typeExpression,
ComparisonOperator.EQUAL,
new EntityTypeLiteral( domainModel.findEntityDescriptor( subclassEntityNames.iterator().next() ) )
getTypeLiteral( typeExpression, subtypeNames.iterator().next(), entity )
);
}
else {
final List<Expression> typeLiterals = new ArrayList<>( subclassEntityNames.size() );
for ( String subclassEntityName : subclassEntityNames ) {
typeLiterals.add( new EntityTypeLiteral( domainModel.findEntityDescriptor( subclassEntityName ) ) );
final List<Expression> typeLiterals = new ArrayList<>( subtypeNames.size() );
for ( String subtypeName : subtypeNames ) {
typeLiterals.add( getTypeLiteral( typeExpression, subtypeName, entity ) );
}
discriminatorPredicate = new InListPredicate( typeExpression, typeLiterals );
}
@ -5384,6 +5470,22 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return discriminatorPredicate;
}
private Expression getTypeLiteral(SqmPathInterpretation<?> typeExpression, String typeName, boolean entity) {
if ( entity ) {
return new EntityTypeLiteral( domainModel.findEntityDescriptor( typeName ) );
}
else {
final EmbeddableDomainType<?> embeddable = creationContext.getSessionFactory()
.getRuntimeMetamodels()
.getJpaMetamodel()
.embeddable( typeName );
return new EmbeddableTypeLiteral(
embeddable,
(BasicType<?>) typeExpression.getExpressionType().getSingleJdbcMapping()
);
}
}
private MappingModelExpressible<?> resolveInferredType() {
final Supplier<MappingModelExpressible<?>> inferableTypeAccess = inferrableTypeAccessStack.getCurrent();
if ( inTypeInference || inferableTypeAccess == null ) {
@ -7130,6 +7232,16 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return new EntityTypeLiteral( mappingDescriptor );
}
@Override
public Object visitEmbeddableTypeLiteralExpression(SqmLiteralEmbeddableType<?> expression) {
final MappingModelExpressible<?> inferredValueMapping = getInferredValueMapping();
// The inferred value mapping for literal embeddable types will either be the
// discriminator mapping for polymorphic embeddables or the Class<?> basic type
final BasicType<?> basicType = inferredValueMapping != null ?
(BasicType<?>) inferredValueMapping.getSingleJdbcMapping() :
expression.getNodeType();
return new EmbeddableTypeLiteral( expression.getExpressible(), basicType );
}
@Override
public Expression visitAnyDiscriminatorTypeValueExpression(SqmAnyDiscriminatorValue<?> expression) {
@ -7278,7 +7390,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
disjunctEntityNameUsesArray = new Map[predicate.getPredicates().size()];
entityNameUsesToPropagate = previousTableGroupEntityNameUses == null
? new IdentityHashMap<>()
: previousTableGroupEntityNameUses;
: new IdentityHashMap<>( previousTableGroupEntityNameUses );
}
if ( i == 0 ) {
// Collect the table groups for which filters are registered
@ -7420,48 +7532,56 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return disjunction;
}
// Build the intersection of the conjunct treat usages,
// so that we can push that up and infer during pruning, which entity subclasses can be omitted
final Iterator<Map.Entry<TableGroup, Map<String, EntityNameUse>>> iterator = tableGroupEntityNameUses.entrySet().iterator();
while ( iterator.hasNext() ) {
final Map.Entry<TableGroup, Map<String, EntityNameUse>> entry = iterator.next();
final Map<String, EntityNameUse> intersected = new HashMap<>( entry.getValue() );
entry.setValue( intersected );
boolean remove = false;
for ( Map<TableGroup, Map<String, EntityNameUse>> conjunctTreatUsages : disjunctEntityNameUsesArray ) {
final Map<String, EntityNameUse> entityNames;
if ( conjunctTreatUsages == null || ( entityNames = conjunctTreatUsages.get( entry.getKey() ) ) == null ) {
remove = true;
continue;
}
// Intersect the two sets and transfer the common elements to the intersection
final Iterator<Map.Entry<String, EntityNameUse>> intersectedIter = intersected.entrySet().iterator();
while ( intersectedIter.hasNext() ) {
final Map.Entry<String, EntityNameUse> intersectedEntry = intersectedIter.next();
final EntityNameUse intersectedUseKind = intersectedEntry.getValue();
final EntityNameUse useKind = entityNames.get( intersectedEntry.getKey() );
if ( useKind == null ) {
intersectedIter.remove();
if ( !tableGroupEntityNameUses.isEmpty() ) {
// Build the intersection of the conjunct treat usages,
// so that we can push that up and infer during pruning, which entity subclasses can be omitted
final Iterator<Map.Entry<TableGroup, Map<String, EntityNameUse>>> iterator = tableGroupEntityNameUses.entrySet().iterator();
while ( iterator.hasNext() ) {
final Map.Entry<TableGroup, Map<String, EntityNameUse>> entry = iterator.next();
final Map<String, EntityNameUse> intersected = new HashMap<>( entry.getValue() );
entry.setValue( intersected );
boolean remove = false;
for ( Map<TableGroup, Map<String, EntityNameUse>> conjunctTreatUsages : disjunctEntityNameUsesArray ) {
final Map<String, EntityNameUse> entityNames;
if ( conjunctTreatUsages == null || ( entityNames = conjunctTreatUsages.get( entry.getKey() ) ) == null ) {
remove = true;
break;
}
else {
// Possibly downgrade a FILTER use to EXPRESSION if one of the disjunctions does not use FILTER
intersectedEntry.setValue( intersectedUseKind.weaker( useKind ) );
// Intersect the two sets and transfer the common elements to the intersection
final Iterator<Map.Entry<String, EntityNameUse>> intersectedIter = intersected.entrySet()
.iterator();
while ( intersectedIter.hasNext() ) {
final Map.Entry<String, EntityNameUse> intersectedEntry = intersectedIter.next();
final EntityNameUse intersectedUseKind = intersectedEntry.getValue();
final EntityNameUse useKind = entityNames.get( intersectedEntry.getKey() );
if ( useKind == null ) {
intersectedIter.remove();
}
else {
// Possibly downgrade a FILTER use to EXPRESSION if one of the disjunctions does not use FILTER
intersectedEntry.setValue( intersectedUseKind.weaker( useKind ) );
}
}
if ( intersected.isEmpty() ) {
remove = true;
break;
}
entityNames.keySet().removeAll( intersected.keySet() );
if ( entityNames.isEmpty() ) {
conjunctTreatUsages.remove( entry.getKey() );
}
}
if ( intersected.isEmpty() ) {
remove = true;
continue;
}
entityNames.keySet().removeAll( intersected.keySet() );
if ( entityNames.isEmpty() ) {
conjunctTreatUsages.remove( entry.getKey() );
}
}
if ( remove ) {
iterator.remove();
if ( remove ) {
entityNameUsesToPropagate.remove( entry.getKey() );
iterator.remove();
}
}
}
else {
// If there's no baseline to construct the intersection from don't propagate
entityNameUsesToPropagate.clear();
}
// Prepend the treat type usages to the respective conjuncts
for ( int i = 0; i < disjunctEntityNameUsesArray.length; i++ ) {
@ -7476,6 +7596,12 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
);
}
}
// Restore the parent context entity name uses state
tableGroupEntityNameUses.clear();
if ( previousTableGroupEntityNameUses != null ) {
tableGroupEntityNameUses.putAll( previousTableGroupEntityNameUses );
}
// Propagate the union of the entity name uses upwards
for ( Map.Entry<TableGroup, Map<String, EntityNameUse>> entry : entityNameUsesToPropagate.entrySet() ) {
final Map<String, EntityNameUse> entityNameUses = tableGroupEntityNameUses.putIfAbsent(
@ -7638,7 +7764,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
List<EntityTypeLiteral> literalExpressions,
boolean inclusive) {
final TableGroup tableGroup = getFromClauseIndex().getTableGroup( typeExpression.getNavigablePath().getParent() );
final EntityMappingType entityMappingType = (EntityMappingType) tableGroup.getModelPart().getPartMappingType();
final MappingType partMappingType = tableGroup.getModelPart().getPartMappingType();
if ( !( partMappingType instanceof EntityMappingType ) ) {
return;
}
final EntityMappingType entityMappingType = (EntityMappingType) partMappingType;
if ( entityMappingType.getDiscriminatorMapping().hasPhysicalColumn() ) {
// If the entity has a physical discriminator column we don't need to register any FILTER usages.
// Register only an EXPRESSION usage to prevent pruning of the root type's table reference which

View File

@ -33,6 +33,7 @@ import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.update.Assignable;
import static jakarta.persistence.metamodel.Type.PersistenceType.ENTITY;
import static org.hibernate.internal.util.NullnessUtil.castNonNull;
import static org.hibernate.query.sqm.internal.SqmUtil.getTargetMappingIfNeeded;
@ -51,8 +52,8 @@ public class BasicValuedPathInterpretation<T> extends AbstractSqmPathInterpretat
final TableGroup tableGroup = sqlAstCreationState.getFromClauseAccess().getTableGroup( lhs.getNavigablePath() );
EntityMappingType treatTarget = null;
final ModelPartContainer modelPartContainer;
if ( lhs instanceof SqmTreatedPath<?, ?> ) {
final EntityDomainType<?> treatTargetDomainType = ( (SqmTreatedPath<?, ?>) lhs ).getTreatTarget();
if ( lhs instanceof SqmTreatedPath<?, ?> && ( (SqmTreatedPath<?, ?>) lhs ).getTreatTarget().getPersistenceType() == ENTITY ) {
final EntityDomainType<?> treatTargetDomainType = (EntityDomainType<?>) ( (SqmTreatedPath<?, ?>) lhs ).getTreatTarget();
final MappingMetamodel mappingMetamodel = sqlAstCreationState.getCreationContext()
.getSessionFactory()

View File

@ -13,6 +13,8 @@ import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.model.domain.DiscriminatorSqmPath;
import org.hibernate.metamodel.model.domain.internal.EmbeddedDiscriminatorSqmPath;
import org.hibernate.metamodel.model.domain.internal.EntityDiscriminatorSqmPath;
import org.hibernate.query.results.ResultSetMappingSqlSelection;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
@ -27,6 +29,8 @@ import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.query.sqm.internal.SqmMappingModelHelper.resolveMappingModelExpressible;
/**
* SqmPathInterpretation and DomainResultProducer implementation for entity discriminator
*
@ -58,22 +62,38 @@ public class DiscriminatorPathInterpretation<T> extends AbstractSqmPathInterpret
}
public static SqmPathInterpretation<?> from(
EntityDiscriminatorSqmPath path,
DiscriminatorSqmPath<?> path,
SqmToSqlAstConverter converter) {
assert path.getEntityDescriptor().hasSubclasses();
final NavigablePath navigablePath = path.getNavigablePath();
final TableGroup tableGroup = converter.getFromClauseAccess().getTableGroup( navigablePath.getParent() );
final ModelPartContainer modelPart = tableGroup.getModelPart();
final EntityMappingType entityMapping;
if ( modelPart instanceof EntityValuedModelPart ) {
entityMapping = ( (EntityValuedModelPart) modelPart ).getEntityMappingType();
if ( path instanceof EntityDiscriminatorSqmPath<?> ) {
assert ((EntityDiscriminatorSqmPath<?>) path).getEntityDescriptor().hasSubclasses();
final EntityMappingType entityMapping;
if ( modelPart instanceof EntityValuedModelPart ) {
entityMapping = ( (EntityValuedModelPart) modelPart ).getEntityMappingType();
}
else {
entityMapping = (EntityMappingType) ( (PluralAttributeMapping) modelPart ).getElementDescriptor().getPartMappingType();
}
return new DiscriminatorPathInterpretation<>( navigablePath, entityMapping, tableGroup, converter );
}
else {
entityMapping = (EntityMappingType) ( (PluralAttributeMapping) modelPart ).getElementDescriptor().getPartMappingType();
final EmbeddedDiscriminatorSqmPath<?> embeddableDiscriminator = (EmbeddedDiscriminatorSqmPath<?>) path;
final DiscriminatorMapping discriminator = (DiscriminatorMapping) resolveMappingModelExpressible(
embeddableDiscriminator,
converter.getCreationContext().getMappingMetamodel(),
converter.getFromClauseAccess()::findTableGroup
);
return new DiscriminatorPathInterpretation<>(
navigablePath,
discriminator,
tableGroup,
converter
);
}
return new DiscriminatorPathInterpretation<>( navigablePath, entityMapping, tableGroup, converter );
}
public EntityDiscriminatorMapping getDiscriminatorMapping() {

View File

@ -28,6 +28,7 @@ import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.update.Assignable;
import static jakarta.persistence.metamodel.Type.PersistenceType.ENTITY;
import static org.hibernate.query.sqm.internal.SqmUtil.getTargetMappingIfNeeded;
/**
@ -50,16 +51,14 @@ public class EmbeddableValuedPathInterpretation<T> extends AbstractSqmPathInterp
.getSessionFactory()
.getRuntimeMetamodels()
.getMappingMetamodel();
if ( lhs instanceof SqmTreatedPath ) {
//noinspection rawtypes
final EntityDomainType<?> treatTargetDomainType = ( (SqmTreatedPath) lhs ).getTreatTarget();
if ( lhs instanceof SqmTreatedPath<?, ?> && ( (SqmTreatedPath<?, ?>) lhs ).getTreatTarget().getPersistenceType() == ENTITY ) {
final EntityDomainType<?> treatTargetDomainType = (EntityDomainType<?>) ( (SqmTreatedPath<?, ?>) lhs ).getTreatTarget();
treatTarget = mappingMetamodel.findEntityDescriptor( treatTargetDomainType.getHibernateEntityName() );
}
else if ( lhs.getNodeType() instanceof EntityDomainType ) {
//noinspection rawtypes
final EntityDomainType<?> entityDomainType = (EntityDomainType) lhs.getNodeType();
treatTarget = mappingMetamodel.findEntityDescriptor( entityDomainType.getHibernateEntityName() );
}
}

View File

@ -286,22 +286,16 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
}
}
@Override
public boolean hasTreats() {
return treats != null && !treats.isEmpty();
}
@Override
public List<SqmFrom<?, ?>> getSqmTreats() {
return treats == null ? Collections.emptyList() : treats;
}
protected <S, X extends SqmFrom<?, S>> X findTreat(EntityDomainType<S> targetType, String alias) {
protected <S, X extends SqmFrom<?, S>> X findTreat(ManagedDomainType<S> targetType, String alias) {
if ( treats != null ) {
for ( SqmFrom<?, ?> treat : treats ) {
if ( treat.getModel() == targetType ) {
if ( treat.getExplicitAlias() == null && alias == null
|| Objects.equals( treat.getExplicitAlias(), alias ) ) {
if ( Objects.equals( treat.getExplicitAlias(), alias ) ) {
//noinspection unchecked
return (X) treat;
}

View File

@ -17,6 +17,7 @@ import org.hibernate.internal.util.NullnessUtil;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
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.ManagedDomainType;
import org.hibernate.metamodel.model.domain.PersistentAttribute;
@ -219,12 +220,17 @@ public abstract class AbstractSqmPath<T> extends AbstractSqmExpression<T> implem
}
}
protected <S extends T> SqmTreatedPath<T, S> getTreatedPath(EntityDomainType<S> treatTarget) {
final NavigablePath treat = getNavigablePath().treatAs( treatTarget.getHibernateEntityName() );
protected <S extends T> SqmTreatedPath<T, S> getTreatedPath(ManagedDomainType<S> treatTarget) {
final NavigablePath treat = getNavigablePath().treatAs( treatTarget.getTypeName() );
//noinspection unchecked
SqmTreatedPath<T, S> path = (SqmTreatedPath<T, S>) getLhs().getReusablePath( treat.getLocalName() );
if ( path == null ) {
path = new SqmTreatedSimplePath<>( this, treatTarget, nodeBuilder() );
if ( treatTarget instanceof EntityDomainType<?> ) {
path = new SqmTreatedEntityValuedSimplePath<>( this, (EntityDomainType<S>) treatTarget, nodeBuilder() );
}
else {
path = new SqmTreatedEmbeddedValuedSimplePath<>( this, (EmbeddableDomainType<S>) treatTarget );
}
getLhs().registerReusablePath( path );
}
return path;

View File

@ -10,6 +10,8 @@ import java.util.Collection;
import org.hibernate.metamodel.model.domain.BagPersistentAttribute;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.TreatableDomainType;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.spi.NavigablePath;
import org.hibernate.query.criteria.JpaCollectionJoin;
@ -116,8 +118,8 @@ public class SqmBagJoin<O, E> extends AbstractSqmPluralJoin<O,Collection<E>, E>
}
@Override
public <S extends E> SqmTreatedBagJoin<O, E, S> treatAs(Class<S> treatAsType) {
return treatAs( nodeBuilder().getDomainModel().entity( treatAsType ) );
public <S extends E> SqmTreatedBagJoin<O, E, S> treatAs(Class<S> treatJavaType) {
return treatAs( treatJavaType, null );
}
@Override
@ -127,7 +129,7 @@ public class SqmBagJoin<O, E> extends AbstractSqmPluralJoin<O,Collection<E>, E>
@Override
public <S extends E> SqmTreatedBagJoin<O,E,S> treatAs(Class<S> treatJavaType, String alias) {
return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias );
return treatAs( treatJavaType, alias, false );
}
@Override
@ -136,8 +138,18 @@ public class SqmBagJoin<O, E> extends AbstractSqmPluralJoin<O,Collection<E>, E>
}
@Override
public <S extends E> SqmTreatedBagJoin<O,E,S> treatAs(Class<S> treatJavaType, String alias, boolean fetch) {
return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias, fetch );
public <S extends E> SqmTreatedBagJoin<O, E, S> treatAs(Class<S> treatJavaType, String alias, boolean fetch) {
final ManagedDomainType<S> treatTarget = nodeBuilder().getDomainModel().managedType( treatJavaType );
final SqmTreatedBagJoin<O, E, S> treat = findTreat( treatTarget, alias );
if ( treat == null ) {
if ( treatTarget instanceof TreatableDomainType<?> ) {
return addTreat( new SqmTreatedBagJoin<>( this, (TreatableDomainType<S>) treatTarget, alias, fetch ) );
}
else {
throw new IllegalArgumentException( "Not a treatable type: " + treatJavaType.getName() );
}
}
return treat;
}
@Override

View File

@ -99,12 +99,12 @@ public class SqmEmbeddedValuedSimplePath<T>
@Override
public <S extends T> SqmTreatedPath<T, S> treatAs(Class<S> treatJavaType) throws PathException {
throw new FunctionArgumentException( "Embeddable paths cannot be TREAT-ed" );
return getTreatedPath( nodeBuilder().getDomainModel().embeddable( treatJavaType ) );
}
@Override
public <S extends T> SqmTreatedPath<T, S> treatAs(EntityDomainType<S> treatTarget) throws PathException {
throw new FunctionArgumentException( "Embeddable paths cannot be TREAT-ed" );
throw new FunctionArgumentException( "Embeddable paths cannot be TREAT-ed to an entity type" );
}
@Override

View File

@ -78,8 +78,8 @@ public class SqmEntityValuedSimplePath<T> extends AbstractSqmSimplePath<T> {
// }
@Override
public <S extends T> SqmTreatedSimplePath<T,S> treatAs(Class<S> treatJavaType) throws PathException {
return (SqmTreatedSimplePath<T, S>) treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ) );
public <S extends T> SqmTreatedEntityValuedSimplePath<T,S> treatAs(Class<S> treatJavaType) throws PathException {
return (SqmTreatedEntityValuedSimplePath<T, S>) treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ) );
}
@Override

View File

@ -10,6 +10,8 @@ import java.util.List;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.ListPersistentAttribute;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.TreatableDomainType;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.spi.NavigablePath;
import org.hibernate.query.criteria.JpaExpression;
@ -123,8 +125,8 @@ public class SqmListJoin<O,E>
}
@Override
public <S extends E> SqmTreatedListJoin<O,E,S> treatAs(Class<S> treatAsType) {
return treatAs( nodeBuilder().getDomainModel().entity( treatAsType ), null );
public <S extends E> SqmTreatedListJoin<O,E,S> treatAs(Class<S> treatJavaType) {
return treatAs( treatJavaType, null );
}
@Override
@ -134,7 +136,7 @@ public class SqmListJoin<O,E>
@Override
public <S extends E> SqmTreatedListJoin<O,E,S> treatAs(Class<S> treatJavaType, String alias) {
return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias );
return treatAs( treatJavaType, alias, false );
}
@Override
@ -143,8 +145,18 @@ public class SqmListJoin<O,E>
}
@Override
public <S extends E> SqmTreatedListJoin<O,E,S> treatAs(Class<S> treatJavaType, String alias, boolean fetch) {
return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias, fetch );
public <S extends E> SqmTreatedListJoin<O, E, S> treatAs(Class<S> treatJavaType, String alias, boolean fetch) {
final ManagedDomainType<S> treatTarget = nodeBuilder().getDomainModel().managedType( treatJavaType );
final SqmTreatedListJoin<O, E, S> treat = findTreat( treatTarget, alias );
if ( treat == null ) {
if ( treatTarget instanceof TreatableDomainType<?> ) {
return addTreat( new SqmTreatedListJoin<>( this, (TreatableDomainType<S>) treatTarget, alias, fetch ) );
}
else {
throw new IllegalArgumentException( "Not a treatable type: " + treatJavaType.getName() );
}
}
return treat;
}
@Override

View File

@ -11,7 +11,9 @@ import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Predicate;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.MapPersistentAttribute;
import org.hibernate.metamodel.model.domain.TreatableDomainType;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.spi.NavigablePath;
import org.hibernate.query.criteria.JpaExpression;
@ -136,7 +138,7 @@ public class SqmMapJoin<O, K, V>
@Override
public <S extends V> SqmTreatedMapJoin<O, K, V, S> treatAs(Class<S> treatJavaType) {
return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ) );
return treatAs( treatJavaType, null );
}
@Override
@ -146,7 +148,7 @@ public class SqmMapJoin<O, K, V>
@Override
public <S extends V> SqmTreatedMapJoin<O, K, V, S> treatAs(Class<S> treatJavaType, String alias) {
return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias );
return treatAs( treatJavaType, alias, false );
}
@Override
@ -156,7 +158,17 @@ public class SqmMapJoin<O, K, V>
@Override
public <S extends V> SqmTreatedMapJoin<O, K, V, S> treatAs(Class<S> treatJavaType, String alias, boolean fetch) {
return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias, fetch );
final ManagedDomainType<S> treatTarget = nodeBuilder().getDomainModel().managedType( treatJavaType );
final SqmTreatedMapJoin<O, K, V, S> treat = findTreat( treatTarget, alias );
if ( treat == null ) {
if ( treatTarget instanceof TreatableDomainType<?> ) {
return addTreat( new SqmTreatedMapJoin<>( this, (TreatableDomainType<S>) treatTarget, alias, fetch ) );
}
else {
throw new IllegalArgumentException( "Not a treatable type: " + treatJavaType.getName() );
}
}
return treat;
}
@Override

View File

@ -171,13 +171,13 @@ public class SqmPluralValuedSimplePath<E> extends AbstractSqmSimplePath<E> {
}
@Override
public <S extends E> SqmTreatedSimplePath<E,S> treatAs(Class<S> treatJavaType) throws PathException {
return (SqmTreatedSimplePath<E, S>) treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ) );
public <S extends E> SqmTreatedPath<E, S> treatAs(Class<S> treatJavaType) throws PathException {
throw new UnsupportedOperationException( "Cannot treat plural valued simple paths" );
}
@Override
public <S extends E> SqmTreatedPath<E, S> treatAs(EntityDomainType<S> treatTarget) throws PathException {
return getTreatedPath( treatTarget );
public <S extends E> SqmTreatedEntityValuedSimplePath<E, S> treatAs(EntityDomainType<S> treatTarget) throws PathException {
throw new UnsupportedOperationException( "Cannot treat plural valued simple paths" );
}
// @Override

View File

@ -9,7 +9,9 @@ package org.hibernate.query.sqm.tree.domain;
import java.util.Set;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.SetPersistentAttribute;
import org.hibernate.metamodel.model.domain.TreatableDomainType;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.spi.NavigablePath;
import org.hibernate.query.criteria.JpaExpression;
@ -116,8 +118,8 @@ public class SqmSetJoin<O, E>
}
@Override
public <S extends E> SqmTreatedSetJoin<O,E,S> treatAs(Class<S> treatAsType) {
return treatAs( nodeBuilder().getDomainModel().entity( treatAsType ) );
public <S extends E> SqmTreatedSetJoin<O,E,S> treatAs(Class<S> treatJavaType) {
return treatAs( treatJavaType, null );
}
@Override
@ -127,7 +129,7 @@ public class SqmSetJoin<O, E>
@Override
public <S extends E> SqmTreatedSetJoin<O,E,S> treatAs(Class<S> treatJavaType, String alias) {
return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias );
return treatAs( treatJavaType, alias, false );
}
@Override
@ -136,8 +138,18 @@ public class SqmSetJoin<O, E>
}
@Override
public <S extends E> SqmTreatedSetJoin<O,E,S> treatAs(Class<S> treatJavaType, String alias, boolean fetch) {
return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias, fetch );
public <S extends E> SqmTreatedSetJoin<O, E, S> treatAs(Class<S> treatJavaType, String alias, boolean fetch) {
final ManagedDomainType<S> treatTarget = nodeBuilder().getDomainModel().managedType( treatJavaType );
final SqmTreatedSetJoin<O, E, S> treat = findTreat( treatTarget, alias );
if ( treat == null ) {
if ( treatTarget instanceof TreatableDomainType<?> ) {
return addTreat( new SqmTreatedSetJoin<>( this, (TreatableDomainType<S>) treatTarget, alias, fetch ) );
}
else {
throw new IllegalArgumentException( "Not a treatable type: " + treatJavaType.getName() );
}
}
return treat;
}
@Override

View File

@ -8,8 +8,11 @@ package org.hibernate.query.sqm.tree.domain;
import java.util.Locale;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.metamodel.model.domain.TreatableDomainType;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.spi.NavigablePath;
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
@ -95,7 +98,7 @@ public class SqmSingularJoin<O,T> extends AbstractSqmAttributeJoin<O,T> {
@Override
public <S extends T> SqmTreatedSingularJoin<O,T,S> treatAs(Class<S> treatJavaType) {
return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ) );
return treatAs( treatJavaType, null );
}
@Override
@ -105,7 +108,7 @@ public class SqmSingularJoin<O,T> extends AbstractSqmAttributeJoin<O,T> {
@Override
public <S extends T> SqmTreatedSingularJoin<O,T,S> treatAs(Class<S> treatJavaType, String alias) {
return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias );
return treatAs( treatJavaType, alias, false );
}
@Override
@ -115,7 +118,17 @@ public class SqmSingularJoin<O,T> extends AbstractSqmAttributeJoin<O,T> {
@Override
public <S extends T> SqmTreatedSingularJoin<O,T,S> treatAs(Class<S> treatJavaType, String alias, boolean fetch) {
return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias, fetch );
final ManagedDomainType<S> treatTarget = nodeBuilder().getDomainModel().managedType( treatJavaType );
final SqmTreatedSingularJoin<O, T, S> treat = findTreat( treatTarget, alias );
if ( treat == null ) {
if ( treatTarget instanceof TreatableDomainType<?> ) {
return addTreat( new SqmTreatedSingularJoin<>( this, (TreatableDomainType<S>) treatTarget, alias, fetch ) );
}
else {
throw new IllegalArgumentException( "Not a treatable type: " + treatJavaType.getName() );
}
}
return treat;
}
@Override

View File

@ -8,7 +8,7 @@ package org.hibernate.query.sqm.tree.domain;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.model.domain.BagPersistentAttribute;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.TreatableDomainType;
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.SqmCopyContext;
@ -20,18 +20,18 @@ import org.hibernate.spi.NavigablePath;
*/
public class SqmTreatedBagJoin<O,T, S extends T> extends SqmBagJoin<O,S> implements SqmTreatedPath<T,S> {
private final SqmBagJoin<O, T> wrappedPath;
private final EntityDomainType<S> treatTarget;
private final TreatableDomainType<S> treatTarget;
public SqmTreatedBagJoin(
SqmBagJoin<O, T> wrappedPath,
EntityDomainType<S> treatTarget,
TreatableDomainType<S> treatTarget,
String alias) {
this( wrappedPath, treatTarget, alias, false );
}
public SqmTreatedBagJoin(
SqmBagJoin<O, T> wrappedPath,
EntityDomainType<S> treatTarget,
TreatableDomainType<S> treatTarget,
String alias,
boolean fetched) {
//noinspection unchecked
@ -39,7 +39,7 @@ public class SqmTreatedBagJoin<O,T, S extends T> extends SqmBagJoin<O,S> impleme
wrappedPath.getLhs(),
wrappedPath.getNavigablePath()
.append( CollectionPart.Nature.ELEMENT.getName() )
.treatAs( treatTarget.getHibernateEntityName(), alias ),
.treatAs( treatTarget.getTypeName(), alias ),
(BagPersistentAttribute<O, S>) wrappedPath.getAttribute(),
alias,
wrappedPath.getSqmJoinType(),
@ -53,16 +53,13 @@ public class SqmTreatedBagJoin<O,T, S extends T> extends SqmBagJoin<O,S> impleme
private SqmTreatedBagJoin(
NavigablePath navigablePath,
SqmBagJoin<O, T> wrappedPath,
EntityDomainType<S> treatTarget,
TreatableDomainType<S> treatTarget,
String alias,
boolean fetched) {
//noinspection unchecked
super(
wrappedPath.getLhs(),
wrappedPath.getNavigablePath().treatAs(
treatTarget.getHibernateEntityName(),
alias
),
navigablePath,
(BagPersistentAttribute<O, S>) wrappedPath.getAttribute(),
alias,
wrappedPath.getSqmJoinType(),
@ -99,7 +96,7 @@ public class SqmTreatedBagJoin<O,T, S extends T> extends SqmBagJoin<O,S> impleme
}
@Override
public EntityDomainType<S> getTreatTarget() {
public TreatableDomainType<S> getTreatTarget() {
return treatTarget;
}
@ -109,7 +106,7 @@ public class SqmTreatedBagJoin<O,T, S extends T> extends SqmBagJoin<O,S> impleme
}
@Override
public EntityDomainType<S> getReferencedPathSource() {
public TreatableDomainType<S> getReferencedPathSource() {
return treatTarget;
}
@ -128,7 +125,7 @@ public class SqmTreatedBagJoin<O,T, S extends T> extends SqmBagJoin<O,S> impleme
sb.append( "treat(" );
wrappedPath.appendHqlString( sb );
sb.append( " as " );
sb.append( treatTarget.getName() );
sb.append( treatTarget.getTypeName() );
sb.append( ')' );
}
}

View File

@ -0,0 +1,122 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.query.sqm.tree.domain;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.spi.NavigablePath;
/**
* @author Steve Ebersole
*/
public class SqmTreatedEmbeddedValuedSimplePath<T, S extends T> extends SqmEmbeddedValuedSimplePath<S>
implements SqmTreatedPath<T, S> {
private final SqmPath<T> wrappedPath;
private final EmbeddableDomainType<S> treatTarget;
@SuppressWarnings( { "unchecked" } )
public SqmTreatedEmbeddedValuedSimplePath(
SqmPath<T> wrappedPath,
EmbeddableDomainType<S> treatTarget) {
super(
wrappedPath.getNavigablePath().treatAs( treatTarget.getTypeName() ),
(SqmPathSource<S>) wrappedPath.getReferencedPathSource(),
null,
wrappedPath.nodeBuilder()
);
this.wrappedPath = wrappedPath;
this.treatTarget = treatTarget;
}
@SuppressWarnings( { "unchecked" } )
private SqmTreatedEmbeddedValuedSimplePath(
NavigablePath navigablePath,
SqmPath<T> wrappedPath,
EmbeddableDomainType<S> treatTarget) {
super(
navigablePath,
(SqmPathSource<S>) wrappedPath.getReferencedPathSource(),
null,
wrappedPath.nodeBuilder()
);
this.wrappedPath = wrappedPath;
this.treatTarget = treatTarget;
}
@Override
public SqmTreatedEmbeddedValuedSimplePath<T, S> copy(SqmCopyContext context) {
final SqmTreatedEmbeddedValuedSimplePath<T, S> existing = context.getCopy( this );
if ( existing != null ) {
return existing;
}
final SqmTreatedEmbeddedValuedSimplePath<T, S> path = context.registerCopy(
this,
new SqmTreatedEmbeddedValuedSimplePath<>(
getNavigablePath(),
wrappedPath.copy( context ),
treatTarget
)
);
copyTo( path, context );
return path;
}
@Override
public EmbeddableDomainType<S> getTreatTarget() {
return treatTarget;
}
@Override
public SqmPath<T> getWrappedPath() {
return wrappedPath;
}
@Override
public SqmPathSource<S> getNodeType() {
return treatTarget;
}
@Override
public SqmPathSource<?> getResolvedModel() {
return treatTarget;
}
@Override
public EmbeddableDomainType<S> getReferencedPathSource() {
return getTreatTarget();
}
@Override
public SqmPath<?> getLhs() {
return wrappedPath.getLhs();
}
@Override
public <X> X accept(SemanticQueryWalker<X> walker) {
return walker.visitTreatedPath( this );
}
@Override
public SqmPath<?> resolvePathPart(String name, boolean isTerminal, SqmCreationState creationState) {
final SqmPath<?> sqmPath = get( name );
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
return sqmPath;
}
@Override
public void appendHqlString(StringBuilder sb) {
sb.append( "treat(" );
wrappedPath.appendHqlString( sb );
sb.append( " as " );
sb.append( treatTarget.getTypeName() );
sb.append( ')' );
}
}

View File

@ -17,14 +17,14 @@ import org.hibernate.spi.NavigablePath;
/**
* @author Steve Ebersole
*/
public class SqmTreatedSimplePath<T, S extends T>
public class SqmTreatedEntityValuedSimplePath<T, S extends T>
extends SqmEntityValuedSimplePath<S>
implements SqmSimplePath<S>, SqmTreatedPath<T,S> {
private final EntityDomainType<S> treatTarget;
private final SqmPath<T> wrappedPath;
public SqmTreatedSimplePath(
public SqmTreatedEntityValuedSimplePath(
SqmPluralValuedSimplePath<T> wrappedPath,
EntityDomainType<S> treatTarget,
NodeBuilder nodeBuilder) {
@ -41,7 +41,7 @@ public class SqmTreatedSimplePath<T, S extends T>
this.wrappedPath = wrappedPath;
}
public SqmTreatedSimplePath(
public SqmTreatedEntityValuedSimplePath(
SqmPath<T> wrappedPath,
EntityDomainType<S> treatTarget,
NodeBuilder nodeBuilder) {
@ -58,7 +58,7 @@ public class SqmTreatedSimplePath<T, S extends T>
this.wrappedPath = wrappedPath;
}
private SqmTreatedSimplePath(
private SqmTreatedEntityValuedSimplePath(
NavigablePath navigablePath,
SqmPath<T> wrappedPath,
EntityDomainType<S> treatTarget,
@ -75,15 +75,15 @@ public class SqmTreatedSimplePath<T, S extends T>
}
@Override
public SqmTreatedSimplePath<T, S> copy(SqmCopyContext context) {
final SqmTreatedSimplePath<T, S> existing = context.getCopy( this );
public SqmTreatedEntityValuedSimplePath<T, S> copy(SqmCopyContext context) {
final SqmTreatedEntityValuedSimplePath<T, S> existing = context.getCopy( this );
if ( existing != null ) {
return existing;
}
final SqmTreatedSimplePath<T, S> path = context.registerCopy(
final SqmTreatedEntityValuedSimplePath<T, S> path = context.registerCopy(
this,
new SqmTreatedSimplePath<>(
new SqmTreatedEntityValuedSimplePath<>(
getNavigablePath(),
wrappedPath.copy( context ),
getTreatTarget(),
@ -120,7 +120,7 @@ public class SqmTreatedSimplePath<T, S extends T>
}
@Override
public <S1 extends S> SqmTreatedSimplePath<S,S1> treatAs(Class<S1> treatJavaType) throws PathException {
public <S1 extends S> SqmTreatedEntityValuedSimplePath<S,S1> treatAs(Class<S1> treatJavaType) throws PathException {
return super.treatAs( treatJavaType );
}

View File

@ -7,8 +7,8 @@
package org.hibernate.query.sqm.tree.domain;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.ListPersistentAttribute;
import org.hibernate.metamodel.model.domain.TreatableDomainType;
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.SqmPathSource;
@ -22,18 +22,18 @@ import org.hibernate.spi.NavigablePath;
*/
public class SqmTreatedListJoin<O,T, S extends T> extends SqmListJoin<O,S> implements SqmTreatedPath<T,S> {
private final SqmListJoin<O,T> wrappedPath;
private final EntityDomainType<S> treatTarget;
private final TreatableDomainType<S> treatTarget;
public SqmTreatedListJoin(
SqmListJoin<O, T> wrappedPath,
EntityDomainType<S> treatTarget,
TreatableDomainType<S> treatTarget,
String alias) {
this( wrappedPath, treatTarget, alias, false );
}
public SqmTreatedListJoin(
SqmListJoin<O, T> wrappedPath,
EntityDomainType<S> treatTarget,
TreatableDomainType<S> treatTarget,
String alias,
boolean fetched) {
//noinspection unchecked
@ -41,7 +41,7 @@ public class SqmTreatedListJoin<O,T, S extends T> extends SqmListJoin<O,S> imple
wrappedPath.getLhs(),
wrappedPath.getNavigablePath()
.append( CollectionPart.Nature.ELEMENT.getName() )
.treatAs( treatTarget.getHibernateEntityName(), alias ),
.treatAs( treatTarget.getTypeName(), alias ),
(ListPersistentAttribute<O, S>) wrappedPath.getAttribute(),
alias,
wrappedPath.getSqmJoinType(),
@ -55,7 +55,7 @@ public class SqmTreatedListJoin<O,T, S extends T> extends SqmListJoin<O,S> imple
private SqmTreatedListJoin(
NavigablePath navigablePath,
SqmListJoin<O, T> wrappedPath,
EntityDomainType<S> treatTarget,
TreatableDomainType<S> treatTarget,
String alias,
boolean fetched) {
//noinspection unchecked
@ -98,7 +98,7 @@ public class SqmTreatedListJoin<O,T, S extends T> extends SqmListJoin<O,S> imple
}
@Override
public EntityDomainType<S> getTreatTarget() {
public TreatableDomainType<S> getTreatTarget() {
return treatTarget;
}
@ -108,7 +108,7 @@ public class SqmTreatedListJoin<O,T, S extends T> extends SqmListJoin<O,S> imple
}
@Override
public EntityDomainType<S> getReferencedPathSource() {
public TreatableDomainType<S> getReferencedPathSource() {
return treatTarget;
}
@ -135,7 +135,7 @@ public class SqmTreatedListJoin<O,T, S extends T> extends SqmListJoin<O,S> imple
sb.append( "treat(" );
wrappedPath.appendHqlString( sb );
sb.append( " as " );
sb.append( treatTarget.getName() );
sb.append( treatTarget.getTypeName() );
sb.append( ')' );
}
}

View File

@ -7,7 +7,7 @@
package org.hibernate.query.sqm.tree.domain;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.TreatableDomainType;
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.SqmCopyContext;
@ -18,18 +18,18 @@ import org.hibernate.spi.NavigablePath;
*/
public class SqmTreatedMapJoin<O, K, V, S extends V> extends SqmMapJoin<O, K, S> implements SqmTreatedPath<V, S> {
private final SqmMapJoin<O, K, V> wrappedPath;
private final EntityDomainType<S> treatTarget;
private final TreatableDomainType<S> treatTarget;
public SqmTreatedMapJoin(
SqmMapJoin<O, K, V> wrappedPath,
EntityDomainType<S> treatTarget,
TreatableDomainType<S> treatTarget,
String alias) {
this( wrappedPath, treatTarget, alias, false );
}
public SqmTreatedMapJoin(
SqmMapJoin<O, K, V> wrappedPath,
EntityDomainType<S> treatTarget,
TreatableDomainType<S> treatTarget,
String alias,
boolean fetched) {
//noinspection unchecked
@ -37,7 +37,7 @@ public class SqmTreatedMapJoin<O, K, V, S extends V> extends SqmMapJoin<O, K, S>
wrappedPath.getLhs(),
wrappedPath.getNavigablePath()
.append( CollectionPart.Nature.ELEMENT.getName() )
.treatAs( treatTarget.getHibernateEntityName(), alias ),
.treatAs( treatTarget.getTypeName(), alias ),
( (SqmMapJoin<O, K, S>) wrappedPath ).getModel(),
alias,
wrappedPath.getSqmJoinType(),
@ -51,7 +51,7 @@ public class SqmTreatedMapJoin<O, K, V, S extends V> extends SqmMapJoin<O, K, S>
private SqmTreatedMapJoin(
NavigablePath navigablePath,
SqmMapJoin<O, K, V> wrappedPath,
EntityDomainType<S> treatTarget,
TreatableDomainType<S> treatTarget,
String alias,
boolean fetched) {
//noinspection unchecked
@ -94,7 +94,7 @@ public class SqmTreatedMapJoin<O, K, V, S extends V> extends SqmMapJoin<O, K, S>
}
@Override
public EntityDomainType<S> getTreatTarget() {
public TreatableDomainType<S> getTreatTarget() {
return treatTarget;
}
@ -104,7 +104,7 @@ public class SqmTreatedMapJoin<O, K, V, S extends V> extends SqmMapJoin<O, K, S>
}
@Override
public EntityDomainType<S> getReferencedPathSource() {
public TreatableDomainType<S> getReferencedPathSource() {
return treatTarget;
}
@ -127,7 +127,7 @@ public class SqmTreatedMapJoin<O, K, V, S extends V> extends SqmMapJoin<O, K, S>
sb.append( "treat(" );
wrappedPath.appendHqlString( sb );
sb.append( " as " );
sb.append( treatTarget.getName() );
sb.append( treatTarget.getTypeName() );
sb.append( ')' );
}
}

View File

@ -6,14 +6,13 @@
*/
package org.hibernate.query.sqm.tree.domain;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
/**
* @author Steve Ebersole
*/
public interface SqmTreatedPath<T, S extends T> extends SqmPathWrapper<T, S> {
EntityDomainType<S> getTreatTarget();
ManagedDomainType<S> getTreatTarget();
@Override
SqmPath<T> getWrappedPath();

View File

@ -7,7 +7,7 @@
package org.hibernate.query.sqm.tree.domain;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.TreatableDomainType;
import org.hibernate.metamodel.model.domain.SetPersistentAttribute;
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
import org.hibernate.query.sqm.SqmPathSource;
@ -20,18 +20,18 @@ import org.hibernate.spi.NavigablePath;
*/
public class SqmTreatedSetJoin<O,T, S extends T> extends SqmSetJoin<O,S> implements SqmTreatedPath<T,S> {
private final SqmSetJoin<O,T> wrappedPath;
private final EntityDomainType<S> treatTarget;
private final TreatableDomainType<S> treatTarget;
public SqmTreatedSetJoin(
SqmSetJoin<O, T> wrappedPath,
EntityDomainType<S> treatTarget,
TreatableDomainType<S> treatTarget,
String alias) {
this( wrappedPath, treatTarget, alias, false );
}
public SqmTreatedSetJoin(
SqmSetJoin<O, T> wrappedPath,
EntityDomainType<S> treatTarget,
TreatableDomainType<S> treatTarget,
String alias,
boolean fetched) {
//noinspection unchecked
@ -39,7 +39,7 @@ public class SqmTreatedSetJoin<O,T, S extends T> extends SqmSetJoin<O,S> impleme
wrappedPath.getLhs(),
wrappedPath.getNavigablePath()
.append( CollectionPart.Nature.ELEMENT.getName() )
.treatAs( treatTarget.getHibernateEntityName(), alias ),
.treatAs( treatTarget.getTypeName(), alias ),
(SetPersistentAttribute<O, S>) wrappedPath.getAttribute(),
alias,
wrappedPath.getSqmJoinType(),
@ -53,7 +53,7 @@ public class SqmTreatedSetJoin<O,T, S extends T> extends SqmSetJoin<O,S> impleme
private SqmTreatedSetJoin(
NavigablePath navigablePath,
SqmSetJoin<O, T> wrappedPath,
EntityDomainType<S> treatTarget,
TreatableDomainType<S> treatTarget,
String alias,
boolean fetched) {
//noinspection unchecked
@ -96,7 +96,7 @@ public class SqmTreatedSetJoin<O,T, S extends T> extends SqmSetJoin<O,S> impleme
}
@Override
public EntityDomainType<S> getTreatTarget() {
public TreatableDomainType<S> getTreatTarget() {
return treatTarget;
}
@ -106,7 +106,7 @@ public class SqmTreatedSetJoin<O,T, S extends T> extends SqmSetJoin<O,S> impleme
}
@Override
public EntityDomainType<S> getReferencedPathSource() {
public TreatableDomainType<S> getReferencedPathSource() {
return treatTarget;
}
@ -125,7 +125,7 @@ public class SqmTreatedSetJoin<O,T, S extends T> extends SqmSetJoin<O,S> impleme
sb.append( "treat(" );
wrappedPath.appendHqlString( sb );
sb.append( " as " );
sb.append( treatTarget.getName() );
sb.append( treatTarget.getTypeName() );
sb.append( ')' );
}
}

View File

@ -6,8 +6,8 @@
*/
package org.hibernate.query.sqm.tree.domain;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.metamodel.model.domain.TreatableDomainType;
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.SqmCopyContext;
@ -19,25 +19,25 @@ import org.hibernate.spi.NavigablePath;
*/
public class SqmTreatedSingularJoin<O,T, S extends T> extends SqmSingularJoin<O,S> implements SqmTreatedPath<T,S> {
private final SqmSingularJoin<O,T> wrappedPath;
private final EntityDomainType<S> treatTarget;
private final TreatableDomainType<S> treatTarget;
public SqmTreatedSingularJoin(
SqmSingularJoin<O,T> wrappedPath,
EntityDomainType<S> treatTarget,
TreatableDomainType<S> treatTarget,
String alias) {
this( wrappedPath, treatTarget, alias, false );
}
public SqmTreatedSingularJoin(
SqmSingularJoin<O,T> wrappedPath,
EntityDomainType<S> treatTarget,
TreatableDomainType<S> treatTarget,
String alias,
boolean fetched) {
//noinspection unchecked
super(
wrappedPath.getLhs(),
wrappedPath.getNavigablePath().treatAs(
treatTarget.getHibernateEntityName(),
treatTarget.getTypeName(),
alias
),
(SingularPersistentAttribute<O, S>) wrappedPath.getAttribute(),
@ -53,7 +53,7 @@ public class SqmTreatedSingularJoin<O,T, S extends T> extends SqmSingularJoin<O,
private SqmTreatedSingularJoin(
NavigablePath navigablePath,
SqmSingularJoin<O,T> wrappedPath,
EntityDomainType<S> treatTarget,
TreatableDomainType<S> treatTarget,
String alias,
boolean fetched) {
//noinspection unchecked
@ -96,7 +96,7 @@ public class SqmTreatedSingularJoin<O,T, S extends T> extends SqmSingularJoin<O,
}
@Override
public EntityDomainType<S> getTreatTarget() {
public TreatableDomainType<S> getTreatTarget() {
return treatTarget;
}
@ -106,7 +106,7 @@ public class SqmTreatedSingularJoin<O,T, S extends T> extends SqmSingularJoin<O,
}
@Override
public EntityDomainType<S> getReferencedPathSource() {
public TreatableDomainType<S> getReferencedPathSource() {
return treatTarget;
}
@ -125,7 +125,7 @@ public class SqmTreatedSingularJoin<O,T, S extends T> extends SqmSingularJoin<O,
sb.append( "treat(" );
wrappedPath.appendHqlString( sb );
sb.append( " as " );
sb.append( treatTarget.getName() );
sb.append( treatTarget.getTypeName() );
sb.append( ')' );
}
}

View File

@ -0,0 +1,110 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.query.sqm.tree.expression;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
import org.hibernate.query.hql.HqlInterpretationException;
import org.hibernate.query.hql.spi.SemanticPathPart;
import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.select.SqmSelectableNode;
import org.hibernate.type.BasicType;
import org.hibernate.type.StandardBasicTypes;
/**
* Represents a reference to an embeddable type as a literal.
*
* @author Marco Belladelli
*/
public class SqmLiteralEmbeddableType<T>
extends AbstractSqmExpression<T>
implements SqmSelectableNode<T>, SemanticPathPart {
final EmbeddableDomainType<T> embeddableDomainType;
public SqmLiteralEmbeddableType(
EmbeddableDomainType<T> embeddableDomainType,
NodeBuilder nodeBuilder) {
//noinspection unchecked
super(
(SqmExpressible<? super T>) nodeBuilder.getTypeConfiguration()
.getBasicTypeRegistry()
.resolve( StandardBasicTypes.CLASS ),
nodeBuilder
);
this.embeddableDomainType = embeddableDomainType;
}
public EmbeddableDomainType<T> getEmbeddableDomainType() {
return embeddableDomainType;
}
@Override
public BasicType<T> getNodeType() {
return (BasicType<T>) super.getNodeType();
}
@Override
public EmbeddableDomainType<T> getExpressible() {
return embeddableDomainType;
}
@Override
public SqmLiteralEmbeddableType<T> copy(SqmCopyContext context) {
final SqmLiteralEmbeddableType<T> existing = context.getCopy( this );
if ( existing != null ) {
return existing;
}
final SqmLiteralEmbeddableType<T> expression = context.registerCopy(
this,
new SqmLiteralEmbeddableType<>(
embeddableDomainType,
nodeBuilder()
)
);
copyTo( expression, context );
return expression;
}
@Override
public void internalApplyInferableType(SqmExpressible<?> type) {
}
@Override
public <X> X accept(SemanticQueryWalker<X> walker) {
return walker.visitEmbeddableTypeLiteralExpression( this );
}
@Override
public String asLoggableText() {
return "TYPE(" + embeddableDomainType + ")";
}
@Override
public SemanticPathPart resolvePathPart(
String name,
boolean isTerminal,
SqmCreationState creationState) {
throw new HqlInterpretationException( "Cannot dereference an embeddable name" );
}
@Override
public SqmPath<?> resolveIndexedAccess(
SqmExpression<?> selector,
boolean isTerminal,
SqmCreationState creationState) {
throw new HqlInterpretationException( "Cannot dereference an embeddable name" );
}
@Override
public void appendHqlString(StringBuilder sb) {
sb.append( embeddableDomainType.getTypeName() );
}
}

View File

@ -8,11 +8,8 @@ package org.hibernate.query.sqm.tree.from;
import java.util.List;
import java.util.function.Consumer;
import jakarta.persistence.criteria.CollectionJoin;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.ListJoin;
import jakarta.persistence.criteria.MapJoin;
import jakarta.persistence.criteria.SetJoin;
import jakarta.persistence.metamodel.CollectionAttribute;
import jakarta.persistence.metamodel.ListAttribute;
import jakarta.persistence.metamodel.MapAttribute;
@ -20,11 +17,7 @@ import jakarta.persistence.metamodel.SetAttribute;
import jakarta.persistence.metamodel.SingularAttribute;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.PathException;
import org.hibernate.query.criteria.JpaFrom;
import org.hibernate.query.criteria.JpaJoin;
import org.hibernate.query.criteria.JpaPath;
import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.SqmVisitableNode;
@ -35,6 +28,8 @@ import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmSetJoin;
import org.hibernate.query.sqm.tree.domain.SqmSingularJoin;
import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;
/**
* Models a Bindable's inclusion in the {@code FROM} clause.
*
@ -67,6 +62,15 @@ public interface SqmFrom<O,T> extends SqmVisitableNode, SqmPath<T>, JpaFrom<O, T
*/
void visitSqmJoins(Consumer<SqmJoin<T, ?>> consumer);
/**
* The treats associated with this SqmFrom
*/
List<SqmFrom<?, ?>> getSqmTreats();
default boolean hasTreats() {
return !isEmpty( getSqmTreats() );
}
@Override
<S extends T> SqmFrom<?, S> treatAs(Class<S> treatAsType);
@ -77,14 +81,6 @@ public interface SqmFrom<O,T> extends SqmVisitableNode, SqmPath<T>, JpaFrom<O, T
<S extends T> SqmFrom<?, S> treatAs(EntityDomainType<S> treatTarget, String alias);
boolean hasTreats();
/**
* The treats associated with this SqmFrom
*/
List<SqmFrom<?,?>> getSqmTreats();
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// JPA

View File

@ -141,7 +141,7 @@ public class SqmFromClause implements Serializable {
final SqmTreatedPath<?, ?> treatedPath = (SqmTreatedPath<?, ?>) sqmFrom;
sb.append( "treat(" );
sb.append( treatedPath.getWrappedPath().resolveAlias() );
sb.append( " as " ).append( treatedPath.getTreatTarget().getName() ).append( ')' );
sb.append( " as " ).append( treatedPath.getTreatTarget().getTypeName() ).append( ')' );
}
else {
sb.append( sqmFrom.resolveAlias() );

View File

@ -22,6 +22,7 @@ import org.hibernate.sql.ast.tree.expression.AggregateColumnWriteExpression;
import org.hibernate.sql.ast.tree.expression.Distinct;
import org.hibernate.sql.ast.tree.expression.Duration;
import org.hibernate.sql.ast.tree.expression.DurationUnit;
import org.hibernate.sql.ast.tree.expression.EmbeddableTypeLiteral;
import org.hibernate.sql.ast.tree.expression.EntityTypeLiteral;
import org.hibernate.sql.ast.tree.expression.Every;
import org.hibernate.sql.ast.tree.expression.ExtractUnit;
@ -167,6 +168,8 @@ public interface SqlAstWalker {
void visitEntityTypeLiteral(EntityTypeLiteral expression);
void visitEmbeddableTypeLiteral(EmbeddableTypeLiteral expression);
void visitTuple(SqlTuple tuple);
void visitCollation(Collation collation);

View File

@ -115,6 +115,7 @@ import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Distinct;
import org.hibernate.sql.ast.tree.expression.Duration;
import org.hibernate.sql.ast.tree.expression.DurationUnit;
import org.hibernate.sql.ast.tree.expression.EmbeddableTypeLiteral;
import org.hibernate.sql.ast.tree.expression.EntityTypeLiteral;
import org.hibernate.sql.ast.tree.expression.Every;
import org.hibernate.sql.ast.tree.expression.Expression;
@ -218,6 +219,7 @@ import org.hibernate.type.BasicType;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
import org.hibernate.type.descriptor.jdbc.JdbcType;
@ -225,6 +227,7 @@ import org.hibernate.type.descriptor.sql.DdlType;
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.persister.entity.DiscriminatorHelper.jdbcLiteral;
import static org.hibernate.query.sqm.BinaryArithmeticOperator.DIVIDE_PORTABLE;
import static org.hibernate.query.sqm.TemporalUnit.NANOSECOND;
import static org.hibernate.sql.ast.SqlTreePrinter.logSqlAst;
@ -7203,6 +7206,19 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
appendSql( expression.getEntityTypeDescriptor().getDiscriminatorSQLValue() );
}
@SuppressWarnings( { "rawtypes", "unchecked" } )
@Override
public void visitEmbeddableTypeLiteral(EmbeddableTypeLiteral expression) {
final BasicValueConverter valueConverter = expression.getJdbcMapping().getValueConverter();
appendSql( jdbcLiteral(
valueConverter != null ?
valueConverter.toRelationalValue( expression.getEmbeddableClass() ) :
expression.getEmbeddableClass(),
expression.getExpressionType().getSingleJdbcMapping().getJdbcLiteralFormatter(),
getDialect()
) );
}
@Override
public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) {
final BinaryArithmeticOperator operator = arithmeticExpression.getOperator();

View File

@ -25,6 +25,7 @@ import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Distinct;
import org.hibernate.sql.ast.tree.expression.Duration;
import org.hibernate.sql.ast.tree.expression.DurationUnit;
import org.hibernate.sql.ast.tree.expression.EmbeddableTypeLiteral;
import org.hibernate.sql.ast.tree.expression.EntityTypeLiteral;
import org.hibernate.sql.ast.tree.expression.Every;
import org.hibernate.sql.ast.tree.expression.Expression;
@ -529,6 +530,10 @@ public class AbstractSqlAstWalker implements SqlAstWalker {
public void visitEntityTypeLiteral(EntityTypeLiteral expression) {
}
@Override
public void visitEmbeddableTypeLiteral(EmbeddableTypeLiteral expression) {
}
@Override
public void visitNamedTableReference(NamedTableReference tableReference) {
}

View File

@ -17,6 +17,7 @@ import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Distinct;
import org.hibernate.sql.ast.tree.expression.Duration;
import org.hibernate.sql.ast.tree.expression.DurationUnit;
import org.hibernate.sql.ast.tree.expression.EmbeddableTypeLiteral;
import org.hibernate.sql.ast.tree.expression.EntityTypeLiteral;
import org.hibernate.sql.ast.tree.expression.Every;
import org.hibernate.sql.ast.tree.expression.Expression;
@ -249,6 +250,10 @@ public class AggregateFunctionChecker extends AbstractSqlAstWalker {
public void visitEntityTypeLiteral(EntityTypeLiteral expression) {
}
@Override
public void visitEmbeddableTypeLiteral(EmbeddableTypeLiteral expression) {
}
@Override
public void visitSqlSelectionExpression(SqlSelectionExpression expression) {
}

View File

@ -26,6 +26,7 @@ import org.hibernate.sql.ast.tree.expression.AggregateColumnWriteExpression;
import org.hibernate.sql.ast.tree.expression.Distinct;
import org.hibernate.sql.ast.tree.expression.Duration;
import org.hibernate.sql.ast.tree.expression.DurationUnit;
import org.hibernate.sql.ast.tree.expression.EmbeddableTypeLiteral;
import org.hibernate.sql.ast.tree.expression.EntityTypeLiteral;
import org.hibernate.sql.ast.tree.expression.Every;
import org.hibernate.sql.ast.tree.expression.Expression;
@ -215,6 +216,11 @@ public class ExpressionReplacementWalker implements SqlAstWalker {
doReplaceExpression( expression );
}
@Override
public void visitEmbeddableTypeLiteral(EmbeddableTypeLiteral expression) {
doReplaceExpression( expression );
}
@Override
public void visitTuple(SqlTuple tuple) {
doReplaceExpression( tuple );

View File

@ -0,0 +1,151 @@
/*
* 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.sql.ast.tree.expression;
import org.hibernate.cache.MutableCacheKeyBuilder;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.IndexedConsumer;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingModelExpressible;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaType;
/**
* @author Steve Ebersole
*/
public class EmbeddableTypeLiteral
implements Expression, DomainResultProducer<Object>, BasicValuedMapping {
private final Class<?> embeddableClass;
private final BasicType<?> basicType;
public EmbeddableTypeLiteral(
EmbeddableDomainType<?> embeddableDomainType,
BasicType<?> basicType) {
this.embeddableClass = embeddableDomainType.getJavaType();
this.basicType = basicType;
}
public Object getEmbeddableClass() {
return embeddableClass;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// BasicValuedMapping
@Override
public MappingModelExpressible<?> getExpressionType() {
return this;
}
@Override
public JdbcMapping getJdbcMapping() {
return basicType;
}
@Override
public MappingType getMappedType() {
return basicType;
}
@Override
public int getJdbcTypeCount() {
return basicType.getJdbcTypeCount();
}
@Override
public JdbcMapping getJdbcMapping(int index) {
return basicType.getJdbcMapping( index );
}
@Override
public JdbcMapping getSingleJdbcMapping() {
return basicType.getSingleJdbcMapping();
}
@Override
public int forEachJdbcType(int offset, IndexedConsumer<JdbcMapping> action) {
return basicType.forEachJdbcType( offset, action );
}
@Override
public Object disassemble(Object value, SharedSessionContractImplementor session) {
return basicType.disassemble( value, session );
}
@Override
public void addToCacheKey(MutableCacheKeyBuilder cacheKey, Object value, SharedSessionContractImplementor session) {
basicType.addToCacheKey( cacheKey, value, session );
}
@Override
public <X, Y> int forEachDisassembledJdbcValue(
Object value,
int offset,
X x,
Y y,
JdbcValuesBiConsumer<X, Y> valuesConsumer,
SharedSessionContractImplementor session) {
return basicType.forEachDisassembledJdbcValue( value, offset, x, y, valuesConsumer, session );
}
@Override
public <X, Y> int forEachJdbcValue(
Object value,
int offset,
X x,
Y y,
JdbcValuesBiConsumer<X, Y> valuesConsumer,
SharedSessionContractImplementor session) {
return basicType.forEachJdbcValue( value, offset, x, y, valuesConsumer, session );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// DomainResultProducer
@Override
public void applySqlSelections(DomainResultCreationState creationState) {
createSqlSelection( creationState );
}
@Override
public DomainResult<Object> createDomainResult(String resultVariable, DomainResultCreationState creationState) {
return new BasicResult<>(
createSqlSelection( creationState ).getValuesArrayPosition(),
resultVariable,
basicType
);
}
private SqlSelection createSqlSelection(DomainResultCreationState creationState) {
return creationState.getSqlAstCreationState().getSqlExpressionResolver().resolveSqlSelection(
this,
basicType.getJdbcJavaType(),
null,
creationState.getSqlAstCreationState().getCreationContext().getMappingMetamodel().getTypeConfiguration()
);
}
@Override
public void accept(SqlAstWalker sqlTreeWalker) {
sqlTreeWalker.visitEmbeddableTypeLiteral( this );
}
@Override
public JavaType getExpressibleJavaType() {
return basicType.getExpressibleJavaType();
}
}

View File

@ -800,8 +800,8 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
final EmbeddableRepresentationStrategy representationStrategy = embeddableTypeDescriptor().getRepresentationStrategy();
final EmbeddableInstantiator instantiator;
if ( embeddableTypeDescriptor().isPolymorphic() ) {
// the discriminator here is the composite class name because it gets converted to the domain type when extracted
instantiator = representationStrategy.getInstantiatorForClass( (String) value[value.length - 1] );
// the discriminator here is the composite class because it gets converted to the domain type when extracted
instantiator = representationStrategy.getInstantiatorForClass( ( (Class<?>) value[value.length - 1] ).getName() );
}
else {
instantiator = representationStrategy.getInstantiator();

View File

@ -6,6 +6,8 @@
*/
package org.hibernate.orm.test.inheritance.embeddable;
import org.hibernate.annotations.Imported;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Embeddable;

View File

@ -6,6 +6,8 @@
*/
package org.hibernate.orm.test.inheritance.embeddable;
import org.hibernate.annotations.Imported;
import jakarta.persistence.Embeddable;
/**

View File

@ -106,6 +106,46 @@ public class ElementCollectionEmbeddableInheritanceTest {
} );
}
@Test
public void testType(SessionFactoryScope scope) {
scope.inTransaction( session -> {
assertThat( session.createQuery(
"select type(t.embeddables) from TestEntity t where id = 2",
Class.class
).getSingleResult() ).isEqualTo( SubChildOneEmbeddable.class );
assertThat( session.createQuery(
"select t.id from TestEntity t where type(t.embeddables) = SubChildOneEmbeddable",
Long.class
).getSingleResult() ).isEqualTo( 2L );
assertThat( session.createQuery(
"select type(e) from TestEntity t join t.embeddables e where id = 2",
Class.class
).getSingleResult() ).isEqualTo( SubChildOneEmbeddable.class );
assertThat( session.createQuery(
"select t.id from TestEntity t join t.embeddables e where type(e) = SubChildOneEmbeddable",
Long.class
).getSingleResult() ).isEqualTo( 2L );
} );
}
@Test
public void testTreat(SessionFactoryScope scope) {
scope.inTransaction( session -> {
assertThat( session.createQuery(
"select treat(e as SubChildOneEmbeddable) from TestEntity t join t.embeddables e",
SubChildOneEmbeddable.class
).getSingleResult().getSubChildOneProp() ).isEqualTo( 2.0 );
assertThat( session.createQuery(
"select t.id from TestEntity t join t.embeddables e where treat(e as SubChildOneEmbeddable).subChildOneProp = 2.0",
Long.class
).getSingleResult() ).isEqualTo( 2L );
assertThat( session.createQuery(
"select t.id from TestEntity t join treat(t.embeddables as SubChildOneEmbeddable) e where e.subChildOneProp = 2.0",
Long.class
).getSingleResult() ).isEqualTo( 2L );
} );
}
@BeforeAll
public void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {

View File

@ -0,0 +1,219 @@
/*
* 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.inheritance.embeddable;
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.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Marco Belladelli
*/
@DomainModel( annotatedClasses = {
EmbeddableTypeAndTreatTest.TestEntity.class,
SimpleEmbeddable.class,
ParentEmbeddable.class,
ChildOneEmbeddable.class,
SubChildOneEmbeddable.class,
ChildTwoEmbeddable.class,
} )
@SessionFactory( useCollectingStatementInspector = true )
public class EmbeddableTypeAndTreatTest {
@Test
public void testType(SessionFactoryScope scope) {
scope.inTransaction( session -> {
assertThat( session.createQuery(
"select type(t.embeddable) from TestEntity t where t.id = 1",
Class.class
).getSingleResult() ).isEqualTo( ChildTwoEmbeddable.class );
assertThat( session.createQuery(
"select type(e) from TestEntity t join t.embeddable e where t.id = 2",
Class.class
).getSingleResult() ).isEqualTo( SubChildOneEmbeddable.class );
assertThat( session.createQuery(
"select t.id from TestEntity t where type(t.embeddable) = SubChildOneEmbeddable",
Long.class
).getSingleResult() ).isEqualTo( 2L );
assertThat( session.createQuery(
"select t.id from TestEntity t join t.embeddable e where type(e) = ChildTwoEmbeddable",
Long.class
).getSingleResult() ).isEqualTo( 1L );
assertThat( session.createQuery(
"select t.id from TestEntity t where type(t.embeddable) = SubChildOneEmbeddable or t.id = 1",
Long.class
).getResultList() ).hasSize( 2 );
assertThat( session.createQuery(
"select t.id from TestEntity t where type(t.embeddable) = SubChildOneEmbeddable and type(t.embeddable) = ChildTwoEmbeddable",
Long.class
).getResultList() ).hasSize( 0 );
} );
}
@Test
public void testTreat(SessionFactoryScope scope) {
scope.inTransaction( session -> {
assertThat( session.createQuery(
"select treat(t.embeddable as SubChildOneEmbeddable) from TestEntity t",
SubChildOneEmbeddable.class
).getSingleResult().getSubChildOneProp() ).isEqualTo( 2.0 );
assertThat( session.createQuery(
"select treat(e as SubChildOneEmbeddable) from TestEntity t join t.embeddable e",
SubChildOneEmbeddable.class
).getSingleResult().getSubChildOneProp() ).isEqualTo( 2.0 );
assertThat( session.createQuery(
"select e from TestEntity t join treat(t.embeddable as SubChildOneEmbeddable) e",
SubChildOneEmbeddable.class
).getSingleResult().getSubChildOneProp() ).isEqualTo( 2.0 );
assertThat( session.createQuery(
"select t.id from TestEntity t where treat(t.embeddable as ChildTwoEmbeddable).childTwoProp = 1",
Long.class
).getSingleResult() ).isEqualTo( 1L );
assertThat( session.createQuery(
"select t.id from TestEntity t join t.embeddable e where treat(e as ChildTwoEmbeddable).childTwoProp = 1",
Long.class
).getSingleResult() ).isEqualTo( 1L );
assertThat( session.createQuery(
"select t.id from TestEntity t join treat(t.embeddable as ChildTwoEmbeddable) e where e.childTwoProp = 1",
Long.class
).getSingleResult() ).isEqualTo( 1L );
} );
}
@Test
public void testTreatJunctions(SessionFactoryScope scope) {
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
inspector.clear();
scope.inTransaction( session -> {
assertThat( session.createQuery(
"select t.id from TestEntity t where treat(t.embeddable as SubChildOneEmbeddable).subChildOneProp = 2.0 and id = 2",
Long.class
).getSingleResult() ).isEqualTo( 2L );
inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "embeddable_type", 1 );
inspector.clear();
assertThat( session.createQuery(
"select t.id from TestEntity t where treat(t.embeddable as SubChildOneEmbeddable).subChildOneProp = 2.0 or id = 1",
Long.class
).getResultList() ).hasSize( 2 );
inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "embeddable_type", 1 );
inspector.clear();
assertThat( session.createQuery(
"select t.id from TestEntity t where treat(t.embeddable as SubChildOneEmbeddable).subChildOneProp = 2.0 and treat(t.embeddable as SubChildOneEmbeddable).childOneProp = 2",
Long.class
).getSingleResult() ).isEqualTo( 2L );
inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "embeddable_type", 1 );
inspector.clear();
assertThat( session.createQuery(
"select t.id from TestEntity t where treat(t.embeddable as SubChildOneEmbeddable).subChildOneProp = 2.0 and id = 1",
Long.class
).getResultList() ).hasSize( 0 );
inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "embeddable_type", 1 );
inspector.clear();
assertThat( session.createQuery(
"select t.id from TestEntity t where id = 1 or treat(t.embeddable as SubChildOneEmbeddable).subChildOneProp = 2.0",
Long.class
).getResultList() ).hasSize( 2 );
inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "embeddable_type", 1 );
inspector.clear();
assertThat( session.createQuery(
"select t.id from TestEntity t where treat(t.embeddable as ChildTwoEmbeddable).childTwoProp = 1 or treat(t.embeddable as SubChildOneEmbeddable).subChildOneProp = 2.0",
Long.class
).getResultList() ).hasSize( 2 );
inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "embeddable_type", 2 );
inspector.clear();
assertThat( session.createQuery(
"select t.id from TestEntity t where treat(t.embeddable as SubChildOneEmbeddable).childOneProp = 2 or treat(t.embeddable as SubChildOneEmbeddable).subChildOneProp = 2.0",
Long.class
).getSingleResult() ).isEqualTo( 2L );
inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "embeddable_type", 1 );
inspector.clear();
} );
}
@Test
public void testNonInheritedEmbeddable(SessionFactoryScope scope) {
scope.inTransaction( session -> {
assertThat( session.createQuery(
"select type(t.simpleEmbeddable) from TestEntity t where t.id = 1",
Class.class
).getSingleResult() ).isEqualTo( SimpleEmbeddable.class );
assertThat( session.createQuery(
"select t.id from TestEntity t where type(t.simpleEmbeddable) = SimpleEmbeddable",
Long.class
).getResultList() ).hasSize( 3 );
assertThat( session.createQuery(
"select treat(t.simpleEmbeddable as SimpleEmbeddable) from TestEntity t where t.simpleEmbeddable is not null",
SimpleEmbeddable.class
).getSingleResult().getData() ).isEqualTo( "simple_embeddable_1" );
} );
}
@BeforeAll
public void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final TestEntity testEntity = new TestEntity( 1L, new ChildTwoEmbeddable( "embeddable_1", 1L ) );
testEntity.setSimpleEmbeddable( new SimpleEmbeddable( "simple_embeddable_1" ) );
session.persist( testEntity );
session.persist( new TestEntity( 2L, new SubChildOneEmbeddable( "embeddable_2", 2, 2.0 ) ) );
session.persist( new TestEntity( 3L, new ChildOneEmbeddable( "embeddable_3", 3 ) ) );
} );
}
@AfterAll
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction( session -> session.createMutationQuery( "delete from TestEntity" ).executeUpdate() );
}
@Entity( name = "TestEntity" )
static class TestEntity {
@Id
private Long id;
@Embedded
private ParentEmbeddable embeddable;
@Embedded
private SimpleEmbeddable simpleEmbeddable;
public TestEntity() {
}
public TestEntity(Long id, ParentEmbeddable embeddable) {
this.id = id;
this.embeddable = embeddable;
}
public Long getId() {
return id;
}
public ParentEmbeddable getEmbeddable() {
return embeddable;
}
public void setEmbeddable(ParentEmbeddable embeddable) {
this.embeddable = embeddable;
}
public SimpleEmbeddable getSimpleEmbeddable() {
return simpleEmbeddable;
}
public void setSimpleEmbeddable(SimpleEmbeddable simpleEmbeddable) {
this.simpleEmbeddable = simpleEmbeddable;
}
}
}

View File

@ -0,0 +1,30 @@
/*
* 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.inheritance.embeddable;
import org.hibernate.annotations.Imported;
import jakarta.persistence.Embeddable;
/**
* @author Marco Belladelli
*/
@Embeddable
public class SimpleEmbeddable {
private String data;
public SimpleEmbeddable() {
}
public SimpleEmbeddable(String data) {
this.data = data;
}
public String getData() {
return data;
}
}

View File

@ -6,6 +6,8 @@
*/
package org.hibernate.orm.test.inheritance.embeddable;
import org.hibernate.annotations.Imported;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Embeddable;

View File

@ -26,6 +26,7 @@ public class MetadataCopyingTest {
existingInstance.getEntityBindingMap(),
existingInstance.getComposites(),
existingInstance.getGenericComponentsMap(),
existingInstance.getEmbeddableDiscriminatorTypesMap(),
existingInstance.getMappedSuperclassMap(),
existingInstance.getCollectionBindingMap(),
existingInstance.getTypeDefinitionMap(),

View File

@ -57,7 +57,6 @@ Support for `array_contains()` to accept an array as element argument is depreca
To check if an array is a subset of another array, use the `array_includes()` function,
or the new `INCLUDES` predicate i.e. `array INCLUDES subarray`.
[[merge-versioned-deleted]]
=== Merge versioned entity when row is deleted
Previously, merging a detached entity resulted in a SQL `insert` whenever there was no matching row in the database (for example, if the object had been deleted in another transaction).
@ -70,3 +69,11 @@ For this determination to be possible, the entity must have either:
- a non-primitive `@Version` field.
For entities which have neither, it's impossible to distinguish a new instance from a deleted detached instance, and there is no change from the previous behavior.
[[embeddable-treated-paths]]
== Changes to the `SqmTreatedPath` interface
ORM 6.6 introduced support for `@Embeddable` type inheritance.
With it, we also enabled the `type()` and `treat()` functions to work with embeddable-typed paths.
As a consequence, the `SqmTreatedPath#getTreatTarget()` method will now return a generic `ManagedDomainType` object,
which could in turn be an `EntityDomainType` (as it was before) or also an `EmbeddableDomainType` instance.

View File

@ -58,6 +58,7 @@ import org.hibernate.metamodel.internal.MetadataContext;
import org.hibernate.metamodel.internal.RuntimeMetamodelsImpl;
import org.hibernate.metamodel.mapping.JdbcMapping;
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.ManagedDomainType;
import org.hibernate.metamodel.model.domain.PersistentAttribute;
@ -201,6 +202,7 @@ public abstract class MockSessionFactory
emptyMap(),
emptyMap(),
emptyMap(),
emptyMap(),
new Database(this, MockJdbcServicesInitiator.jdbcServices.getJdbcEnvironment()),
this
);