/* * 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.persister.internal; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy; import org.hibernate.mapping.Collection; import org.hibernate.mapping.Component; import org.hibernate.mapping.MappedSuperclass; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.RootClass; import org.hibernate.orm.persister.collection.internal.CollectionPersisterImpl; import org.hibernate.orm.persister.collection.spi.CollectionPersister; import org.hibernate.orm.persister.embeddable.spi.EmbeddableContainer; import org.hibernate.orm.persister.common.spi.ManagedTypeImplementor; import org.hibernate.orm.persister.embeddable.internal.EmbeddableMapperImpl; import org.hibernate.orm.persister.embeddable.spi.EmbeddableMapper; import org.hibernate.orm.persister.entity.internal.EntityHierarchyImpl; import org.hibernate.orm.persister.entity.internal.EntityPersisterImpl; import org.hibernate.orm.persister.entity.spi.EntityHierarchy; import org.hibernate.orm.persister.entity.spi.EntityPersister; import org.hibernate.orm.persister.entity.spi.IdentifiableTypeImplementor; import org.hibernate.orm.persister.spi.PersisterCreationContext; import org.hibernate.orm.persister.spi.PersisterFactory; import org.hibernate.orm.type.descriptor.java.internal.EntityJavaTypeDescriptorImpl; import org.hibernate.orm.type.descriptor.java.spi.EntityJavaTypeDescriptor; import org.hibernate.orm.type.descriptor.java.spi.ManagedJavaTypeDescriptor; import org.hibernate.service.spi.ServiceRegistryAwareService; import org.hibernate.service.spi.ServiceRegistryImplementor; /** * The standard ORM implementation of the {@link PersisterFactory} contract * * @author Gavin King * @author Steve Ebersole */ public final class PersisterFactoryImpl implements PersisterFactory, ServiceRegistryAwareService { private ServiceRegistryImplementor serviceRegistry; private Set<EntityHierarchyNode> roots = new HashSet<>(); private Map<String,EntityHierarchyNode> nameToHierarchyNodeMap = new HashMap<>(); private Map<EmbeddableMapper,Component> embeddableComponentMap = new HashMap<>(); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // IMPL NOTE // // As a PersisterFactory implementation this class is responsible for // generated both EntityPersister and CollectionPersister. // // todo : add creation of EmbeddablePersisters to PersisterFactory contract? // // The general flow for persister creation is as follows: // 1) #createPersister is called directly from the SessionFactory's // Metamodel object during initialization. It creates the // EntityPersister instance and begins categorizing them in // relation to their hierarchy (nameToHierarchyNodeMap, roots) for // later processing // 2) After #createPersister has been called for all defined entities, // #finishUp will be called. This is the trigger to walk all the // previously created persisters *in a specific order*: starting // from #roots we walk down each hierarchy meaning that as we // perform EntityPersister#finishInitialization processing we know // that the super is completely initialized. Part of this // EntityPersister#finishInitialization process is creating the // entity's Attribute definitions. As PluralAttribute definitions // are recognized we create CollectionPersisters // // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override public void injectServices(ServiceRegistryImplementor serviceRegistry) { this.serviceRegistry = serviceRegistry; } @Override @SuppressWarnings( {"unchecked"}) public EntityPersister createEntityPersister( PersistentClass entityBinding, EntityRegionAccessStrategy entityCacheAccessStrategy, NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy, PersisterCreationContext creationContext) throws HibernateException { // todo : MappedSuperclass... // See if we had an existing (partially created) node... EntityHierarchyNode entityHierarchyNode = nameToHierarchyNodeMap.get( entityBinding.getEntityName() ); if ( entityHierarchyNode != null ) { // we create the EntityHierarchyNode for all super types before we // actually create the super's EntityPersister. This check // makes sure we do not have multiple call paths attempting to // create the same EntityPersister multiple times assert entityHierarchyNode.ormJpaType == null; } else { entityHierarchyNode = new EntityHierarchyNode( entityBinding ); nameToHierarchyNodeMap.put( entityBinding.getEntityName(), entityHierarchyNode ); } final EntityPersister entityPersister = instantiateEntityPersister( entityBinding, entityCacheAccessStrategy, naturalIdCacheAccessStrategy, creationContext ); creationContext.registerEntityPersister( entityPersister ); entityHierarchyNode.ormJpaType = entityPersister; final EntityHierarchyNode superTypeNode = interpretSuperTypeNode( entityBinding ); if ( superTypeNode == null ) { roots.add( entityHierarchyNode ); } else { superTypeNode.addSubEntityNode( entityHierarchyNode ); } return entityPersister; } @SuppressWarnings("unchecked") private <T> EntityPersister<T> instantiateEntityPersister( PersistentClass entityBinding, EntityRegionAccessStrategy entityCacheAccessStrategy, NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy, PersisterCreationContext creationContext) { // // If the metadata for the entity specified an explicit persister class, use it... // Class<? extends EntityPersister> persisterClass = entityBinding.getEntityPersisterClass(); // if ( persisterClass == null ) { // // Otherwise, use the persister class indicated by the PersisterClassResolver service // persisterClass = serviceRegistry.getService( PersisterClassResolver.class ).getEntityPersisterClass( entityBinding ); // } // // return instantiateEntityPersister( // persisterClass, // entityBinding, // entityCacheAccessStrategy, // naturalIdCacheAccessStrategy, // creationContext // ); EntityJavaTypeDescriptor<T> jtd = (EntityJavaTypeDescriptor<T>) creationContext.getTypeConfiguration() .getJavaTypeDescriptorRegistry() .getDescriptor( entityBinding.getEntityName() ); if ( jtd == null ) { jtd = new EntityJavaTypeDescriptorImpl( entityBinding.getClassName(), entityBinding.getEntityName(), entityBinding.getMappedClass(), resolveEntitySuperJavaTypeDescriptor( entityBinding ), null, null ); creationContext.getTypeConfiguration().getJavaTypeDescriptorRegistry().addDescriptor( jtd ); } return new EntityPersisterImpl<>( entityBinding, entityCacheAccessStrategy, naturalIdCacheAccessStrategy, creationContext ); } private ManagedJavaTypeDescriptor resolveEntitySuperJavaTypeDescriptor(PersistentClass entityBinding) { return null; } // @SuppressWarnings( {"unchecked"}) // private EntityPersister instantiateEntityPersister( // Class<? extends EntityPersister> persisterClass, // PersistentClass entityBinding, // EntityRegionAccessStrategy entityCacheAccessStrategy, // NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy, // PersisterCreationContext creationContext) { // try { // final Constructor<? extends EntityPersister> constructor = persisterClass.getConstructor( EntityPersister.CONSTRUCTOR_SIGNATURE ); // try { // return constructor.newInstance( // entityBinding, // entityCacheAccessStrategy, // naturalIdCacheAccessStrategy, // creationContext // ); // } // catch (MappingException e) { // throw e; // } // catch (InvocationTargetException e) { // Throwable target = e.getTargetException(); // if ( target instanceof HibernateException ) { // throw (HibernateException) target; // } // else { // throw new MappingException( "Could not instantiate persister " + persisterClass.getName(), target ); // } // } // catch (Exception e) { // throw new MappingException( "Could not instantiate persister " + persisterClass.getName(), e ); // } // } // catch (MappingException e) { // throw e; // } // catch (Exception e) { // throw new MappingException( "Could not get constructor for " + persisterClass.getName(), e ); // } // } // // private InheritanceStrategy interpretInheritanceStrategy(PersistentClass entityBinding) { // if ( entityBinding instanceof RootClass ) { // if ( !entityBinding.hasSubclasses() ) { // return InheritanceStrategy.NONE; // } // return interpretInheritanceStrategy( (Subclass) entityBinding.getDirectSubclasses().next() ); // } // else { // return interpretInheritanceStrategy( (Subclass) entityBinding ); // } // } // // private InheritanceStrategy interpretInheritanceStrategy(Subclass subEntityBinding) { // if ( subEntityBinding instanceof UnionSubclass ) { // return InheritanceStrategy.UNION; // } // else if ( subEntityBinding instanceof JoinedSubclass ) { // return InheritanceStrategy.JOINED; // } // else { // return InheritanceStrategy.DISCRIMINATOR; // } // } private EntityHierarchyNode interpretSuperTypeNode(PersistentClass entityBinding) { if ( entityBinding.getSuperMappedSuperclass() != null ) { // If entityBinding#getSuperMappedSuperclass() is not null, that is the direct super type return interpretMappedSuperclass( entityBinding.getSuperMappedSuperclass() ); } else if ( entityBinding.getSuperclass() != null ) { // else, if entityBinding#getSuperclass() is not null, that is the direct super type // in this case we want to create the TypeHierarchyNode (if not already there), but not the persisters... // that will happen on later call to final String superTypeName = entityBinding.getSuperclass().getEntityName(); EntityHierarchyNode node = nameToHierarchyNodeMap.computeIfAbsent( superTypeName, k -> new EntityHierarchyNode( entityBinding.getSuperclass() ) ); return node; } else { // else, there is no super. return null; } } private EntityHierarchyNode interpretMappedSuperclass(MappedSuperclass mappedSuperclass) { if ( mappedSuperclass == null ) { return null; } assert mappedSuperclass.getMappedClass() != null; final EntityHierarchyNode existing = nameToHierarchyNodeMap.get( mappedSuperclass.getMappedClass().getName() ); if ( existing != null ) { return existing; } return makeMappedSuperclassTypeNode( mappedSuperclass ); } private EntityHierarchyNode makeMappedSuperclassTypeNode(MappedSuperclass mappedSuperclass) { assert mappedSuperclass.getMappedClass() != null; final EntityHierarchyNode mappedSuperclassTypeSuperNode = interpretMappedSuperclass( mappedSuperclass.getSuperMappedSuperclass() ); nameToHierarchyNodeMap.put( mappedSuperclass.getMappedClass().getName(), mappedSuperclassTypeSuperNode ); return mappedSuperclassTypeSuperNode; } @Override @SuppressWarnings( {"unchecked"}) public CollectionPersister createCollectionPersister( Collection collectionBinding, ManagedTypeImplementor source, String propertyName, CollectionRegionAccessStrategy cacheAccessStrategy, PersisterCreationContext creationContext) throws HibernateException { // // If the metadata for the collection specified an explicit persister class, use it // Class<? extends CollectionPersister> persisterClass = collectionBinding.getCollectionPersisterClass(); // if ( persisterClass == null ) { // // Otherwise, use the persister class indicated by the PersisterClassResolver service // persisterClass = serviceRegistry.getService( PersisterClassResolver.class ) // .getCollectionPersisterClass( collectionBinding ); // } // return createCollectionPersister( persisterClass, collectionBinding, source, propertyName, cacheAccessStrategy, creationContext ); // todo : ^^ we need to make this fit the published ctor contract (as much as possible) // for now just create a simpl testing stub return new CollectionPersisterImpl( collectionBinding, source, propertyName, null, creationContext ); } @SuppressWarnings( {"unchecked"}) private CollectionPersister createCollectionPersister( Class<? extends CollectionPersister> persisterClass, Collection collectionBinding, ManagedTypeImplementor source, String propertyName, CollectionRegionAccessStrategy cacheAccessStrategy, PersisterCreationContext creationContext) { try { Constructor<? extends CollectionPersister> constructor = persisterClass.getConstructor( CollectionPersister.CONSTRUCTOR_SIGNATURE ); try { return constructor.newInstance( collectionBinding, source, propertyName, cacheAccessStrategy, creationContext ); } catch (MappingException e) { throw e; } catch (InvocationTargetException e) { Throwable target = e.getTargetException(); if ( target instanceof HibernateException ) { throw (HibernateException) target; } else { throw new MappingException( "Could not instantiate collection persister " + persisterClass.getName(), target ); } } catch (Exception e) { throw new MappingException( "Could not instantiate collection persister " + persisterClass.getName(), e ); } } catch (MappingException e) { throw e; } catch (Exception e) { throw new MappingException( "Could not get constructor for " + persisterClass.getName(), e ); } } @Override public EmbeddableMapper createEmbeddablePersister( Component componentBinding, EmbeddableContainer source, String localName, PersisterCreationContext creationContext) throws HibernateException { final EmbeddableMapperImpl mapper = new EmbeddableMapperImpl( creationContext, null, source, localName, componentBinding, null, null, null ); embeddableComponentMap.put( mapper, componentBinding ); mapper.setTypeConfiguration( creationContext.getTypeConfiguration() ); return mapper; } @Override public void finishUp(PersisterCreationContext creationContext) { for ( EntityHierarchyNode root : roots ) { // todo : resolve any MappedSuperclasses for supers of the root entity EntityHierarchy entityHierarchy = new EntityHierarchyImpl( creationContext, (RootClass) root.mappingType, (EntityPersister) root.ormJpaType ); finishSupers( root.superEntityNode, entityHierarchy, creationContext ); root.finishUp( entityHierarchy, creationContext ); entityHierarchy.finishInitialization( creationContext, (RootClass) root.mappingType ); } // todo : for ( final Collection model : creationContext.getMetadata().getCollectionBindings() ) { final CollectionPersister collectionPersister = creationContext.getTypeConfiguration().findCollectionPersister( model.getRole() ); if ( collectionPersister == null ) { throw new HibernateException( "Collection role not properly materialized to CollectionPersister : " + model.getRole() ); } collectionPersister.finishInitialization( model, creationContext ); } for ( EmbeddableMapper mapper : creationContext.getTypeConfiguration().getEmbeddablePersisters() ) { mapper.afterInitialization( embeddableComponentMap.get( mapper ), creationContext ); } serviceRegistry = null; roots.clear(); nameToHierarchyNodeMap.clear(); embeddableComponentMap.clear(); } private void finishSupers(EntityHierarchyNode node, EntityHierarchy hierarchy, PersisterCreationContext creationContext) { if ( node == null ) { return; } finishSupers( node.superEntityNode, hierarchy, creationContext ); node.ormJpaType.finishInitialization( hierarchy, node.superEntityNode.ormJpaType, node.superEntityNode.mappingType, creationContext ); } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Inner classes public static class EntityHierarchyNode { private final PersistentClass mappingType; private IdentifiableTypeImplementor ormJpaType; private EntityHierarchyNode superEntityNode; private Set<EntityHierarchyNode> subEntityNodes; public EntityHierarchyNode(PersistentClass mappingType) { this.mappingType = mappingType; } public PersistentClass getMappingType() { return mappingType; } public IdentifiableTypeImplementor getOrmJpaType() { return ormJpaType; } public void inject(IdentifiableTypeImplementor ormJpaType) { if ( this.ormJpaType != null ) { throw new IllegalStateException( "TypeHierarchyNode.ormJpaType ws already defined" ); } this.ormJpaType = ormJpaType; } public void addSubEntityNode(EntityHierarchyNode subEntityNode) { if ( subEntityNodes == null ) { subEntityNodes = new HashSet<>(); } subEntityNodes.add( subEntityNode ); subEntityNode.setSuperEntityNode( this ); } private void setSuperEntityNode(EntityHierarchyNode superEntityNode) { this.superEntityNode = superEntityNode; } public void finishUp(EntityHierarchy hierarchy, PersisterCreationContext creationContext) { if ( getOrmJpaType() == null ) { throw new HibernateException( "ORM-JPA IdentifiableTypeImplementor not yet known; cannot finishUp" ); } final IdentifiableTypeImplementor superOrmJpaType = ( superEntityNode == null ) ? null : superEntityNode.getOrmJpaType(); // todo : how many phases/passes do we want to give these IdentifiableTypeImplementor impls to finalize themselves? getOrmJpaType().finishInitialization( hierarchy, superOrmJpaType, mappingType, creationContext ); // getEntityPersister().generateEntityDefinition(); // getEntityPersister().postInstantiate(); // // // initialize the EntityPersister represented by this hierarchy node // getEntityPersister().finishInitialization( superType, entityBinding , creationContext ); // if ( getOrmJpaType() instanceof EntityPersister ) { creationContext.registerEntityNameResolvers( (EntityPersister) getOrmJpaType() ); } if ( subEntityNodes != null ) { // pass finishUp processing to each of the sub-entity hierarchy nodes (recursive) for ( EntityHierarchyNode subTypeNode : subEntityNodes ) { subTypeNode.finishUp( hierarchy, creationContext ); } } } @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( !( o instanceof EntityHierarchyNode ) ) { return false; } EntityHierarchyNode that = (EntityHierarchyNode) o; return mappingType.getEntityName().equals( that.mappingType.getEntityName() ); } @Override public int hashCode() { return mappingType.getEntityName().hashCode(); } } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Deprecations /** * @deprecated Use {@link org.hibernate.persister.internal.PersisterFactoryImpl#ENTITY_PERSISTER_CONSTRUCTOR_ARGS} instead. */ @Deprecated public static final Class[] ENTITY_PERSISTER_CONSTRUCTOR_ARGS = EntityPersister.CONSTRUCTOR_SIGNATURE; /** * @deprecated Use {@link CollectionPersister#CONSTRUCTOR_SIGNATURE} instead */ @Deprecated public static final Class[] COLLECTION_PERSISTER_CONSTRUCTOR_ARGS = CollectionPersister.CONSTRUCTOR_SIGNATURE; }