6 - SQM based on JPA type system

- SQM tests
This commit is contained in:
Steve Ebersole 2019-07-26 13:24:51 -05:00 committed by Andrea Boriero
parent 21f4cfb891
commit 1003ddf115
34 changed files with 1441 additions and 716 deletions

View File

@ -12,6 +12,6 @@
public enum ValueClassification {
BASIC,
ANY,
EMBEDDED,
EMBEDDABLE,
ENTITY
}

View File

@ -0,0 +1,32 @@
/*
* 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.internal;
import org.hibernate.mapping.Property;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
/**
* Bundle's a Hibernate property mapping together with the JPA metamodel information
* of the attribute owner.
*
* @param <X> The owner type.
*/
public interface AttributeContext<X> {
/**
* Retrieve the attribute owner.
*
* @return The owner.
*/
ManagedDomainType<X> getOwnerType();
/**
* Retrieve the Hibernate property mapping.
*
* @return The Hibernate property mapping.
*/
Property getPropertyMapping();
}

View File

@ -10,13 +10,10 @@
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Iterator;
import javax.persistence.ManyToMany;
import javax.persistence.OneToOne;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.PluralAttribute;
import javax.persistence.metamodel.Type;
import org.hibernate.annotations.common.AssertionFailure;
@ -38,7 +35,6 @@
import org.hibernate.metamodel.model.domain.IdentifiableDomainType;
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.SimpleDomainType;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.metamodel.model.domain.internal.EmbeddableTypeImpl;
@ -84,8 +80,15 @@ public AttributeFactory(MetadataContext context) {
*
* @return The built attribute descriptor or null if the attribute is not part of the JPA 2 model (eg backrefs)
*/
@SuppressWarnings({"unchecked"})
public <X, Y> PersistentAttribute<X, Y> buildAttribute(ManagedDomainType<X> ownerType, Property property) {
return buildAttribute( ownerType, property, context );
}
@SuppressWarnings({"unchecked"})
public static <X, Y> PersistentAttribute<X, Y> buildAttribute(
ManagedDomainType<X> ownerType,
Property property,
MetadataContext metadataContext) {
if ( property.isSynthetic() ) {
// hide synthetic/virtual properties (fabricated by Hibernate) from the JPA metamodel.
LOG.tracef( "Skipping synthetic property %s(%s)", ownerType.getTypeName(), property.getName() );
@ -93,15 +96,19 @@ public <X, Y> PersistentAttribute<X, Y> buildAttribute(ManagedDomainType<X> owne
}
LOG.trace( "Building attribute [" + ownerType.getTypeName() + "." + property.getName() + "]" );
final AttributeContext<X> attributeContext = wrap( ownerType, property );
final AttributeMetadata<X, Y> attributeMetadata = determineAttributeMetadata( attributeContext, normalMemberResolver );
final AttributeMetadata<X, Y> attributeMetadata = determineAttributeMetadata(
attributeContext,
normalMemberResolver,
metadataContext
);
if ( attributeMetadata == null ) {
return null;
}
if ( attributeMetadata.isPlural() ) {
return buildPluralAttribute( (PluralAttributeMetadata) attributeMetadata );
return PluralAttributeBuilder.build( (PluralAttributeMetadata) attributeMetadata, metadataContext );
}
final SingularAttributeMetadata<X, Y> singularAttributeMetadata = (SingularAttributeMetadata<X, Y>) attributeMetadata;
final SimpleDomainType<Y> metaModelType = determineSimpleType( singularAttributeMetadata.getValueContext() );
final SimpleDomainType<Y> metaModelType = determineSimpleType( singularAttributeMetadata.getValueContext(), metadataContext );
return new SingularAttributeImpl(
ownerType,
attributeMetadata.getName(),
@ -114,7 +121,7 @@ public <X, Y> PersistentAttribute<X, Y> buildAttribute(ManagedDomainType<X> owne
);
}
private <X> AttributeContext<X> wrap(final ManagedDomainType<X> ownerType, final Property property) {
private static <X> AttributeContext<X> wrap(final ManagedDomainType<X> ownerType, final Property property) {
return new AttributeContext<X>() {
public ManagedDomainType<X> getOwnerType() {
return ownerType;
@ -186,42 +193,12 @@ public <X, Y> SingularAttributeImpl<X, Y> buildVersionAttribute(
);
}
@SuppressWarnings("unchecked")
private <X, Y, E, K> PluralPersistentAttribute<X,Y,E> buildPluralAttribute(PluralAttributeMetadata<X, Y, E> attributeMetadata) {
final JavaTypeDescriptor<Y> javaTypeDescriptor = context
.getTypeConfiguration()
.getJavaTypeDescriptorRegistry()
.getDescriptor( attributeMetadata.getJavaType() );
final PluralAttributeBuilder info = new PluralAttributeBuilder(
attributeMetadata.getOwnerType(),
determineSimpleType( attributeMetadata.getElementValueContext() ),
javaTypeDescriptor,
determineListIndexOrMapKeyType( attributeMetadata )
);
return info
.member( attributeMetadata.getMember() )
.property( attributeMetadata.getPropertyMapping() )
.persistentAttributeClassification( attributeMetadata.getAttributeClassification() )
.build();
}
private <X, Y, E> SimpleDomainType determineListIndexOrMapKeyType(PluralAttributeMetadata<X, Y, E> attributeMetadata) {
if ( java.util.Map.class.isAssignableFrom( attributeMetadata.getJavaType() ) ) {
return determineSimpleType( attributeMetadata.getMapKeyValueContext() );
}
if ( java.util.List.class.isAssignableFrom( attributeMetadata.getJavaType() ) ) {
}
return java.util.Map.class.isAssignableFrom( attributeMetadata.getJavaType() )
? determineSimpleType( attributeMetadata.getMapKeyValueContext() )
: null;
}
@SuppressWarnings("unchecked")
private <Y> SimpleDomainType<Y> determineSimpleType(ValueContext typeContext) {
return determineSimpleType( typeContext, context );
}
@SuppressWarnings("unchecked")
public static <Y> SimpleDomainType<Y> determineSimpleType(ValueContext typeContext, MetadataContext context) {
switch ( typeContext.getValueClassification() ) {
case BASIC: {
return context.resolveBasicType( typeContext.getJpaBindableType() );
@ -285,7 +262,7 @@ private <Y> SimpleDomainType<Y> determineSimpleType(ValueContext typeContext) {
final Iterator<Property> subProperties = component.getPropertyIterator();
while ( subProperties.hasNext() ) {
final Property property = subProperties.next();
final PersistentAttribute<Y, Object> attribute = buildAttribute( embeddableType, property );
final PersistentAttribute<Y, Y> attribute = buildAttribute( embeddableType, property, context );
if ( attribute != null ) {
inFlightAccess.addAttribute( attribute );
}
@ -301,16 +278,22 @@ private <Y> SimpleDomainType<Y> determineSimpleType(ValueContext typeContext) {
}
private EntityMetamodel getDeclarerEntityMetamodel(AbstractIdentifiableType<?> ownerType) {
return getDeclarerEntityMetamodel( ownerType, context );
}
private static EntityMetamodel getDeclarerEntityMetamodel(
AbstractIdentifiableType<?> ownerType,
MetadataContext metadataContext) {
final Type.PersistenceType persistenceType = ownerType.getPersistenceType();
if ( persistenceType == Type.PersistenceType.ENTITY ) {
return context.getMetamodel()
return metadataContext.getMetamodel()
.getEntityDescriptor( ownerType.getTypeName() )
.getEntityMetamodel();
}
else if ( persistenceType == Type.PersistenceType.MAPPED_SUPERCLASS ) {
PersistentClass persistentClass =
context.getPersistentClassHostingProperties( (MappedSuperclassTypeImpl<?>) ownerType );
return context.getMetamodel()
metadataContext.getPersistentClassHostingProperties( (MappedSuperclassTypeImpl<?>) ownerType );
return metadataContext.getMetamodel()
.findEntityDescriptor( persistentClass.getClassName() )
.getEntityMetamodel();
}
@ -319,159 +302,6 @@ else if ( persistenceType == Type.PersistenceType.MAPPED_SUPERCLASS ) {
}
}
/**
* A contract for defining the meta information about a {@link Value}
*/
private interface ValueContext {
/**
* Enum of the simplified types a value might be. These relate more to the Hibernate classification
* then the JPA classification
*/
enum ValueClassification {
EMBEDDABLE,
ENTITY,
BASIC
}
ValueClassification getValueClassification();
Value getHibernateValue();
Class getJpaBindableType();
AttributeMetadata getAttributeMetadata();
}
/**
* Basic contract for describing an attribute.
*
* @param <X> The attribute owner type
* @param <Y> The attribute type.
*/
private interface AttributeMetadata<X, Y> {
/**
* Retrieve the name of the attribute
*
* @return The attribute name
*/
String getName();
/**
* Retrieve the member defining the attribute
*
* @return The attribute member
*/
Member getMember();
/**
* Retrieve the attribute java type.
*
* @return The java type of the attribute.
*/
Class<Y> getJavaType();
/**
* Get the classification for this attribute
*/
AttributeClassification getAttributeClassification();
/**
* Retrieve the attribute owner's metamodel information
*
* @return The metamodel information for the attribute owner
*/
ManagedDomainType<X> getOwnerType();
/**
* Retrieve the Hibernate property mapping related to this attribute.
*
* @return The Hibernate property mapping
*/
Property getPropertyMapping();
/**
* Is the attribute plural (a collection)?
*
* @return True if it is plural, false otherwise.
*/
boolean isPlural();
}
/**
* Attribute metadata contract for a non-plural attribute.
*
* @param <X> The owner type
* @param <Y> The attribute type
*/
private interface SingularAttributeMetadata<X, Y> extends AttributeMetadata<X, Y> {
/**
* Retrieve the value context for this attribute
*
* @return The attributes value context
*/
ValueContext getValueContext();
}
/**
* Attribute metadata contract for a plural attribute.
*
* @param <X> The owner type
* @param <Y> The attribute type (the collection type)
* @param <E> The collection element type
*/
@SuppressWarnings("UnusedDeclaration")
private interface PluralAttributeMetadata<X, Y, E> extends AttributeMetadata<X, Y> {
/**
* Retrieve the JPA collection type classification for this attribute
*
* @return The JPA collection type classification
*/
PluralAttribute.CollectionType getAttributeCollectionType();
/**
* Retrieve the value context for the collection's elements.
*
* @return The value context for the collection's elements.
*/
ValueContext getElementValueContext();
/**
* Retrieve the value context for the collection's keys (if a map, null otherwise).
*
* @return The value context for the collection's keys (if a map, null otherwise).
*/
ValueContext getMapKeyValueContext();
}
/**
* Bundle's a Hibernate property mapping together with the JPA metamodel information
* of the attribute owner.
*
* @param <X> The owner type.
*/
private interface AttributeContext<X> {
/**
* Retrieve the attribute owner.
*
* @return The owner.
*/
ManagedDomainType<X> getOwnerType();
/**
* Retrieve the Hibernate property mapping.
*
* @return The Hibernate property mapping.
*/
Property getPropertyMapping();
}
/**
* Contract for how we resolve the {@link Member} for a give attribute context.
*/
private interface MemberResolver {
Member resolveMember(AttributeContext attributeContext);
}
/**
* Here is most of the nuts and bolts of this factory, where we interpret the known JPA metadata
* against the known Hibernate metadata and build a descriptor for the attribute.
@ -487,12 +317,19 @@ private interface MemberResolver {
private <X, Y> AttributeMetadata<X, Y> determineAttributeMetadata(
AttributeContext<X> attributeContext,
MemberResolver memberResolver) {
return determineAttributeMetadata( attributeContext, memberResolver, context );
}
private static <X, Y> AttributeMetadata<X, Y> determineAttributeMetadata(
AttributeContext<X> attributeContext,
MemberResolver memberResolver,
MetadataContext context) {
final Property propertyMapping = attributeContext.getPropertyMapping();
final String propertyName = propertyMapping.getName();
LOG.trace( "Starting attribute metadata determination [" + propertyName + "]" );
final Member member = memberResolver.resolveMember( attributeContext );
final Member member = memberResolver.resolveMember( attributeContext, context );
LOG.trace( " Determined member [" + member + "]" );
final Value value = propertyMapping.getValue();
@ -504,7 +341,8 @@ private <X, Y> AttributeMetadata<X, Y> determineAttributeMetadata(
propertyMapping,
attributeContext.getOwnerType(),
member,
AttributeClassification.ANY
AttributeClassification.ANY,
context
);
}
else if ( type.isAssociationType() ) {
@ -515,7 +353,8 @@ else if ( type.isAssociationType() ) {
propertyMapping,
attributeContext.getOwnerType(),
member,
determineSingularAssociationClassification( member )
determineSingularAssociationClassification( member ),
context
);
}
// collection
@ -580,7 +419,8 @@ else if ( value instanceof List ) {
member,
attributeClassification,
elementClassification,
indexClassification
indexClassification,
context
);
}
else if ( value instanceof OneToMany ) {
@ -609,7 +449,8 @@ else if ( propertyMapping.isComposite() ) {
propertyMapping,
attributeContext.getOwnerType(),
member,
AttributeClassification.EMBEDDED
AttributeClassification.EMBEDDED,
context
);
}
else {
@ -618,7 +459,8 @@ else if ( propertyMapping.isComposite() ) {
propertyMapping,
attributeContext.getOwnerType(),
member,
AttributeClassification.BASIC
AttributeClassification.BASIC,
context
);
}
throw new UnsupportedOperationException( "oops, we are missing something: " + propertyMapping );
@ -640,85 +482,11 @@ else if ( member instanceof MapMember ) {
}
}
private abstract class BaseAttributeMetadata<X, Y> implements AttributeMetadata<X, Y> {
private final Property propertyMapping;
private final ManagedDomainType<X> ownerType;
private final Member member;
private final Class<Y> javaType;
private final AttributeClassification attributeClassification;
@SuppressWarnings({"unchecked"})
protected BaseAttributeMetadata(
Property propertyMapping,
ManagedDomainType<X> ownerType,
Member member,
AttributeClassification attributeClassification) {
this.propertyMapping = propertyMapping;
this.ownerType = ownerType;
this.member = member;
this.attributeClassification = attributeClassification;
final Class declaredType;
if ( member == null ) {
// assume we have a MAP entity-mode "class"
declaredType = propertyMapping.getType().getReturnedClass();
}
else if ( Field.class.isInstance( member ) ) {
declaredType = ( (Field) member ).getType();
}
else if ( Method.class.isInstance( member ) ) {
declaredType = ( (Method) member ).getReturnType();
}
else if ( MapMember.class.isInstance( member ) ) {
declaredType = ( (MapMember) member ).getType();
}
else {
throw new IllegalArgumentException( "Cannot determine java-type from given member [" + member + "]" );
}
this.javaType = accountForPrimitiveTypes( declaredType );
}
public String getName() {
return propertyMapping.getName();
}
public Member getMember() {
return member;
}
public String getMemberDescription() {
return determineMemberDescription( getMember() );
}
public String determineMemberDescription(Member member) {
return member.getDeclaringClass().getName() + '#' + member.getName();
}
public Class<Y> getJavaType() {
return javaType;
}
@Override
public AttributeClassification getAttributeClassification() {
return attributeClassification;
}
public ManagedDomainType<X> getOwnerType() {
return ownerType;
}
public boolean isPlural() {
return propertyMapping.getType().isCollectionType();
}
public Property getPropertyMapping() {
return propertyMapping;
}
protected <Y> Class<Y> accountForPrimitiveTypes(Class<Y> declaredType) {
return accountForPrimitiveTypes( declaredType, context );
}
@SuppressWarnings({"unchecked"})
protected <Y> Class<Y> accountForPrimitiveTypes(Class<Y> declaredType) {
public static <Y> Class<Y> accountForPrimitiveTypes(Class<Y> declaredType, MetadataContext metadataContext) {
// if ( !declaredType.isPrimitive() ) {
// return declaredType;
// }
@ -753,196 +521,12 @@ protected <Y> Class<Y> accountForPrimitiveTypes(Class<Y> declaredType) {
return declaredType;
}
private class SingularAttributeMetadataImpl<X, Y>
extends BaseAttributeMetadata<X, Y>
implements SingularAttributeMetadata<X, Y> {
private final ValueContext valueContext;
private SingularAttributeMetadataImpl(
Property propertyMapping,
ManagedDomainType<X> ownerType,
Member member,
AttributeClassification attributeClassification) {
super( propertyMapping, ownerType, member, attributeClassification );
valueContext = new ValueContext() {
public Value getHibernateValue() {
return getPropertyMapping().getValue();
}
public Class getJpaBindableType() {
return getAttributeMetadata().getJavaType();
}
public ValueClassification getValueClassification() {
switch ( attributeClassification ) {
case EMBEDDED: {
return ValueClassification.EMBEDDABLE;
}
case BASIC: {
return ValueClassification.BASIC;
}
default: {
return ValueClassification.ENTITY;
}
}
}
public AttributeMetadata getAttributeMetadata() {
return SingularAttributeMetadataImpl.this;
}
};
}
public ValueContext getValueContext() {
return valueContext;
}
}
private class PluralAttributeMetadataImpl<X, Y, E>
extends BaseAttributeMetadata<X, Y>
implements PluralAttributeMetadata<X, Y, E> {
private final PluralAttribute.CollectionType attributeCollectionType;
private final AttributeClassification elementClassification;
private final AttributeClassification listIndexOrMapKeyClassification;
private final Class elementJavaType;
private final Class keyJavaType;
private final ValueContext elementValueContext;
private final ValueContext keyValueContext;
private PluralAttributeMetadataImpl(
Property propertyMapping,
ManagedDomainType<X> ownerType,
Member member,
AttributeClassification attributeClassification,
AttributeClassification elementClassification,
AttributeClassification listIndexOrMapKeyClassification) {
super( propertyMapping, ownerType, member, attributeClassification );
this.attributeCollectionType = determineCollectionType( getJavaType() );
this.elementClassification = elementClassification;
this.listIndexOrMapKeyClassification = listIndexOrMapKeyClassification;
ParameterizedType signatureType = getSignatureType( member );
if ( this.listIndexOrMapKeyClassification == null ) {
elementJavaType = signatureType != null ?
getClassFromGenericArgument( signatureType.getActualTypeArguments()[0] ) :
Object.class; //FIXME and honor targetEntity?
keyJavaType = null;
}
else {
keyJavaType = signatureType != null ?
getClassFromGenericArgument( signatureType.getActualTypeArguments()[0] ) :
Object.class; //FIXME and honor targetEntity?
elementJavaType = signatureType != null ?
getClassFromGenericArgument( signatureType.getActualTypeArguments()[1] ) :
Object.class; //FIXME and honor targetEntity?
}
this.elementValueContext = new ValueContext() {
public Value getHibernateValue() {
return ( (Collection) getPropertyMapping().getValue() ).getElement();
}
public Class getJpaBindableType() {
return elementJavaType;
}
public ValueClassification getValueClassification() {
switch ( PluralAttributeMetadataImpl.this.elementClassification ) {
case EMBEDDED: {
return ValueClassification.EMBEDDABLE;
}
case BASIC: {
return ValueClassification.BASIC;
}
default: {
return ValueClassification.ENTITY;
}
}
}
public AttributeMetadata getAttributeMetadata() {
return PluralAttributeMetadataImpl.this;
}
};
// interpret the key, if one
if ( this.listIndexOrMapKeyClassification != null ) {
this.keyValueContext = new ValueContext() {
public Value getHibernateValue() {
return ( (Map) getPropertyMapping().getValue() ).getIndex();
}
public Class getJpaBindableType() {
return keyJavaType;
}
public ValueClassification getValueClassification() {
switch ( PluralAttributeMetadataImpl.this.listIndexOrMapKeyClassification ) {
case EMBEDDED: {
return ValueClassification.EMBEDDABLE;
}
case BASIC: {
return ValueClassification.BASIC;
}
default: {
return ValueClassification.ENTITY;
}
}
}
public AttributeMetadata getAttributeMetadata() {
return PluralAttributeMetadataImpl.this;
}
};
}
else {
keyValueContext = null;
}
}
private Class<?> getClassFromGenericArgument(java.lang.reflect.Type type) {
if ( type instanceof Class ) {
return (Class) type;
}
else if ( type instanceof TypeVariable ) {
final java.lang.reflect.Type upperBound = ( (TypeVariable) type ).getBounds()[0];
return getClassFromGenericArgument( upperBound );
}
else if ( type instanceof ParameterizedType ) {
final java.lang.reflect.Type rawType = ( (ParameterizedType) type ).getRawType();
return getClassFromGenericArgument( rawType );
}
else if ( type instanceof WildcardType ) {
final java.lang.reflect.Type upperBound = ( (WildcardType) type ).getUpperBounds()[0];
return getClassFromGenericArgument( upperBound );
}
else {
throw new AssertionFailure(
"Fail to process type argument in a generic declaration. Member : " + getMemberDescription()
+ " Type: " + type.getClass()
);
}
}
public ValueContext getElementValueContext() {
return elementValueContext;
}
public PluralAttribute.CollectionType getAttributeCollectionType() {
return attributeCollectionType;
}
public ValueContext getMapKeyValueContext() {
return keyValueContext;
}
}
public static ParameterizedType getSignatureType(Member member) {
final java.lang.reflect.Type type;
if ( Field.class.isInstance( member ) ) {
if ( member instanceof Field ) {
type = ( (Field) member ).getGenericType();
}
else if ( Method.class.isInstance( member ) ) {
else if ( member instanceof Method ) {
type = ( (Method) member ).getGenericReturnType();
}
else {
@ -955,39 +539,18 @@ else if ( Method.class.isInstance( member ) ) {
return (ParameterizedType) type;
}
public static PluralAttribute.CollectionType determineCollectionType(Class javaType) {
if ( java.util.List.class.isAssignableFrom( javaType ) ) {
return PluralAttribute.CollectionType.LIST;
}
else if ( java.util.Set.class.isAssignableFrom( javaType ) ) {
return PluralAttribute.CollectionType.SET;
}
else if ( java.util.Map.class.isAssignableFrom( javaType ) ) {
return PluralAttribute.CollectionType.MAP;
}
else if ( java.util.Collection.class.isAssignableFrom( javaType ) ) {
return PluralAttribute.CollectionType.COLLECTION;
}
else if ( javaType.isArray() ) {
return PluralAttribute.CollectionType.LIST;
}
else {
throw new IllegalArgumentException( "Expecting collection type [" + javaType.getName() + "]" );
}
}
public static boolean isManyToMany(Member member) {
if ( Field.class.isInstance( member ) ) {
if ( member instanceof Field ) {
return ( (Field) member ).getAnnotation( ManyToMany.class ) != null;
}
else if ( Method.class.isInstance( member ) ) {
else if ( member instanceof Method ) {
return ( (Method) member ).getAnnotation( ManyToMany.class ) != null;
}
return false;
}
private final MemberResolver embeddedMemberResolver = attributeContext -> {
private static final MemberResolver embeddedMemberResolver = (attributeContext, metadataContext) -> {
// the owner is an embeddable
final EmbeddableDomainType<?> ownerType = (EmbeddableDomainType) attributeContext.getOwnerType();
@ -1003,9 +566,9 @@ else if ( Method.class.isInstance( member ) ) {
};
private final MemberResolver virtualIdentifierMemberResolver = attributeContext -> {
private static final MemberResolver virtualIdentifierMemberResolver = (attributeContext, metadataContext) -> {
final AbstractIdentifiableType identifiableType = (AbstractIdentifiableType) attributeContext.getOwnerType();
final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( identifiableType );
final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( identifiableType, metadataContext );
if ( !entityMetamodel.getIdentifierProperty().isVirtual() ) {
throw new IllegalArgumentException( "expecting IdClass mapping" );
}
@ -1028,22 +591,22 @@ else if ( Method.class.isInstance( member ) ) {
/**
* A {@link Member} resolver for normal attributes.
*/
private final MemberResolver normalMemberResolver = attributeContext -> {
private static final MemberResolver normalMemberResolver = (attributeContext, metadataContext) -> {
final ManagedDomainType ownerType = attributeContext.getOwnerType();
final Property property = attributeContext.getPropertyMapping();
final Type.PersistenceType persistenceType = ownerType.getPersistenceType();
if ( Type.PersistenceType.EMBEDDABLE == persistenceType ) {
return embeddedMemberResolver.resolveMember( attributeContext );
return embeddedMemberResolver.resolveMember( attributeContext, metadataContext );
}
else if ( Type.PersistenceType.ENTITY == persistenceType
|| Type.PersistenceType.MAPPED_SUPERCLASS == persistenceType ) {
final AbstractIdentifiableType identifiableType = (AbstractIdentifiableType) ownerType;
final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( identifiableType );
final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( identifiableType, metadataContext );
final String propertyName = property.getName();
final Integer index = entityMetamodel.getPropertyIndexOrNull( propertyName );
if ( index == null ) {
// just like in #determineIdentifierJavaMember , this *should* indicate we have an IdClass mapping
return virtualIdentifierMemberResolver.resolveMember( attributeContext );
return virtualIdentifierMemberResolver.resolveMember( attributeContext, metadataContext );
}
else {
final Getter getter = entityMetamodel.getTuplizer().getGetter( index );
@ -1057,13 +620,13 @@ else if ( Type.PersistenceType.ENTITY == persistenceType
}
};
private final MemberResolver identifierMemberResolver = attributeContext -> {
private final MemberResolver identifierMemberResolver = (attributeContext, metadataContext) -> {
final AbstractIdentifiableType identifiableType = (AbstractIdentifiableType) attributeContext.getOwnerType();
final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( identifiableType );
if ( !attributeContext.getPropertyMapping().getName()
.equals( entityMetamodel.getIdentifierProperty().getName() ) ) {
// this *should* indicate processing part of an IdClass...
return virtualIdentifierMemberResolver.resolveMember( attributeContext );
return virtualIdentifierMemberResolver.resolveMember( attributeContext, metadataContext );
}
final Getter getter = entityMetamodel.getTuplizer().getIdentifierGetter();
if ( PropertyAccessMapImpl.GetterImpl.class.isInstance( getter ) ) {
@ -1079,7 +642,9 @@ else if ( Type.PersistenceType.ENTITY == persistenceType
private final MemberResolver versionMemberResolver = new MemberResolver() {
@Override
public Member resolveMember(AttributeContext attributeContext) {
public Member resolveMember(
AttributeContext attributeContext,
MetadataContext metadataContext) {
final AbstractIdentifiableType identifiableType = (AbstractIdentifiableType) attributeContext.getOwnerType();
final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( identifiableType );
final String versionPropertyName = attributeContext.getPropertyMapping().getName();

View File

@ -0,0 +1,69 @@
/*
* 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.internal;
import java.lang.reflect.Member;
import org.hibernate.mapping.Property;
import org.hibernate.metamodel.AttributeClassification;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
/**
* Basic contract for describing an attribute.
*
* @param <X> The attribute owner type
* @param <Y> The attribute type.
*/
public interface AttributeMetadata<X, Y> {
/**
* Retrieve the name of the attribute
*
* @return The attribute name
*/
String getName();
/**
* Retrieve the member defining the attribute
*
* @return The attribute member
*/
Member getMember();
/**
* Retrieve the attribute java type.
*
* @return The java type of the attribute.
*/
Class<Y> getJavaType();
/**
* Get the classification for this attribute
*/
AttributeClassification getAttributeClassification();
/**
* Retrieve the attribute owner's metamodel information
*
* @return The metamodel information for the attribute owner
*/
ManagedDomainType<X> getOwnerType();
/**
* Retrieve the Hibernate property mapping related to this attribute.
*
* @return The Hibernate property mapping
*/
Property getPropertyMapping();
/**
* Is the attribute plural (a collection)?
*
* @return True if it is plural, false otherwise.
*/
boolean isPlural();
}

View File

@ -0,0 +1,97 @@
/*
* 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.internal;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import org.hibernate.mapping.Property;
import org.hibernate.metamodel.AttributeClassification;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.internal.MapMember;
/**
* @author Steve Ebersole
*/
public abstract class BaseAttributeMetadata<X, Y> implements AttributeMetadata<X, Y> {
private final Property propertyMapping;
private final ManagedDomainType<X> ownerType;
private final Member member;
private final Class<Y> javaType;
private final AttributeClassification attributeClassification;
protected BaseAttributeMetadata(
Property propertyMapping,
ManagedDomainType<X> ownerType,
Member member,
AttributeClassification attributeClassification,
MetadataContext metadataContext) {
this.propertyMapping = propertyMapping;
this.ownerType = ownerType;
this.member = member;
this.attributeClassification = attributeClassification;
final Class declaredType;
if ( member == null ) {
// assume we have a MAP entity-mode "class"
declaredType = propertyMapping.getType().getReturnedClass();
}
else if ( member instanceof Field ) {
declaredType = ( (Field) member ).getType();
}
else if ( member instanceof Method ) {
declaredType = ( (Method) member ).getReturnType();
}
else if ( member instanceof MapMember ) {
declaredType = ( (MapMember) member ).getType();
}
else {
throw new IllegalArgumentException( "Cannot determine java-type from given member [" + member + "]" );
}
//noinspection unchecked
this.javaType = AttributeFactory.accountForPrimitiveTypes( declaredType, metadataContext );
}
public String getName() {
return propertyMapping.getName();
}
public Member getMember() {
return member;
}
public String getMemberDescription() {
return determineMemberDescription( getMember() );
}
public String determineMemberDescription(Member member) {
return member.getDeclaringClass().getName() + '#' + member.getName();
}
public Class<Y> getJavaType() {
return javaType;
}
@Override
public AttributeClassification getAttributeClassification() {
return attributeClassification;
}
public ManagedDomainType<X> getOwnerType() {
return ownerType;
}
public boolean isPlural() {
return propertyMapping.getType().isCollectionType();
}
public Property getPropertyMapping() {
return propertyMapping;
}
}

View File

@ -0,0 +1,16 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.metamodel.internal;
import java.lang.reflect.Member;
/**
* Contract for how we resolve the {@link Member} for a give attribute context.
*/
public interface MemberResolver {
Member resolveMember(AttributeContext attributeContext, MetadataContext metadataContext);
}

View File

@ -0,0 +1,41 @@
/*
* 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.internal;
import javax.persistence.metamodel.PluralAttribute;
import org.hibernate.metamodel.CollectionClassification;
/**
* Attribute metadata contract for a plural attribute.
*
* @param <X> The owner type
* @param <Y> The attribute type (the collection type)
* @param <E> The collection element type
*/
@SuppressWarnings("UnusedDeclaration")
public interface PluralAttributeMetadata<X, Y, E> extends AttributeMetadata<X, Y> {
/**
* The classification of the collection, indicating the collection semantics
* to be used.
*/
CollectionClassification getCollectionClassification();
/**
* Retrieve the value context for the collection's elements.
*
* @return The value context for the collection's elements.
*/
ValueContext getElementValueContext();
/**
* Retrieve the value context for the collection's keys (if a map, null otherwise).
*
* @return The value context for the collection's keys (if a map, null otherwise).
*/
ValueContext getMapKeyValueContext();
}

View File

@ -0,0 +1,226 @@
/*
* 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.internal;
import java.lang.reflect.Member;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Map;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.AttributeClassification;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.ValueClassification;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
/**
* @author Steve Ebersole
*/
class PluralAttributeMetadataImpl<X, Y, E>
extends BaseAttributeMetadata<X, Y>
implements PluralAttributeMetadata<X, Y, E> {
private final CollectionClassification collectionClassification;
private final AttributeClassification elementClassification;
private final AttributeClassification listIndexOrMapKeyClassification;
private final Class elementJavaType;
private final Class keyJavaType;
private final ValueContext elementValueContext;
private final ValueContext keyValueContext;
PluralAttributeMetadataImpl(
Property propertyMapping,
ManagedDomainType<X> ownerType,
Member member,
AttributeClassification attributeClassification,
AttributeClassification elementClassification,
AttributeClassification listIndexOrMapKeyClassification,
MetadataContext metadataContext) {
super( propertyMapping, ownerType, member, attributeClassification, metadataContext );
this.collectionClassification = determineCollectionType( getJavaType(), propertyMapping );
this.elementClassification = elementClassification;
this.listIndexOrMapKeyClassification = listIndexOrMapKeyClassification;
final ParameterizedType signatureType = AttributeFactory.getSignatureType( member );
switch ( collectionClassification ) {
case MAP:
case SORTED_MAP:
case ORDERED_MAP: {
this.keyJavaType = signatureType != null
? getClassFromGenericArgument( signatureType.getActualTypeArguments()[0] )
: Object.class;
this.elementJavaType = signatureType != null
? getClassFromGenericArgument( signatureType.getActualTypeArguments()[1] )
: Object.class;
break;
}
case ARRAY:
case LIST: {
this.keyJavaType = Integer.class;
this.elementJavaType = signatureType != null
? getClassFromGenericArgument( signatureType.getActualTypeArguments()[0] )
: Object.class;
break;
}
default: {
this.elementJavaType = signatureType != null
? getClassFromGenericArgument( signatureType.getActualTypeArguments()[0] )
: Object.class;
this.keyJavaType = null;
}
}
this.elementValueContext = new ValueContext() {
public Value getHibernateValue() {
return ( (Collection) getPropertyMapping().getValue() ).getElement();
}
public Class getJpaBindableType() {
return elementJavaType;
}
public ValueClassification getValueClassification() {
switch ( PluralAttributeMetadataImpl.this.elementClassification ) {
case EMBEDDED: {
return ValueClassification.EMBEDDABLE;
}
case BASIC: {
return ValueClassification.BASIC;
}
default: {
return ValueClassification.ENTITY;
}
}
}
public AttributeMetadata getAttributeMetadata() {
return PluralAttributeMetadataImpl.this;
}
};
// interpret the key, if one
if ( this.listIndexOrMapKeyClassification != null ) {
this.keyValueContext = new ValueContext() {
public Value getHibernateValue() {
return ( (Map) getPropertyMapping().getValue() ).getIndex();
}
public Class getJpaBindableType() {
return keyJavaType;
}
public ValueClassification getValueClassification() {
switch ( PluralAttributeMetadataImpl.this.listIndexOrMapKeyClassification ) {
case EMBEDDED: {
return ValueClassification.EMBEDDABLE;
}
case BASIC: {
return ValueClassification.BASIC;
}
default: {
return ValueClassification.ENTITY;
}
}
}
public AttributeMetadata getAttributeMetadata() {
return PluralAttributeMetadataImpl.this;
}
};
}
else {
keyValueContext = null;
}
}
private Class<?> getClassFromGenericArgument(java.lang.reflect.Type type) {
if ( type instanceof Class ) {
return (Class) type;
}
else if ( type instanceof TypeVariable ) {
final java.lang.reflect.Type upperBound = ( (TypeVariable) type ).getBounds()[0];
return getClassFromGenericArgument( upperBound );
}
else if ( type instanceof ParameterizedType ) {
final java.lang.reflect.Type rawType = ( (ParameterizedType) type ).getRawType();
return getClassFromGenericArgument( rawType );
}
else if ( type instanceof WildcardType ) {
final java.lang.reflect.Type upperBound = ( (WildcardType) type ).getUpperBounds()[0];
return getClassFromGenericArgument( upperBound );
}
else {
throw new AssertionFailure(
"Fail to process type argument in a generic declaration. Member : " + getMemberDescription()
+ " Type: " + type.getClass()
);
}
}
public static CollectionClassification determineCollectionType(Class javaType, Property property) {
final Collection collection = (Collection) property.getValue();
if ( java.util.List.class.isAssignableFrom( javaType ) ) {
return CollectionClassification.LIST;
}
else if ( java.util.Set.class.isAssignableFrom( javaType ) ) {
if ( collection.isSorted() ) {
return CollectionClassification.SORTED_SET;
}
if ( collection.hasOrder() ) {
return CollectionClassification.ORDERED_SET;
}
return CollectionClassification.SET;
}
else if ( java.util.Map.class.isAssignableFrom( javaType ) ) {
if ( collection.isSorted() ) {
return CollectionClassification.SORTED_MAP;
}
if ( collection.hasOrder() ) {
return CollectionClassification.ORDERED_MAP;
}
return CollectionClassification.MAP;
}
else if ( java.util.Collection.class.isAssignableFrom( javaType ) ) {
if ( collection.isIdentified() ) {
return CollectionClassification.IDBAG;
}
return CollectionClassification.BAG;
}
else if ( javaType.isArray() ) {
return CollectionClassification.ARRAY;
}
else {
throw new IllegalArgumentException( "Expecting collection type [" + javaType.getName() + "]" );
}
}
public ValueContext getElementValueContext() {
return elementValueContext;
}
@Override
public CollectionClassification getCollectionClassification() {
return collectionClassification;
}
public ValueContext getMapKeyValueContext() {
return keyValueContext;
}
}

View File

@ -0,0 +1,22 @@
/*
* 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.internal;
/**
* Attribute metadata contract for a non-plural attribute.
*
* @param <X> The owner type
* @param <Y> The attribute type
*/
public interface SingularAttributeMetadata<X, Y> extends AttributeMetadata<X, Y> {
/**
* Retrieve the value context for this attribute
*
* @return The attributes value context
*/
ValueContext getValueContext();
}

View File

@ -0,0 +1,63 @@
/*
* 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.internal;
import java.lang.reflect.Member;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.AttributeClassification;
import org.hibernate.metamodel.ValueClassification;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
/**
* @author Steve Ebersole
*/
public class SingularAttributeMetadataImpl<X, Y> extends BaseAttributeMetadata<X, Y>
implements SingularAttributeMetadata<X, Y> {
private final ValueContext valueContext;
SingularAttributeMetadataImpl(
Property propertyMapping,
ManagedDomainType<X> ownerType,
Member member,
AttributeClassification attributeClassification,
MetadataContext metadataContext) {
super( propertyMapping, ownerType, member, attributeClassification, metadataContext );
valueContext = new ValueContext() {
public Value getHibernateValue() {
return getPropertyMapping().getValue();
}
public Class getJpaBindableType() {
return getAttributeMetadata().getJavaType();
}
public ValueClassification getValueClassification() {
switch ( attributeClassification ) {
case EMBEDDED: {
return ValueClassification.EMBEDDABLE;
}
case BASIC: {
return ValueClassification.BASIC;
}
default: {
return ValueClassification.ENTITY;
}
}
}
public AttributeMetadata getAttributeMetadata() {
return SingularAttributeMetadataImpl.this;
}
};
}
public ValueContext getValueContext() {
return valueContext;
}
}

View File

@ -0,0 +1,23 @@
/*
* 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.internal;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.ValueClassification;
/**
* A contract for defining the meta information about a {@link Value}
*/
public interface ValueContext {
ValueClassification getValueClassification();
Value getHibernateValue();
Class getJpaBindableType();
AttributeMetadata getAttributeMetadata();
}

View File

@ -10,12 +10,6 @@
import java.util.Collection;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.ValueClassification;
import org.hibernate.metamodel.model.domain.AbstractManagedType;
import org.hibernate.metamodel.model.domain.AnyMappingDomainType;
import org.hibernate.metamodel.model.domain.BasicDomainType;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.metamodel.model.domain.SimpleDomainType;
import org.hibernate.query.NavigablePath;
@ -37,14 +31,6 @@ public abstract class AbstractPluralAttribute<D,C,E>
extends AbstractAttribute<D,C,E>
implements PluralPersistentAttribute<D,C,E>, Serializable {
public static <X,C,E,K> PluralAttributeBuilder<X,C,E,K> create(
AbstractManagedType<X> ownerType,
SimpleDomainType<E> attrType,
JavaTypeDescriptor<C> collectionClass,
SimpleDomainType<K> listIndexOrMapKeyType) {
return new PluralAttributeBuilder<>( ownerType, attrType, collectionClass, listIndexOrMapKeyType );
}
private final CollectionClassification classification;
private final SqmPathSource<E> elementPathSource;
@ -62,35 +48,12 @@ protected AbstractPluralAttribute(PluralAttributeBuilder<D,C,E,?> builder) {
this.classification = builder.getCollectionClassification();
this.elementPathSource = DomainModelHelper.resolveSqmPathSource(
interpretValueClassification( builder.getValueType() ),
getName(),
builder.getValueType(),
BindableType.PLURAL_ATTRIBUTE
);
}
private ValueClassification interpretValueClassification(SimpleDomainType<E> valueType) {
if ( valueType instanceof BasicDomainType ) {
return ValueClassification.BASIC;
}
if ( valueType instanceof AnyMappingDomainType ) {
return ValueClassification.ANY;
}
if ( valueType instanceof EmbeddableDomainType ) {
return ValueClassification.EMBEDDED;
}
if ( valueType instanceof EntityDomainType ) {
return ValueClassification.ENTITY;
}
throw new IllegalArgumentException(
"Unrecognized value type Java-type [" + valueType.getTypeName() + "] for plural attribute value"
);
}
@Override
public String getPathName() {
return getName();

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.metamodel.model.domain.internal;
import org.hibernate.metamodel.model.domain.AllowableFunctionReturnType;
import org.hibernate.metamodel.model.domain.AllowableParameterType;
import org.hibernate.metamodel.model.domain.BasicDomainType;
import org.hibernate.query.NavigablePath;
@ -18,7 +19,9 @@
/**
* @author Steve Ebersole
*/
public class BasicSqmPathSource<J> extends AbstractSqmPathSource<J> implements AllowableParameterType<J> {
public class BasicSqmPathSource<J>
extends AbstractSqmPathSource<J>
implements AllowableParameterType<J>, AllowableFunctionReturnType<J> {
@SuppressWarnings("WeakerAccess")
public BasicSqmPathSource(
String localPathName,
@ -48,4 +51,14 @@ public SqmPath<J> createSqmPath(SqmPath<?> lhs, SqmCreationState creationState)
creationState.getCreationContext().getNodeBuilder()
);
}
@Override
public PersistenceType getPersistenceType() {
return PersistenceType.BASIC;
}
@Override
public Class<J> getJavaType() {
return getExpressableJavaTypeDescriptor().getJavaType();
}
}

View File

@ -10,7 +10,6 @@
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.ValueClassification;
import org.hibernate.metamodel.model.domain.AnyMappingDomainType;
import org.hibernate.metamodel.model.domain.BasicDomainType;
import org.hibernate.metamodel.model.domain.DomainType;
@ -80,42 +79,44 @@ public static EntityPersister resolveEntityPersister(
}
public static <J> SqmPathSource<J> resolveSqmPathSource(
ValueClassification classification,
String name,
DomainType<J> valueDomainType,
Bindable.BindableType jpaBindableType) {
switch ( classification ) {
case BASIC: {
return new BasicSqmPathSource<>(
name,
(BasicDomainType<J>) valueDomainType,
jpaBindableType
);
}
case ANY: {
return new AnyMappingSqmPathSource<>(
name,
(AnyMappingDomainType<J>) valueDomainType,
jpaBindableType
);
}
case EMBEDDED: {
return new EmbeddedSqmPathSource<>(
name,
(EmbeddableDomainType<J>) valueDomainType,
jpaBindableType
);
}
case ENTITY: {
return new EntitySqmPathSource<>(
name,
(EntityDomainType<J>) valueDomainType,
jpaBindableType
);
}
default: {
throw new IllegalArgumentException( "Unrecognized ValueClassification : " + classification );
}
if ( valueDomainType instanceof BasicDomainType ) {
return new BasicSqmPathSource<>(
name,
(BasicDomainType<J>) valueDomainType,
jpaBindableType
);
}
if ( valueDomainType instanceof AnyMappingDomainType ) {
return new AnyMappingSqmPathSource<>(
name,
(AnyMappingDomainType<J>) valueDomainType,
jpaBindableType
);
}
if ( valueDomainType instanceof EmbeddableDomainType ) {
return new EmbeddedSqmPathSource<>(
name,
(EmbeddableDomainType<J>) valueDomainType,
jpaBindableType
);
}
if ( valueDomainType instanceof EntityDomainType ) {
return new EntitySqmPathSource<>(
name,
(EntityDomainType<J>) valueDomainType,
jpaBindableType
);
}
throw new IllegalArgumentException(
"Unrecognized value type Java-type [" + valueDomainType.getTypeName() + "] for plural attribute value"
);
}
}

View File

@ -10,6 +10,7 @@
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.produce.spi.SqmCreationState;
import org.hibernate.query.sqm.tree.domain.SqmAnyValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
/**
@ -36,7 +37,7 @@ public SqmPathSource<?> findSubPathSource(String name) {
@Override
public SqmPath<J> createSqmPath(SqmPath<?> lhs, SqmCreationState creationState) {
return new SqmAnyValuedSimplePath<>(
return new SqmEmbeddedValuedSimplePath<>(
lhs.getNavigablePath().append( getPathName() ),
this,
lhs,

View File

@ -8,7 +8,6 @@
import java.util.List;
import org.hibernate.metamodel.ValueClassification;
import org.hibernate.metamodel.model.domain.ListPersistentAttribute;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.produce.spi.SqmCreationState;
@ -28,7 +27,6 @@ class ListAttributeImpl<X, E> extends AbstractPluralAttribute<X, List<E>, E> imp
//noinspection unchecked
this.indexPathSource = (SqmPathSource) DomainModelHelper.resolveSqmPathSource(
ValueClassification.BASIC,
getName(),
builder.getListIndexOrMapKeyType(),
BindableType.PLURAL_ATTRIBUTE

View File

@ -9,7 +9,6 @@
import java.util.Map;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.metamodel.ValueClassification;
import org.hibernate.metamodel.model.domain.MapPersistentAttribute;
import org.hibernate.metamodel.model.domain.SimpleDomainType;
import org.hibernate.query.sqm.SqmPathSource;
@ -29,7 +28,6 @@ class MapAttributeImpl<X, K, V> extends AbstractPluralAttribute<X, Map<K, V>, V>
super( xceBuilder );
this.keyPathSource = DomainModelHelper.resolveSqmPathSource(
ValueClassification.BASIC,
getName(),
xceBuilder.getListIndexOrMapKeyType(),
BindableType.PLURAL_ATTRIBUTE
@ -51,6 +49,11 @@ public SqmPathSource getKeyPathSource() {
return keyPathSource;
}
@Override
public SqmPathSource getIndexPathSource() {
return getKeyPathSource();
}
@Override
public SimpleDomainType<K> getKeyType() {
return (SimpleDomainType<K>) keyPathSource.getSqmPathType();

View File

@ -15,35 +15,130 @@
import org.hibernate.mapping.Property;
import org.hibernate.metamodel.AttributeClassification;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.internal.AttributeFactory;
import org.hibernate.metamodel.internal.MetadataContext;
import org.hibernate.metamodel.internal.PluralAttributeMetadata;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.PersistentAttribute;
import org.hibernate.metamodel.model.domain.SimpleDomainType;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import static org.hibernate.metamodel.internal.AttributeFactory.determineSimpleType;
/**
* A "parameter object" for creating a plural attribute
*/
public class PluralAttributeBuilder<D, C, E, K> {
private final JavaTypeDescriptor<C> collectionJtd;
private final AttributeClassification attributeClassification;
private final CollectionClassification collectionClassification;
private final SimpleDomainType<E> elementType;
private final SimpleDomainType<K> listIndexOrMapKeyType;
private final ManagedDomainType<D> declaringType;
private final SimpleDomainType<E> valueType;
private SimpleDomainType<K> listIndexOrMapKeyType;
private AttributeClassification attributeClassification;
private CollectionClassification collectionClassification;
private JavaTypeDescriptor<C> collectionJavaTypeDescriptor;
private Property property;
private Member member;
private final Property property;
private final Member member;
public PluralAttributeBuilder(
ManagedDomainType<D> ownerType,
JavaTypeDescriptor<C> collectionJtd,
AttributeClassification attributeClassification,
CollectionClassification collectionClassification,
SimpleDomainType<E> elementType,
JavaTypeDescriptor<C> collectionJavaTypeDescriptor,
SimpleDomainType<K> listIndexOrMapKeyType) {
this.declaringType = ownerType;
this.valueType = elementType;
this.collectionJavaTypeDescriptor = collectionJavaTypeDescriptor;
SimpleDomainType<K> listIndexOrMapKeyType,
ManagedDomainType<D> declaringType,
Property property,
Member member) {
this.collectionJtd = collectionJtd;
this.attributeClassification = attributeClassification;
this.collectionClassification = collectionClassification;
this.elementType = elementType;
this.listIndexOrMapKeyType = listIndexOrMapKeyType;
this.declaringType = declaringType;
this.property = property;
this.member = member;
}
public static <Y, X> PersistentAttribute<X, Y> build(
PluralAttributeMetadata<?,Y,?> attributeMetadata,
MetadataContext metadataContext) {
final JavaTypeDescriptor<Y> attributeJtd = metadataContext.getTypeConfiguration()
.getJavaTypeDescriptorRegistry()
.getDescriptor( attributeMetadata.getJavaType() );
//noinspection unchecked
final PluralAttributeBuilder builder = new PluralAttributeBuilder(
attributeJtd,
attributeMetadata.getAttributeClassification(),
attributeMetadata.getCollectionClassification(),
AttributeFactory.determineSimpleType(
attributeMetadata.getElementValueContext(),
metadataContext
),
determineListIndexOrMapKeyType( attributeMetadata, metadataContext ),
attributeMetadata.getOwnerType(),
attributeMetadata.getPropertyMapping(),
attributeMetadata.getMember()
);
if ( Map.class.equals( attributeJtd.getJavaType() ) ) {
//noinspection unchecked
return new MapAttributeImpl<>( builder );
}
else if ( Set.class.equals( attributeJtd.getJavaType() ) ) {
//noinspection unchecked
return new SetAttributeImpl<>( builder );
}
else if ( List.class.equals( attributeJtd.getJavaType() ) ) {
//noinspection unchecked
return new ListAttributeImpl<>( builder );
}
else if ( Collection.class.equals( attributeJtd.getJavaType() ) ) {
//noinspection unchecked
return new BagAttributeImpl<>( builder );
}
//apply loose rules
if ( attributeJtd.getJavaType().isArray() ) {
//noinspection unchecked
return new ListAttributeImpl<>( builder );
}
if ( Map.class.isAssignableFrom( attributeJtd.getJavaType() ) ) {
//noinspection unchecked
return new MapAttributeImpl<>( builder );
}
else if ( Set.class.isAssignableFrom( attributeJtd.getJavaType() ) ) {
//noinspection unchecked
return new SetAttributeImpl<>( builder );
}
else if ( List.class.isAssignableFrom( attributeJtd.getJavaType() ) ) {
//noinspection unchecked
return new ListAttributeImpl<>( builder );
}
else if ( Collection.class.isAssignableFrom( attributeJtd.getJavaType() ) ) {
//noinspection unchecked
return new BagAttributeImpl<>( builder );
}
throw new UnsupportedOperationException( "Unknown collection: " + attributeJtd.getJavaType() );
}
private static SimpleDomainType<?> determineListIndexOrMapKeyType(
PluralAttributeMetadata<?,?,?> attributeMetadata,
MetadataContext metadataContext) {
if ( java.util.Map.class.isAssignableFrom( attributeMetadata.getJavaType() ) ) {
return determineSimpleType( attributeMetadata.getMapKeyValueContext(), metadataContext );
}
if ( java.util.List.class.isAssignableFrom( attributeMetadata.getJavaType() ) ) {
return metadataContext.getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( Integer.class );
}
return null;
}
public ManagedDomainType<D> getDeclaringType() {
@ -63,11 +158,11 @@ public SimpleDomainType<K> getListIndexOrMapKeyType() {
}
public JavaTypeDescriptor<C> getCollectionJavaTypeDescriptor() {
return collectionJavaTypeDescriptor;
return collectionJtd;
}
public SimpleDomainType<E> getValueType() {
return valueType;
return elementType;
}
public Property getProperty() {
@ -77,82 +172,4 @@ public Property getProperty() {
public Member getMember() {
return member;
}
public PluralAttributeBuilder<D,C,E,K> member(Member member) {
this.member = member;
return this;
}
public PluralAttributeBuilder<D,C,E,K> property(Property property) {
this.property = property;
return this;
}
public PluralAttributeBuilder<D,C,E,K> persistentAttributeClassification(AttributeClassification classification) {
this.attributeClassification = classification;
return this;
}
@SuppressWarnings( "unchecked" )
public AbstractPluralAttribute<D,C,E> build() {
//apply strict spec rules first
if ( Map.class.equals( collectionJavaTypeDescriptor.getJavaType() ) ) {
final PluralAttributeBuilder<D,Map<K,E>,E,K> builder = (PluralAttributeBuilder<D,Map<K,E>,E,K>) this;
return (AbstractPluralAttribute<D, C, E>) new MapAttributeImpl<>(
builder
);
}
else if ( Set.class.equals( collectionJavaTypeDescriptor.getJavaType() ) ) {
final PluralAttributeBuilder<D,Set<E>, E,?> builder = (PluralAttributeBuilder<D, Set<E>, E,?>) this;
return (AbstractPluralAttribute<D, C, E>) new SetAttributeImpl<>(
builder
);
}
else if ( List.class.equals( collectionJavaTypeDescriptor.getJavaType() ) ) {
final PluralAttributeBuilder<D, List<E>, E,?> builder = (PluralAttributeBuilder<D, List<E>, E,?>) this;
return (AbstractPluralAttribute<D, C, E>) new ListAttributeImpl<>(
builder
);
}
else if ( Collection.class.equals( collectionJavaTypeDescriptor.getJavaType() ) ) {
final PluralAttributeBuilder<D, Collection<E>,E,?> builder = (PluralAttributeBuilder<D, Collection<E>, E,?>) this;
return (AbstractPluralAttribute<D, C, E>) new BagAttributeImpl<>(
builder
);
}
//apply loose rules
if ( collectionJavaTypeDescriptor.getJavaType().isArray() ) {
final PluralAttributeBuilder<D, List<E>, E,?> builder = (PluralAttributeBuilder<D, List<E>, E,?>) this;
return (AbstractPluralAttribute<D, C, E>) new ListAttributeImpl<>(
builder
);
}
if ( Map.class.isAssignableFrom( collectionJavaTypeDescriptor.getJavaType() ) ) {
final PluralAttributeBuilder<D,Map<K,E>,E,K> builder = (PluralAttributeBuilder<D,Map<K,E>,E,K>) this;
return (AbstractPluralAttribute<D, C, E>) new MapAttributeImpl<>(
builder
);
}
else if ( Set.class.isAssignableFrom( collectionJavaTypeDescriptor.getJavaType() ) ) {
final PluralAttributeBuilder<D,Set<E>, E,?> builder = (PluralAttributeBuilder<D, Set<E>, E,?>) this;
return (AbstractPluralAttribute<D, C, E>) new SetAttributeImpl<>(
builder
);
}
else if ( List.class.isAssignableFrom( collectionJavaTypeDescriptor.getJavaType() ) ) {
final PluralAttributeBuilder<D, List<E>, E,?> builder = (PluralAttributeBuilder<D, List<E>, E,?>) this;
return (AbstractPluralAttribute<D, C, E>) new ListAttributeImpl<>(
builder
);
}
else if ( Collection.class.isAssignableFrom( collectionJavaTypeDescriptor.getJavaType() ) ) {
final PluralAttributeBuilder<D, Collection<E>,E,?> builder = (PluralAttributeBuilder<D, Collection<E>, E,?>) this;
return (AbstractPluralAttribute<D, C, E>) new BagAttributeImpl<>(
builder
);
}
throw new UnsupportedOperationException( "Unknown collection: " + collectionJavaTypeDescriptor.getJavaType() );
}
}

View File

@ -12,7 +12,6 @@
import org.hibernate.graph.spi.GraphHelper;
import org.hibernate.metamodel.AttributeClassification;
import org.hibernate.metamodel.ValueClassification;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.SimpleDomainType;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
@ -57,36 +56,12 @@ public SingularAttributeImpl(
this.sqmPathSource = DomainModelHelper.resolveSqmPathSource(
determineValueClassification( attributeClassification ),
name,
attributeType,
BindableType.SINGULAR_ATTRIBUTE
);
}
private static ValueClassification determineValueClassification(AttributeClassification attributeClassification) {
switch ( attributeClassification ) {
case BASIC: {
return ValueClassification.BASIC;
}
case ANY: {
return ValueClassification.ANY;
}
case EMBEDDED: {
return ValueClassification.EMBEDDED;
}
case ONE_TO_ONE:
case MANY_TO_ONE: {
return ValueClassification.ENTITY;
}
default: {
throw new IllegalArgumentException(
"Unrecognized AttributeClassification (for singular attribute): " + attributeClassification
);
}
}
}
@Override
public String getPathName() {
return getName();

View File

@ -1879,7 +1879,7 @@ public SqmExpression visitFloorFunction(HqlParser.FloorFunctionContext ctx) {
}
private SqmFunctionTemplate getFunctionTemplate(String name) {
return creationContext.getQueryEngine().getSqmFunctionRegistry().findFunctionTemplate(name);
return creationContext.getQueryEngine().getSqmFunctionRegistry().findFunctionTemplate( name );
}
@Override
@ -2752,12 +2752,12 @@ private SqmTreatedPath resolveTreatedPath(SqmPath<?> sqmPath, EntityDomainType<?
@Override
public SqmPath<?> visitCollectionElementNavigablePath(HqlParser.CollectionElementNavigablePathContext ctx) {
final SqmPath<?> sqmPath = consumeManagedTypeReference( ctx.path() );
final SqmPathSource<?> referencedPathSource = sqmPath.getReferencedPathSource();
final SqmPath<?> pluralAttributePath = consumeDomainPath( ctx.path() );
final SqmPathSource<?> referencedPathSource = pluralAttributePath.getReferencedPathSource();
if ( !(referencedPathSource instanceof PluralPersistentAttribute ) ) {
throw new PathException(
"Illegal attempt to treat non-plural path as a plural path : " + sqmPath.getNavigablePath()
"Illegal attempt to treat non-plural path as a plural path : " + pluralAttributePath.getNavigablePath()
);
}
@ -2770,7 +2770,7 @@ public SqmPath<?> visitCollectionElementNavigablePath(HqlParser.CollectionElemen
}
SqmPath result = attribute.getElementPathSource().createSqmPath(
sqmPath,
pluralAttributePath,
this
);

View File

@ -9,7 +9,7 @@
import org.hibernate.query.sqm.produce.spi.SqmCreationState;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.domain.SqmNavigableReference;
import org.hibernate.query.sqm.tree.domain.SqmSimplePath;
/**
* @asciidoc
@ -20,7 +20,7 @@
* * class name
* * field name
* * enum name
* * {@link SqmNavigableReference}
* * {@link SqmSimplePath}
*
* @author Steve Ebersole
*/

View File

@ -21,6 +21,8 @@
import org.hibernate.query.sqm.tree.expression.SqmExpression;
/**
* Specialized CASE statement for resolving the first non-null value in a list of values
*
* @author Steve Ebersole
* @author Gavin King
*/

View File

@ -20,7 +20,7 @@
import org.hibernate.query.sqm.mutation.spi.UpdateHandler;
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.query.sqm.tree.domain.SqmNavigableReference;
import org.hibernate.query.sqm.tree.domain.SqmSimplePath;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.predicate.SqmBetweenPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmGroupedPredicate;
@ -197,7 +197,7 @@ public static boolean hasNonIdReferences(SqmPredicate predicate) {
* Is the given `expression` a `SqmNavigableReference` that is also a reference
* to a non-`EntityIdentifier` `Navigable`?
*
* @see SqmNavigableReference
* @see SqmSimplePath
*/
@SuppressWarnings("WeakerAccess")
public static boolean isNonIdentifierReference(SqmExpression expression) {

View File

@ -6,7 +6,10 @@
*/
package org.hibernate.query.sqm.tree.domain;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.BiFunction;
@ -95,6 +98,15 @@ public SqmPath<?> getLhs() {
return lhs;
}
@Override
public List<SqmPath<?>> getImplicitJoinPaths() {
if ( implicitJoinPaths == null ) {
return Collections.emptyList();
}
return new ArrayList<>( implicitJoinPaths.values() );
}
@Override
public void visitImplicitJoinPaths(Consumer<SqmPath<?>> consumer) {
if ( implicitJoinPaths != null ) {

View File

@ -13,7 +13,7 @@
/**
* @author Steve Ebersole
*/
public abstract class AbstractSqmSimplePath<T> extends AbstractSqmPath<T> implements SqmNavigableReference<T> {
public abstract class AbstractSqmSimplePath<T> extends AbstractSqmPath<T> implements SqmSimplePath<T> {
private final NavigablePath navigablePath;
@SuppressWarnings("WeakerAccess")

View File

@ -57,6 +57,10 @@ public String getAlias() {
return explicitAlias;
}
public SqmPath<?> getMapPath() {
return mapPath;
}
@Override
public JpaSelection<Map.Entry<K, V>> alias(String name) {
this.explicitAlias = name;

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.query.sqm.tree.domain;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
@ -63,6 +64,11 @@ public interface SqmPath<T> extends SqmExpression<T>, SemanticPathPart, JpaPath<
*/
SqmPath<?> getLhs();
/**
* Returns an immutable List of implicit-join paths
*/
List<SqmPath<?>> getImplicitJoinPaths();
/**
* Visit each implicit-join path relative to this path
*/

View File

@ -12,5 +12,5 @@
*
* @author Steve Ebersole
*/
public interface SqmNavigableReference<T> extends SqmPath<T> {
public interface SqmSimplePath<T> extends SqmPath<T> {
}

View File

@ -16,7 +16,7 @@
*/
public class SqmTreatedSimplePath<T, S extends T>
extends SqmEntityValuedSimplePath<S>
implements SqmNavigableReference<S>, SqmTreatedPath<T,S> {
implements SqmSimplePath<S>, SqmTreatedPath<T,S> {
private final EntityDomainType<S> treatTarget;
private final SqmPath<T> wrappedPath;

View File

@ -11,7 +11,7 @@
import org.hibernate.orm.test.query.sqm.BaseSqmUnitTest;
import org.hibernate.query.sqm.AliasCollisionException;
import org.hibernate.query.sqm.produce.spi.ImplicitAliasGenerator;
import org.hibernate.query.sqm.tree.domain.SqmNavigableReference;
import org.hibernate.query.sqm.tree.domain.SqmSimplePath;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmInSubQueryPredicate;
@ -143,12 +143,12 @@ public void testSubqueryUsingIdentificationVariableDefinedInRootQuery() {
);
final SqmComparisonPredicate correlation = (SqmComparisonPredicate) subQuerySpec.getWhereClause().getPredicate();
final SqmNavigableReference leftHandExpression = (SqmNavigableReference) correlation.getLeftHandExpression();
final SqmSimplePath leftHandExpression = (SqmSimplePath) correlation.getLeftHandExpression();
assertThat(
leftHandExpression.getLhs().getExplicitAlias(),
is( "a" )
);
final SqmNavigableReference rightHandExpression = (SqmNavigableReference) correlation.getRightHandExpression();
final SqmSimplePath rightHandExpression = (SqmSimplePath) correlation.getRightHandExpression();
assertThat(
rightHandExpression.getLhs().getExplicitAlias(),
is( "b" )

View File

@ -0,0 +1,105 @@
/*
* 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.query.hql;
import java.util.List;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.orm.test.query.sqm.BaseSqmUnitTest;
import org.hibernate.orm.test.query.sqm.domain.Person;
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.query.sqm.tree.select.SqmSelection;
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* @author Steve Ebersole
*/
@SuppressWarnings("WeakerAccess")
public class AttributePathTests extends BaseSqmUnitTest {
@Override
protected Class[] getAnnotatedClasses() {
return new Class[] { Person.class };
}
@Test
public void testImplicitJoinReuse() {
final SqmSelectStatement statement = interpretSelect( "select s.mate.dob, s.mate.numberOfToes from Person s" );
assertThat( statement.getQuerySpec().getFromClause().getRoots().size(), is(1) );
final SqmRoot sqmRoot = statement.getQuerySpec().getFromClause().getRoots().get( 0 );
assertThat( sqmRoot.getJoins().size(), is(0) );
assertThat( sqmRoot.getImplicitJoinPaths().size(), is(1) );
// from-clause paths
// assertPropertyPath( space.getRoot(), "com.acme.Something(s)" );
// assertPropertyPath( space.getJoins().get( 0 ), "com.acme.Something(s).entity" );
final List<SqmSelection> selections = statement.getQuerySpec().getSelectClause().getSelections();
assertThat( selections.size(), is(2) );
// expression paths
assertPropertyPath( (SqmExpression) selections.get( 0 ).getSelectableNode(), Person.class.getName() + "(s).mate.dob" );
assertPropertyPath( (SqmExpression) selections.get( 1 ).getSelectableNode(), Person.class.getName() + "(s).mate.numberOfToes" );
}
private void assertPropertyPath(SqmExpression expression, String expectedFullPath) {
assertThat( expression, instanceOf( SqmSimplePath.class ) );
final SqmSimplePath domainReferenceBinding = (SqmSimplePath) expression;
assertThat( domainReferenceBinding.getNavigablePath().getFullPath(), is( expectedFullPath) );
}
@Test
public void testImplicitJoinReuse2() {
final SqmSelectStatement statement = interpretSelect( "select s.mate from Person s where s.mate.dob = ?1" );
assertThat( statement.getQuerySpec().getFromClause().getRoots().size(), is(1) );
final SqmRoot sqmRoot = statement.getQuerySpec().getFromClause().getRoots().get( 0 );
assertThat( sqmRoot.getJoins().size(), is(0) );
assertThat( sqmRoot.getImplicitJoinPaths().size(), is(1) );
final SqmSelection selection = statement.getQuerySpec().getSelectClause().getSelections().get( 0 );
assertThat( selection.getSelectableNode(), instanceOf( SqmEntityValuedSimplePath.class ) );
final SqmPath selectExpression = (SqmPath) selection.getSelectableNode();
assertThat( selectExpression.getReferencedPathSource().getSqmPathType(), instanceOf( EntityDomainType.class ) );
final SqmComparisonPredicate predicate = (SqmComparisonPredicate) statement.getQuerySpec().getWhereClause().getPredicate();
final SqmPath predicateLhs = (SqmPath) predicate.getLeftHandExpression();
assertThat( predicateLhs.getLhs(), notNullValue() );
// from-clause paths
// assertPropertyPath( space.getRoot(), "com.acme.Something(s)" );
// assertPropertyPath( space.getJoins().get( 0 ), "com.acme.Something(s).entity" );
// expression paths
assertPropertyPath( (SqmExpression) selection.getSelectableNode(), Person.class.getName() + "(s).mate" );
assertPropertyPath( predicateLhs, Person.class.getName() + "(s).mate.dob" );
}
@Test
public void testEntityIdReferences() {
interpretSelect( "select s.mate from Person s where s.id = ?1" );
interpretSelect( "select s.mate from Person s where s.{id} = ?1" );
interpretSelect( "select s.mate from Person s where s.pk = ?1" );
}
}

View File

@ -0,0 +1,127 @@
/*
* 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.query.hql;
import org.hibernate.orm.test.query.sqm.BaseSqmUnitTest;
import org.hibernate.orm.test.query.sqm.domain.Person;
import org.hibernate.query.sqm.function.SqmCoalesce;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.expression.SqmCaseSearched;
import org.hibernate.query.sqm.tree.expression.SqmCaseSimple;
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.query.sqm.tree.select.SqmSelectableNode;
import org.hibernate.testing.orm.junit.TestingUtil;
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hibernate.testing.hamcrest.CollectionMatchers.hasSize;
import static org.junit.Assert.assertEquals;
/**
* @author Steve Ebersole
*/
@SuppressWarnings("WeakerAccess")
public class CaseExpressionsTest extends BaseSqmUnitTest {
@Override
protected Class[] getAnnotatedClasses() {
return new Class[] { Person.class };
}
@Test
public void testBasicSimpleCaseExpression() {
SqmSelectStatement select = interpretSelect(
"select p from Person p where p.numberOfToes = case p.dob when ?1 then 6 else 8 end"
);
final SqmComparisonPredicate predicate = TestingUtil.cast(
select.getQuerySpec().getWhereClause().getPredicate(),
SqmComparisonPredicate.class
);
final SqmCaseSimple caseStatement = TestingUtil.cast(
predicate.getRightHandExpression(),
SqmCaseSimple.class
);
assertThat( caseStatement.getFixture(), notNullValue() );
assertThat( caseStatement.getFixture(), instanceOf( SqmPath.class ) );
assertThat( caseStatement.getOtherwise(), notNullValue() );
assertThat( caseStatement.getOtherwise(), instanceOf( SqmLiteral.class ) );
assertThat( caseStatement.getWhenFragments().size(), is(1) );
}
@Test
public void testBasicSearchedCaseExpression() {
SqmSelectStatement select = interpretSelect(
"select p from Person p where p.numberOfToes = case when p.dob = ?1 then 6 else 8 end"
);
final SqmComparisonPredicate predicate = TestingUtil.cast(
select.getQuerySpec().getWhereClause().getPredicate(),
SqmComparisonPredicate.class
);
final SqmCaseSearched caseStatement = TestingUtil.cast(
predicate.getRightHandExpression(),
SqmCaseSearched.class
);
assertThat( caseStatement.getOtherwise(), notNullValue() );
assertThat( caseStatement.getOtherwise(), instanceOf( SqmLiteral.class ) );
assertThat( caseStatement.getWhenFragments().size(), is(1) );
}
@Test
public void testBasicCoalesceExpression() {
SqmSelectStatement select = interpretSelect(
"select coalesce(p.nickName, p.mate.nickName) from Person p"
);
assertThat( select.getQuerySpec().getSelectClause().getSelections(), hasSize( 1 ) );
final SqmCoalesce<?> coalesce = TestingUtil.cast(
select.getQuerySpec().getSelectClause().getSelections().get( 0 ).getSelectableNode(),
SqmCoalesce.class
);
assertThat( coalesce.getArguments(), hasSize( 2 ) );
assertEquals( coalesce.getJavaTypeDescriptor().getJavaType(), String.class );
}
@Test
public void testBasicNullifExpression() {
SqmSelectStatement select = interpretSelect(
"select nullif(p.nickName, p.mate.nickName) from Person p"
);
assertThat( select.getQuerySpec().getSelectClause().getSelections(), hasSize( 1 ) );
final SqmSelectableNode<?> selectableNode = select.getQuerySpec()
.getSelectClause()
.getSelections()
.get( 0 )
.getSelectableNode();
assertEquals( selectableNode.getJavaTypeDescriptor().getJavaType(), String.class );
// final nullif = TestingUtil.cast(
// selectableNode,
// SqmNullifFunction.class
// );
}
}

View File

@ -0,0 +1,22 @@
/*
* 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.query.hql;
import org.hibernate.orm.test.query.sqm.BaseSqmUnitTest;
import org.hibernate.orm.test.query.sqm.domain.Person;
/**
* @author Steve Ebersole
*/
public class FunctionTests extends BaseSqmUnitTest {
@Override
protected Class[] getAnnotatedClasses() {
return new Class[] { Person.class };
}
}

View File

@ -0,0 +1,322 @@
/*
* 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.query.hql;
import java.util.Map;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.model.domain.BasicDomainType;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.metamodel.model.domain.SimpleDomainType;
import org.hibernate.orm.test.query.sqm.BaseSqmUnitTest;
import org.hibernate.orm.test.query.sqm.domain.Person;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmSimplePath;
import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.query.sqm.tree.select.SqmSelectableNode;
import org.hibernate.query.sqm.tree.select.SqmSelection;
import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
import org.hibernate.testing.orm.domain.gambit.EntityOfLists;
import org.hibernate.testing.orm.domain.gambit.EntityOfMaps;
import org.hibernate.testing.orm.domain.gambit.EntityOfSets;
import org.hibernate.testing.orm.junit.TestingUtil;
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.endsWith;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* Test various forms of selections
*
* @author Steve Ebersole
*/
@SuppressWarnings("WeakerAccess")
public class SelectClauseTests extends BaseSqmUnitTest {
@Test
public void testSimpleAliasSelection() {
SqmSelectStatement statement = interpretSelect( "select p from Person p" );
assertEquals( 1, statement.getQuerySpec().getSelectClause().getSelections().size() );
SqmSelection selection = statement.getQuerySpec().getSelectClause().getSelections().get( 0 );
assertThat( selection.getSelectableNode(), instanceOf( SqmRoot.class ) );
}
@Test
public void testSimpleAttributeSelection() {
SqmSelectStatement statement = interpretSelect( "select p.nickName from Person p" );
assertEquals( 1, statement.getQuerySpec().getSelectClause().getSelections().size() );
SqmSelection selection = statement.getQuerySpec().getSelectClause().getSelections().get( 0 );
assertThat( selection.getSelectableNode(), instanceOf( SqmSimplePath.class ) );
}
@Test
public void testCompoundAttributeSelection() {
SqmSelectStatement statement = interpretSelect( "select p.nickName, p.name.first from Person p" );
assertEquals( 2, statement.getQuerySpec().getSelectClause().getSelections().size() );
assertThat(
statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getSelectableNode(),
instanceOf( SqmSimplePath.class )
);
assertThat(
statement.getQuerySpec().getSelectClause().getSelections().get( 1 ).getSelectableNode(),
instanceOf( SqmSimplePath.class )
);
}
@Test
public void testMixedAliasAndAttributeSelection() {
SqmSelectStatement statement = interpretSelect( "select p, p.nickName from Person p" );
assertEquals( 2, statement.getQuerySpec().getSelectClause().getSelections().size() );
assertThat(
statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getSelectableNode(),
instanceOf( SqmRoot.class )
);
assertThat(
statement.getQuerySpec().getSelectClause().getSelections().get( 1 ).getSelectableNode(),
instanceOf( SqmSimplePath.class )
);
}
@Test
public void testBinaryArithmeticExpression() {
final String query = "select p.numberOfToes + p.numberOfToes as b from Person p";
final SqmSelectStatement selectStatement = interpretSelect( query );
final SqmQuerySpec querySpec = selectStatement.getQuerySpec();
final SqmSelection selection = querySpec.getSelectClause().getSelections().get( 0 );
assertThat( querySpec.getFromClause().getRoots().size(), is(1) );
final SqmRoot root = querySpec.getFromClause().getRoots().get( 0 );
assertThat( root.getEntityName(), endsWith( "Person" ) );
assertThat( root.getJoins().size(), is(0) );
SqmBinaryArithmetic expression = (SqmBinaryArithmetic) selection.getSelectableNode();
SqmPath leftHandOperand = (SqmPath) expression.getLeftHandOperand();
assertThat( leftHandOperand.getLhs(), sameInstance( root ) );
assertThat( leftHandOperand.getReferencedPathSource().getPathName(), is( "numberOfToes" ) );
// assertThat( leftHandOperand.getFromElement(), nullValue() );
SqmPath rightHandOperand = (SqmPath) expression.getRightHandOperand();
assertThat( rightHandOperand.getLhs(), sameInstance( root ) );
assertThat( rightHandOperand.getReferencedPathSource().getPathName(), is( "numberOfToes" ) );
// assertThat( leftHandOperand.getFromElement(), nullValue() );
}
@Test
public void testBinaryArithmeticExpressionWithMultipleFromSpaces() {
final String query = "select p.numberOfToes + p2.numberOfToes as b from Person p, Person p2";
final SqmSelectStatement selectStatement = interpretSelect( query );
final SqmQuerySpec querySpec = selectStatement.getQuerySpec();
final SqmSelection selection = querySpec.getSelectClause().getSelections().get( 0 );
assertThat( querySpec.getFromClause().getRoots().size(), is(2) );
final SqmRoot entityRoot = querySpec.getFromClause().getRoots().get( 0 );
assertThat( entityRoot.getEntityName(), endsWith( "Person" ) );
final SqmRoot entity2Root = querySpec.getFromClause().getRoots().get( 1 );
assertThat( entity2Root.getEntityName(), endsWith( "Person" ) );
SqmBinaryArithmetic addExpression = (SqmBinaryArithmetic) selection.getSelectableNode();
SqmPath leftHandOperand = (SqmPath) addExpression.getLeftHandOperand();
assertThat( leftHandOperand.getLhs(), sameInstance( entityRoot ) );
assertThat( leftHandOperand.getReferencedPathSource().getPathName(), is( "numberOfToes" ) );
SqmPath rightHandOperand = (SqmPath) addExpression.getRightHandOperand();
assertThat( rightHandOperand.getLhs(), sameInstance( entity2Root ) );
assertThat( rightHandOperand.getReferencedPathSource().getPathName(), is( "numberOfToes" ) );
}
@Test
public void testMapKeyFunction() {
collectionIndexFunctionAssertions(
interpretSelect( "select key(m) from EntityOfMaps e join e.basicToBasicMap m" ),
CollectionClassification.MAP,
BasicDomainType.class,
"m"
);
collectionIndexFunctionAssertions(
interpretSelect( "select key(m) from EntityOfMaps e join e.componentToBasicMap m" ),
CollectionClassification.MAP,
EmbeddableDomainType.class,
"m"
);
}
private void collectionIndexFunctionAssertions(
SqmSelectStatement statement,
CollectionClassification expectedCollectionClassification,
Class<? extends SimpleDomainType> expectedIndexDomainTypeType,
String expectedAlias) {
assertEquals( 1, statement.getQuerySpec().getSelectClause().getSelections().size() );
final SqmSelectableNode selectedExpr = statement.getQuerySpec()
.getSelectClause()
.getSelections()
.get( 0 )
.getSelectableNode();
assertThat( selectedExpr, instanceOf( SqmSimplePath.class ) );
final SqmSimplePath selectedPath = (SqmSimplePath) selectedExpr;
assertThat( selectedPath.getLhs().getExplicitAlias(), is( expectedAlias ) );
final PluralPersistentAttribute attribute = (PluralPersistentAttribute) selectedPath.getLhs().getReferencedPathSource();
assertThat( attribute.getCollectionClassification(), is( expectedCollectionClassification ) );
final SimpleDomainType<?> indexDomainType = attribute.getKeyGraphType();
assertThat( indexDomainType, instanceOf( expectedIndexDomainTypeType ) );
assertThat( selectedPath.getReferencedPathSource(), sameInstance( attribute.getIndexPathSource() ) );
}
@Test
public void testMapValueFunction() {
collectionValueFunctionAssertions(
interpretSelect( "select value(m) from EntityOfMaps e join e.basicToBasicMap m" ),
EntityOfMaps.class.getName() + ".basicToBasicMap",
"m"
);
collectionValueFunctionAssertions(
interpretSelect( "select value(m) from EntityOfMaps e join e.basicToComponentMap m" ),
EntityOfMaps.class.getName() + ".basicToComponentMap",
"m"
);
collectionValueFunctionAssertions(
interpretSelect( "select value(m) from EntityOfMaps e join e.basicToOneToMany m" ),
EntityOfMaps.class.getName() + ".basicToOneToMany",
"m"
);
// todo : ManyToMany not properly handled atm
}
@Test
public void testCollectionValueFunction() {
collectionValueFunctionAssertions(
interpretSelect( "select value(b) from EntityOfLists e join e.listOfBasics b" ),
EntityOfLists.class.getName() + ".listOfBasics",
"b"
);
collectionValueFunctionAssertions(
interpretSelect( "select value(b) from EntityOfLists e join e.listOfComponents b" ),
EntityOfLists.class.getName() + ".listOfComponents",
"b"
);
collectionValueFunctionAssertions(
interpretSelect( "select value(b) from EntityOfLists e join e.listOfOneToMany b" ),
EntityOfLists.class.getName() + ".listOfOneToMany",
"b"
);
collectionValueFunctionAssertions(
interpretSelect( "select value(b) from EntityOfSets e join e.setOfBasics b" ),
EntityOfSets.class.getName() + ".setOfBasics",
"b"
);
collectionValueFunctionAssertions(
interpretSelect( "select value(b) from EntityOfSets e join e.setOfComponents b" ),
EntityOfSets.class.getName() + ".setOfComponents",
"b"
);
collectionValueFunctionAssertions(
interpretSelect( "select value(b) from EntityOfSets e join e.setOfOneToMany b" ),
EntityOfSets.class.getName() + ".setOfOneToMany",
"b"
);
// todo : ManyToMany not properly handled atm
}
private void collectionValueFunctionAssertions(
SqmSelectStatement statement,
String collectionRole,
String collectionIdentificationVariable) {
assertEquals( 1, statement.getQuerySpec().getSelectClause().getSelections().size() );
final SqmSelectableNode selectedExpression = statement.getQuerySpec()
.getSelectClause()
.getSelections()
.get( 0 )
.getSelectableNode();
assertThat( selectedExpression, instanceOf( SqmPath.class ) );
final SqmPath selectedPath = (SqmPath) selectedExpression;
final SqmPathSource referencedPathSource = selectedPath.getReferencedPathSource();
final String ownerName = StringHelper.qualifier( collectionRole );
final String attributeName = StringHelper.unqualify( collectionRole );
final EntityDomainType<?> entity = sessionFactory().getJpaMetamodel().entity( ownerName );
final PluralPersistentAttribute<?,?,?> pluralAttribute = entity.findPluralAttribute( attributeName );
assertThat( referencedPathSource, sameInstance( pluralAttribute.getElementPathSource() ) );
assertThat( selectedPath.getLhs().getExplicitAlias(), is( collectionIdentificationVariable ) );
}
@Test
public void testMapEntryFunction() {
SqmSelectStatement statement = interpretSelect( "select entry(m) from EntityOfMaps e join e.basicToManyToMany m" );
assertEquals( 1, statement.getQuerySpec().getSelectClause().getSelections().size() );
final SqmMapEntryReference mapEntryPath = (SqmMapEntryReference) statement.getQuerySpec()
.getSelectClause()
.getSelections()
.get( 0 )
.getSelectableNode();
assertThat( mapEntryPath.getJavaTypeDescriptor().getJavaType(), is( equalTo( Map.Entry.class ) ) );
final SqmPath selectedPathLhs = mapEntryPath.getMapPath();
assertThat( selectedPathLhs.getExplicitAlias(), is( "m" ) );
}
@Test
public void testSimpleRootEntitySelection() {
SqmSelectStatement statement = interpretSelect( "select e from EntityOfBasics e" );
assertEquals( 1, statement.getQuerySpec().getSelectClause().getSelections().size() );
final SqmPath sqmEntityReference = TestingUtil.cast(
statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getSelectableNode(),
SqmPath.class
);
assertThat( sqmEntityReference.getJavaTypeDescriptor().getJavaType(), equalTo( EntityOfBasics.class ));
}
@Override
protected Class[] getAnnotatedClasses() {
return new Class[] {
Person.class,
EntityOfBasics.class,
EntityOfLists.class,
EntityOfMaps.class,
EntityOfSets.class,
};
}
}