/* * 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.sqm.test.domain; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.orm.persister.collection.spi.CollectionPersister; import org.hibernate.orm.persister.common.internal.DatabaseModelImpl; import org.hibernate.orm.persister.common.spi.DatabaseModel; import org.hibernate.orm.persister.embeddable.spi.EmbeddableMapper; import org.hibernate.orm.persister.entity.spi.EntityPersister; import org.hibernate.orm.persister.internal.PersisterFactoryImpl; import org.hibernate.orm.persister.spi.PersisterCreationContext; import org.hibernate.orm.persister.spi.PersisterFactory; import org.hibernate.orm.type.spi.BasicType; import org.hibernate.orm.type.spi.TypeConfiguration; import org.hibernate.query.sqm.domain.SqmDomainMetamodel; import org.hibernate.query.sqm.domain.SqmExpressableTypeBasic; import org.hibernate.query.sqm.domain.SqmExpressableTypeEntity; import org.hibernate.query.sqm.domain.SqmExpressableTypeEntityPolymorphicEntity; import org.hibernate.query.sqm.domain.type.SqmDomainTypeBasic; import org.hibernate.query.sqm.tree.expression.BinaryArithmeticSqmExpression; import org.jboss.logging.Logger; /** * @author Steve Ebersole */ public class ExplicitSqmDomainMetamodel implements SqmDomainMetamodel, PersisterCreationContext { private static final Logger log = Logger.getLogger( ExplicitSqmDomainMetamodel.class ); private final TypeConfiguration typeConfiguration = new TypeConfiguration(); private MetadataImplementor mappingMetadata; private final DatabaseModel databaseModel = new DatabaseModelImpl(); private Map<String, String> importMap = new HashMap<>(); private Map<String, SqmExpressableTypeEntityPolymorphicEntity> polymorphicEntityReferenceMap = new HashMap<>(); public ExplicitSqmDomainMetamodel(MetadataImplementor mappingMetadata) { this.mappingMetadata = mappingMetadata; } @Override public SqmExpressableTypeEntity resolveEntityReference(String entityName) { if ( importMap.containsKey( entityName ) ) { entityName = importMap.get( entityName ); } SqmExpressableTypeEntity entityType = typeConfiguration.findEntityPersister( entityName ); if ( entityType == null ) { entityType = polymorphicEntityReferenceMap.get( entityName ); if ( entityType == null ) { // see if it is an unmapped polymorphic entity reference entityType = resolveUnmappedPolymorphicReference( entityName ); } } if ( entityType == null ) { throw new IllegalArgumentException( "Per JPA spec : no entity named " + entityName ); } return entityType; } private SqmExpressableTypeEntity resolveUnmappedPolymorphicReference(String entityName) { // we have had a request to resolve a (supposed) entity-name into its `SqmExpressableTypeEntity` // (EntityPersister) reference, but were not able to find a persister with exactly that name. // Another option is that the query named a class (generally an interface) implemented // bu one or more of the entities - see if we can find any final Class requestedClass = resolveRequestedClass( entityName ); if ( requestedClass == null ) { return null; } // todo : explicit/implicit polymorphism... // todo : handle "duplicates" within a hierarchy final HashSet<EntityPersister> matchingPersisters = new HashSet<>(); for ( EntityPersister checkPersister : typeConfiguration.getEntityPersisters() ) { if ( checkPersister.getJavaType() == null ) { continue; } if ( requestedClass.isAssignableFrom( checkPersister.getJavaType() ) ) { matchingPersisters.add( checkPersister ); } } final PolymorphicEntityReferenceImpl entityReference = new PolymorphicEntityReferenceImpl( entityName, matchingPersisters ); if ( entityReference != null ) { polymorphicEntityReferenceMap.put( entityName, entityReference ); } return entityReference; } private Class resolveRequestedClass(String entityName) { // try { // return getSessionFactory().getServiceRegistry().getService( ClassLoaderService.class ).classForName( className ); // } // catch (ClassLoadingException e) { // return null; // } try { return Class.forName( entityName ); } catch (ClassNotFoundException e) { return null; } } @Override public <T> SqmExpressableTypeEntity<T> resolveEntityReference(Class<T> javaType) { final SqmExpressableTypeEntity entityType = typeConfiguration.findEntityPersister( javaType.getName() ); if ( entityType == null ) { throw new IllegalArgumentException( "Per JPA spec" ); } return entityType; } @Override public <T> BasicType<T> resolveBasicType(Class<T> javaType) { return typeConfiguration.getBasicTypeRegistry().getBasicType( javaType ); } @Override public SqmExpressableTypeBasic resolveArithmeticType( SqmExpressableTypeBasic firstType, SqmExpressableTypeBasic secondType, BinaryArithmeticSqmExpression.Operation operation) { return ExpressionTypeHelper.resolveArithmeticType( firstType, secondType, operation == BinaryArithmeticSqmExpression.Operation.DIVIDE, this ); } @Override public SqmExpressableTypeBasic resolveSumFunctionType(SqmExpressableTypeBasic argumentType) { return ExpressionTypeHelper.resolveSingleNumericType( argumentType, this ); } @Override public SqmDomainTypeBasic resolveCastTargetType(String name) { return typeConfiguration.getBasicTypeRegistry().getBasicTypeForCast( name ); } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Initialization @Override public SessionFactoryImplementor getSessionFactory() { return null; } @Override public MetadataImplementor getMetadata() { return mappingMetadata; } @Override public DatabaseModel getDatabaseModel() { return databaseModel; } @Override public TypeConfiguration getTypeConfiguration() { return typeConfiguration; } private final PersisterFactoryImpl persisterFactory = new PersisterFactoryImpl(); @Override public PersisterFactory getPersisterFactory() { return persisterFactory; } @Override public void registerEntityPersister(EntityPersister entityPersister) { typeConfiguration.register( entityPersister ); importMap.put( entityPersister.getEntityName(), entityPersister.getEntityName() ); importMap.put( entityPersister.getJpaEntityName(), entityPersister.getEntityName() ); if ( entityPersister.getJavaTypeDescriptor().getJavaType() != null ) { importMap.put( entityPersister.getJavaTypeDescriptor().getJavaType().getSimpleName(), entityPersister.getEntityName() ); } // todo : when moving upstream into MetamodelImpl, need stuff like: // // if ( entityPersister.getConcreteProxyClass() != null // && entityPersister.getConcreteProxyClass().isInterface() // && !Map.class.isAssignableFrom( cp.getConcreteProxyClass() ) // && entityPersister.getMappedClass() != entityPersister.getConcreteProxyClass() ) { // // IMPL NOTE : we exclude Map based proxy interfaces here because that should // // indicate MAP entity mode.0 // // if ( entityPersister.getMappedClass().equals( entityPersister.getConcreteProxyClass() ) ) { // // this part handles an odd case in the Hibernate test suite where we map an interface // // as the class and the proxy. I cannot think of a real life use case for that // // specific test, but.. // log.debugf( "Entity [%s] mapped same interface [%s] as class and proxy", cp.getEntityName(), cp.getMappedClass() ); // } // else { // final String old = entityProxyInterfaceMap.put( entityPersister.getConcreteProxyClass(), entityPersister.getEntityName() ); // if ( old != null ) { // throw new HibernateException( // String.format( // Locale.ENGLISH, // "Multiple entities [%s, %s] named the same interface [%s] as their proxy which is not supported", // old, // entityPersister.getEntityName(), // entityPersister.getConcreteProxyClass().getName() // ) // ); // } // } // } } @Override public void registerCollectionPersister(CollectionPersister collectionPersister) { typeConfiguration.register( collectionPersister ); } @Override public void registerEmbeddablePersister(EmbeddableMapper embeddableMapper) { typeConfiguration.register( embeddableMapper ); } @Override public void registerEntityNameResolvers(EntityPersister entityPersister) { } }