All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.metamodel.internal.AttributeFactory Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha3
Show newest version
/*
 * 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 .
 */
package org.hibernate.metamodel.internal;

import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.Iterator;
import javax.persistence.ManyToMany;
import javax.persistence.OneToOne;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.Type;

import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.internal.EntityManagerMessageLogger;
import org.hibernate.internal.HEMLogging;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.List;
import org.hibernate.mapping.Map;
import org.hibernate.mapping.OneToMany;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.AttributeClassification;
import org.hibernate.metamodel.RepresentationMode;
import org.hibernate.metamodel.model.domain.AbstractIdentifiableType;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
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.SimpleDomainType;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.metamodel.model.domain.internal.EmbeddableTypeImpl;
import org.hibernate.metamodel.model.domain.internal.MapMember;
import org.hibernate.metamodel.model.domain.internal.MappedSuperclassTypeImpl;
import org.hibernate.metamodel.model.domain.internal.PluralAttributeBuilder;
import org.hibernate.metamodel.model.domain.internal.SingularAttributeImpl;
import org.hibernate.metamodel.spi.ManagedTypeRepresentationStrategy;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.property.access.internal.PropertyAccessMapImpl;
import org.hibernate.property.access.spi.Getter;
import org.hibernate.tuple.entity.EntityMetamodel;
import org.hibernate.type.EmbeddedComponentType;
import org.hibernate.type.EntityType;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.spi.JavaTypeDescriptorRegistry;

/**
 * A factory for building {@link Attribute} instances.  Exposes 3 main services for building
    *
  1. {@link #buildAttribute normal attributes}
  2. *
  3. {@link #buildIdAttribute id attributes}
  4. *
  5. {@link #buildVersionAttribute version attributes}
  6. *
      * * @author Steve Ebersole * @author Emmanuel Bernard */ public class AttributeFactory { private static final EntityManagerMessageLogger LOG = HEMLogging.messageLogger( AttributeFactory.class ); private final MetadataContext context; public AttributeFactory(MetadataContext context) { this.context = context; } /** * Build a normal attribute. * * @param ownerType The descriptor of the attribute owner (aka declarer). * @param property The Hibernate property descriptor for the attribute * @param The type of the owner * @param The attribute type * * @return The built attribute descriptor or null if the attribute is not part of the JPA 2 model (eg backrefs) */ public PersistentAttribute buildAttribute(ManagedDomainType ownerType, Property property) { return buildAttribute( ownerType, property, context ); } @SuppressWarnings({"unchecked"}) public static PersistentAttribute buildAttribute( ManagedDomainType 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() ); return null; } LOG.trace( "Building attribute [" + ownerType.getTypeName() + "." + property.getName() + "]" ); final AttributeContext attributeContext = wrap( ownerType, property ); final AttributeMetadata attributeMetadata = determineAttributeMetadata( attributeContext, normalMemberResolver, metadataContext ); if ( attributeMetadata == null ) { return null; } if ( attributeMetadata.isPlural() ) { return PluralAttributeBuilder.build( (PluralAttributeMetadata) attributeMetadata, metadataContext ); } final SingularAttributeMetadata singularAttributeMetadata = (SingularAttributeMetadata) attributeMetadata; final SimpleDomainType metaModelType = determineSimpleType( singularAttributeMetadata.getValueContext(), metadataContext ); return new SingularAttributeImpl( ownerType, attributeMetadata.getName(), attributeMetadata.getAttributeClassification(), metaModelType, attributeMetadata.getMember(), false, false, property.isOptional(), metadataContext ); } private static AttributeContext wrap(final ManagedDomainType ownerType, final Property property) { return new AttributeContext() { public ManagedDomainType getOwnerType() { return ownerType; } public Property getPropertyMapping() { return property; } }; } /** * Build the identifier attribute descriptor * * @param ownerType The descriptor of the attribute owner (aka declarer). * @param property The Hibernate property descriptor for the identifier attribute * @param The type of the owner * @param The attribute type * * @return The built attribute descriptor */ @SuppressWarnings({"unchecked"}) public SingularPersistentAttribute buildIdAttribute( IdentifiableDomainType ownerType, Property property) { LOG.trace( "Building identifier attribute [" + ownerType.getTypeName() + "." + property.getName() + "]" ); // ownerType = Entity(Person) // MetadataContext#containerRoleStack -> Person // id-attribute = "id" final SingularAttributeMetadata attributeMetadata = (SingularAttributeMetadata) determineAttributeMetadata( wrap( ownerType, property ), identifierMemberResolver ); return new SingularAttributeImpl.Identifier( ownerType, property.getName(), determineSimpleType( attributeMetadata.getValueContext() ), attributeMetadata.getMember(), attributeMetadata.getAttributeClassification(), context ); } /** * Build the version attribute descriptor * * @param ownerType The descriptor of the attribute owner (aka declarer). * @param property The Hibernate property descriptor for the version attribute * @param The type of the owner * @param The attribute type * * @return The built attribute descriptor */ @SuppressWarnings({"unchecked"}) public SingularAttributeImpl buildVersionAttribute( IdentifiableDomainType ownerType, Property property) { LOG.trace( "Building version attribute [ownerType.getTypeName()" + "." + "property.getName()]" ); final SingularAttributeMetadata attributeMetadata = (SingularAttributeMetadata) determineAttributeMetadata( wrap( ownerType, property ), versionMemberResolver ); return new SingularAttributeImpl.Version( ownerType, property.getName(), attributeMetadata.getAttributeClassification(), determineSimpleType( attributeMetadata.getValueContext() ), attributeMetadata.getMember(), context ); } private SimpleDomainType determineSimpleType(ValueContext typeContext) { return determineSimpleType( typeContext, context ); } @SuppressWarnings("unchecked") public static SimpleDomainType determineSimpleType(ValueContext typeContext, MetadataContext context) { switch ( typeContext.getValueClassification() ) { case BASIC: { return context.resolveBasicType( typeContext.getJpaBindableType() ); } case ENTITY: { final org.hibernate.type.EntityType type = (EntityType) typeContext.getHibernateValue().getType(); return context.locateEntityType( type.getAssociatedEntityName() ); } case EMBEDDABLE: { final Component component = (Component) typeContext.getHibernateValue(); final EmbeddableTypeImpl embeddableType; if ( component.getComponentClass() != null || component.getComponentClassName() != null ) { // we should have a non-dynamic embeddable final Class embeddableClass; if ( component.getComponentClass() != null ) { embeddableClass = component.getComponentClass(); } else { embeddableClass = context.getTypeConfiguration() .getServiceRegistry() .getService( ClassLoaderService.class ) .classForName( component.getComponentClassName() ); } final EmbeddableDomainType cached = context.locateEmbeddable( embeddableClass ); if ( cached != null ) { return cached; } final JavaTypeDescriptorRegistry registry = context.getTypeConfiguration() .getJavaTypeDescriptorRegistry(); final JavaTypeDescriptor javaTypeDescriptor = registry.resolveDescriptor( embeddableClass ); final ManagedTypeRepresentationStrategy representationStrategy = context.getTypeConfiguration() .getMetadataBuildingContext() .getBuildingOptions() .getManagedTypeRepresentationResolver() .resolveStrategy( component, context.getRuntimeModelCreationContext() ); embeddableType = new EmbeddableTypeImpl( javaTypeDescriptor, representationStrategy, context.getJpaMetamodel() ); context.registerEmbeddableType( embeddableType, component ); return embeddableType; } else { embeddableType = new EmbeddableTypeImpl( component.getRoleName(), context.getJpaMetamodel() ); } final EmbeddableTypeImpl.InFlightAccess inFlightAccess = embeddableType.getInFlightAccess(); final Iterator subProperties = component.getPropertyIterator(); while ( subProperties.hasNext() ) { final Property property = subProperties.next(); final PersistentAttribute attribute = buildAttribute( embeddableType, property, context ); if ( attribute != null ) { inFlightAccess.addAttribute( attribute ); } } inFlightAccess.finishUp(); return embeddableType; } default: { throw new AssertionFailure( "Unknown type : " + typeContext.getValueClassification() ); } } } private EntityMetamodel getDeclarerEntityMetamodel(AbstractIdentifiableType ownerType) { return getDeclarerEntityMetamodel( ownerType, context ); } private static EntityPersister getDeclaringEntity( AbstractIdentifiableType ownerType, MetadataContext metadataContext) { final Type.PersistenceType persistenceType = ownerType.getPersistenceType(); if ( persistenceType == Type.PersistenceType.ENTITY ) { return metadataContext.getMetamodel() .getEntityDescriptor( ownerType.getTypeName() ); } else if ( persistenceType == Type.PersistenceType.MAPPED_SUPERCLASS ) { PersistentClass persistentClass = metadataContext.getPersistentClassHostingProperties( (MappedSuperclassTypeImpl) ownerType ); return metadataContext.getMetamodel() .findEntityDescriptor( persistentClass.getClassName() ); } else { throw new AssertionFailure( "Cannot get the metamodel for PersistenceType: " + persistenceType ); } } private static EntityMetamodel getDeclarerEntityMetamodel( AbstractIdentifiableType ownerType, MetadataContext metadataContext) { final Type.PersistenceType persistenceType = ownerType.getPersistenceType(); if ( persistenceType == Type.PersistenceType.ENTITY ) { return metadataContext.getMetamodel() .getEntityDescriptor( ownerType.getTypeName() ) .getEntityMetamodel(); } else if ( persistenceType == Type.PersistenceType.MAPPED_SUPERCLASS ) { PersistentClass persistentClass = metadataContext.getPersistentClassHostingProperties( (MappedSuperclassTypeImpl) ownerType ); return metadataContext.getMetamodel() .findEntityDescriptor( persistentClass.getClassName() ) .getEntityMetamodel(); } else { throw new AssertionFailure( "Cannot get the metamodel for PersistenceType: " + persistenceType ); } } /** * 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. * * @param attributeContext The attribute to be described * @param memberResolver Strategy for how to resolve the member defining the attribute. * @param The owner type * @param The attribute type * * @return The attribute description */ @SuppressWarnings({"unchecked"}) private AttributeMetadata determineAttributeMetadata( AttributeContext attributeContext, MemberResolver memberResolver) { return determineAttributeMetadata( attributeContext, memberResolver, context ); } private static AttributeMetadata determineAttributeMetadata( AttributeContext 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, context ); LOG.trace( " Determined member [" + member + "]" ); final Value value = propertyMapping.getValue(); final org.hibernate.type.Type type = value.getType(); LOG.trace( " Determined type [name=" + type.getName() + ", class=" + type.getClass().getName() + "]" ); if ( type.isAnyType() ) { return new SingularAttributeMetadataImpl<>( propertyMapping, attributeContext.getOwnerType(), member, AttributeClassification.ANY, context ); } else if ( type.isAssociationType() ) { // collection or entity if ( type.isEntityType() ) { // entity return new SingularAttributeMetadataImpl( propertyMapping, attributeContext.getOwnerType(), member, determineSingularAssociationClassification( member ), context ); } // collection if ( value instanceof Collection ) { final Collection collValue = (Collection) value; final Value elementValue = collValue.getElement(); final org.hibernate.type.Type elementType = elementValue.getType(); final boolean isManyToMany = isManyToMany( member ); // First, determine the type of the elements and use that to help determine the // collection type) final AttributeClassification elementClassification; final AttributeClassification attributeClassification; if ( elementType.isAnyType() ) { attributeClassification = AttributeClassification.ELEMENT_COLLECTION; elementClassification = AttributeClassification.ANY; } else if ( elementValue instanceof Component ) { elementClassification = AttributeClassification.EMBEDDED; attributeClassification = AttributeClassification.ELEMENT_COLLECTION; } else if ( elementType.isAssociationType() ) { elementClassification = isManyToMany ? AttributeClassification.MANY_TO_MANY : AttributeClassification.ONE_TO_MANY; attributeClassification = elementClassification; } else { elementClassification = AttributeClassification.BASIC; attributeClassification = AttributeClassification.ELEMENT_COLLECTION; } final AttributeClassification indexClassification; // Finally, we determine the type of the map key (if needed) if ( value instanceof Map ) { final Value keyValue = ( (Map) value ).getIndex(); final org.hibernate.type.Type keyType = keyValue.getType(); if ( keyType.isAnyType() ) { indexClassification = AttributeClassification.ANY; } else if ( keyValue instanceof Component ) { indexClassification = AttributeClassification.EMBEDDED; } else if ( keyType.isAssociationType() ) { indexClassification = AttributeClassification.MANY_TO_ONE; } else { indexClassification = AttributeClassification.BASIC; } } else if ( value instanceof List ) { indexClassification = AttributeClassification.BASIC; } else { indexClassification = null; } return new PluralAttributeMetadataImpl( propertyMapping, attributeContext.getOwnerType(), member, attributeClassification, elementClassification, indexClassification, context ); } else if ( value instanceof OneToMany ) { // TODO : is this even possible??? Really OneToMany should be describing the // element value within a o.h.mapping.Collection (see logic branch above) throw new IllegalArgumentException( "HUH???" ); // final boolean isManyToMany = isManyToMany( member ); // //one to many with FK => entity // return new PluralAttributeMetadataImpl( // attributeContext.getPropertyMapping(), // attributeContext.getOwnerType(), // member, // isManyToMany // ? Attribute.PersistentAttributeType.MANY_TO_MANY // : Attribute.PersistentAttributeType.ONE_TO_MANY // value, // AttributeContext.TypeStatus.ENTITY, // Attribute.PersistentAttributeType.ONE_TO_MANY, // null, null, null // ); } } else if ( propertyMapping.isComposite() ) { // component return new SingularAttributeMetadataImpl<>( propertyMapping, attributeContext.getOwnerType(), member, AttributeClassification.EMBEDDED, context ); } else { // basic type return new SingularAttributeMetadataImpl<>( propertyMapping, attributeContext.getOwnerType(), member, AttributeClassification.BASIC, context ); } throw new UnsupportedOperationException( "oops, we are missing something: " + propertyMapping ); } public static AttributeClassification determineSingularAssociationClassification(Member member) { if ( member instanceof Field ) { return ( (Field) member ).getAnnotation( OneToOne.class ) != null ? AttributeClassification.ONE_TO_ONE : AttributeClassification.MANY_TO_ONE; } else if ( member instanceof MapMember ) { return AttributeClassification.MANY_TO_ONE; // curious to see how this works for non-annotated methods } else { return ( (Method) member ).getAnnotation( OneToOne.class ) != null ? AttributeClassification.ONE_TO_ONE : AttributeClassification.MANY_TO_ONE; } } protected Class accountForPrimitiveTypes(Class declaredType) { return accountForPrimitiveTypes( declaredType, context ); } public static Class accountForPrimitiveTypes(Class declaredType, MetadataContext metadataContext) { // if ( !declaredType.isPrimitive() ) { // return declaredType; // } // // if ( Boolean.TYPE.equals( declaredType ) ) { // return (Class) Boolean.class; // } // if ( Character.TYPE.equals( declaredType ) ) { // return (Class) Character.class; // } // if( Byte.TYPE.equals( declaredType ) ) { // return (Class) Byte.class; // } // if ( Short.TYPE.equals( declaredType ) ) { // return (Class) Short.class; // } // if ( Integer.TYPE.equals( declaredType ) ) { // return (Class) Integer.class; // } // if ( Long.TYPE.equals( declaredType ) ) { // return (Class) Long.class; // } // if ( Float.TYPE.equals( declaredType ) ) { // return (Class) Float.class; // } // if ( Double.TYPE.equals( declaredType ) ) { // return (Class) Double.class; // } // // throw new IllegalArgumentException( "Unexpected type [" + declaredType + "]" ); // if the field is defined as int, return int not Integer... return declaredType; } public static ParameterizedType getSignatureType(Member member) { final java.lang.reflect.Type type; if ( member instanceof Field ) { type = ( (Field) member ).getGenericType(); } else if ( member instanceof Method ) { type = ( (Method) member ).getGenericReturnType(); } else { type = ( (MapMember) member ).getType(); } //this is a raw type if ( type instanceof Class ) { return null; } return (ParameterizedType) type; } public static boolean isManyToMany(Member member) { if ( member instanceof Field ) { return ( (Field) member ).getAnnotation( ManyToMany.class ) != null; } else if ( member instanceof Method ) { return ( (Method) member ).getAnnotation( ManyToMany.class ) != null; } return false; } private static final MemberResolver embeddedMemberResolver = (attributeContext, metadataContext) -> { // the owner is an embeddable final EmbeddableDomainType ownerType = (EmbeddableDomainType) attributeContext.getOwnerType(); if ( ownerType.getRepresentationStrategy().getMode() == RepresentationMode.MAP ) { return new MapMember( attributeContext.getPropertyMapping().getName(), ownerType.getExpressableJavaTypeDescriptor().getJavaType() ); } else { return ownerType.getRepresentationStrategy() .resolvePropertyAccess( attributeContext.getPropertyMapping() ) .getGetter() .getMember(); } }; private static final MemberResolver virtualIdentifierMemberResolver = (attributeContext, metadataContext) -> { final AbstractIdentifiableType identifiableType = (AbstractIdentifiableType) attributeContext.getOwnerType(); final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( identifiableType, metadataContext ); if ( !entityMetamodel.getIdentifierProperty().isVirtual() ) { throw new IllegalArgumentException( "expecting IdClass mapping" ); } org.hibernate.type.Type type = entityMetamodel.getIdentifierProperty().getType(); if ( !(type instanceof EmbeddedComponentType) ) { throw new IllegalArgumentException( "expecting IdClass mapping" ); } final EmbeddedComponentType componentType = (EmbeddedComponentType) type; final String attributeName = attributeContext.getPropertyMapping().getName(); final Getter getter = componentType.getComponentTuplizer() .getGetter( componentType.getPropertyIndex( attributeName ) ); return PropertyAccessMapImpl.GetterImpl.class.isInstance( getter ) ? new MapMember( attributeName, attributeContext.getPropertyMapping().getType().getReturnedClass() ) : getter.getMember(); }; /** * A {@link Member} resolver for normal attributes. */ 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, metadataContext ); } else if ( Type.PersistenceType.ENTITY == persistenceType || Type.PersistenceType.MAPPED_SUPERCLASS == persistenceType ) { final AbstractIdentifiableType identifiableType = (AbstractIdentifiableType) ownerType; final EntityPersister declaringEntityMapping = getDeclaringEntity( identifiableType, metadataContext ); final EntityMetamodel entityMetamodel = declaringEntityMapping.getEntityMetamodel(); 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, metadataContext ); } else { final Getter getter = declaringEntityMapping.getRepresentationStrategy().resolvePropertyAccess( property ).getGetter(); return getter instanceof PropertyAccessMapImpl.GetterImpl ? new MapMember( propertyName, property.getType().getReturnedClass() ) : getter.getMember(); } } else { throw new IllegalArgumentException( "Unexpected owner type : " + persistenceType ); } }; private final MemberResolver identifierMemberResolver = (attributeContext, metadataContext) -> { final AbstractIdentifiableType identifiableType = (AbstractIdentifiableType) attributeContext.getOwnerType(); final EntityPersister declaringEntityMapping = getDeclaringEntity( identifiableType, metadataContext ); final EntityMetamodel entityMetamodel = declaringEntityMapping.getEntityMetamodel(); if ( !attributeContext.getPropertyMapping().getName() .equals( entityMetamodel.getIdentifierProperty().getName() ) ) { // this *should* indicate processing part of an IdClass... return virtualIdentifierMemberResolver.resolveMember( attributeContext, metadataContext ); } final Getter getter = declaringEntityMapping.getRepresentationStrategy().resolvePropertyAccess( attributeContext.getPropertyMapping() ).getGetter(); if ( getter instanceof PropertyAccessMapImpl.GetterImpl ) { return new MapMember( entityMetamodel.getIdentifierProperty().getName(), entityMetamodel.getIdentifierProperty().getType().getReturnedClass() ); } else { return getter.getMember(); } }; private final MemberResolver versionMemberResolver = new MemberResolver() { @Override public Member resolveMember( AttributeContext attributeContext, MetadataContext metadataContext) { final AbstractIdentifiableType identifiableType = (AbstractIdentifiableType) attributeContext.getOwnerType(); final EntityPersister declaringEntityMapping = getDeclaringEntity( identifiableType, metadataContext ); final EntityMetamodel entityMetamodel = declaringEntityMapping.getEntityMetamodel(); final String versionPropertyName = attributeContext.getPropertyMapping().getName(); if ( !versionPropertyName.equals( entityMetamodel.getVersionProperty().getName() ) ) { // this should never happen, but to be safe... throw new IllegalArgumentException( "Given property did not match declared version property" ); } final Getter getter = declaringEntityMapping.getRepresentationStrategy().resolvePropertyAccess( attributeContext.getPropertyMapping() ).getGetter(); if ( PropertyAccessMapImpl.GetterImpl.class.isInstance( getter ) ) { return new MapMember( versionPropertyName, attributeContext.getPropertyMapping().getType().getReturnedClass() ); } else { return getter.getMember(); } } }; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy