diff --git a/core/src/main/java/org/hibernate/EntityNameResolver.java b/core/src/main/java/org/hibernate/EntityNameResolver.java new file mode 100644 index 0000000000..8f9a94b3a9 --- /dev/null +++ b/core/src/main/java/org/hibernate/EntityNameResolver.java @@ -0,0 +1,42 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Middleware LLC. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + * + */ +package org.hibernate; + +/** + * Contract for resolving an entity-name from a given entity instance. + * + * @author Steve Ebersole + */ +public interface EntityNameResolver { + /** + * Given an entity instance, determine its entity-name. + * + * @param entity The entity instance. + * + * @return The corresponding entity-name, or null if this impl does not know how to perform resolution + * for the given entity instance. + */ + public String resolveEntityName(Object entity); +} diff --git a/core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java b/core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java index 79ca7b5693..2a924736fb 100644 --- a/core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java +++ b/core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java @@ -39,6 +39,7 @@ import java.util.Iterator; import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.LinkedHashSet; import javax.naming.NamingException; import javax.naming.Reference; import javax.naming.StringRefAddr; @@ -58,6 +59,8 @@ import org.hibernate.QueryException; import org.hibernate.SessionFactory; import org.hibernate.StatelessSession; import org.hibernate.SessionFactoryObserver; +import org.hibernate.EntityNameResolver; +import org.hibernate.tuple.entity.EntityTuplizer; import org.hibernate.cache.CacheKey; import org.hibernate.cache.CollectionRegion; import org.hibernate.cache.EntityRegion; @@ -117,6 +120,7 @@ import org.hibernate.type.AssociationType; import org.hibernate.type.Type; import org.hibernate.util.CollectionHelper; import org.hibernate.util.ReflectHelper; +import org.hibernate.util.EmptyIterator; /** @@ -177,6 +181,7 @@ public final class SessionFactoryImpl implements SessionFactory, SessionFactoryI private final transient EntityNotFoundDelegate entityNotFoundDelegate; private final transient SQLFunctionRegistry sqlFunctionRegistry; private final transient SessionFactoryObserver observer; + private final transient HashMap entityNameResolvers = new HashMap(); private final QueryPlanCache queryPlanCache = new QueryPlanCache( this ); @@ -325,11 +330,15 @@ public final class SessionFactoryImpl implements SessionFactory, SessionFactoryI // after *all* persisters and named queries are registered Iterator iter = entityPersisters.values().iterator(); while ( iter.hasNext() ) { - ( (EntityPersister) iter.next() ).postInstantiate(); + final EntityPersister persister = ( ( EntityPersister ) iter.next() ); + persister.postInstantiate(); + registerEntityNameResolvers( persister ); + } iter = collectionPersisters.values().iterator(); while ( iter.hasNext() ) { - ( (CollectionPersister) iter.next() ).postInstantiate(); + final CollectionPersister persister = ( ( CollectionPersister ) iter.next() ); + persister.postInstantiate(); } //JNDI + Serialization: @@ -458,6 +467,41 @@ public final class SessionFactoryImpl implements SessionFactory, SessionFactoryI this.observer.sessionFactoryCreated( this ); } + private void registerEntityNameResolvers(EntityPersister persister) { + Iterator itr = persister.getEntityMetamodel().getTuplizerMapping().iterateTuplizers(); + while ( itr.hasNext() ) { + final EntityTuplizer tuplizer = ( EntityTuplizer ) itr.next(); + registerEntityNameResolvers( tuplizer ); + } + } + + private void registerEntityNameResolvers(EntityTuplizer tuplizer) { + EntityNameResolver[] resolvers = tuplizer.getEntityNameResolvers(); + if ( resolvers == null ) { + return; + } + + for ( int i = 0; i < resolvers.length; i++ ) { + registerEntityNameResolver( resolvers[i], tuplizer.getEntityMode() ); + } + } + + public void registerEntityNameResolver(EntityNameResolver resolver, EntityMode entityMode) { + LinkedHashSet resolversForMode = ( LinkedHashSet ) entityNameResolvers.get( entityMode ); + if ( resolversForMode == null ) { + resolversForMode = new LinkedHashSet(); + entityNameResolvers.put( entityMode, resolversForMode ); + } + resolversForMode.add( resolver ); + } + + public Iterator iterateEntityNameResolvers(EntityMode entityMode) { + Set actualEntityNameResolvers = ( Set ) entityNameResolvers.get( entityMode ); + return actualEntityNameResolvers == null + ? EmptyIterator.INSTANCE + : actualEntityNameResolvers.iterator(); + } + public QueryPlanCache getQueryPlanCache() { return queryPlanCache; } diff --git a/core/src/main/java/org/hibernate/impl/SessionImpl.java b/core/src/main/java/org/hibernate/impl/SessionImpl.java index af1f1e0545..0b277a6ca8 100644 --- a/core/src/main/java/org/hibernate/impl/SessionImpl.java +++ b/core/src/main/java/org/hibernate/impl/SessionImpl.java @@ -67,6 +67,7 @@ import org.hibernate.Transaction; import org.hibernate.TransientObjectException; import org.hibernate.UnresolvableObjectException; import org.hibernate.UnknownProfileException; +import org.hibernate.EntityNameResolver; import org.hibernate.collection.PersistentCollection; import org.hibernate.engine.ActionQueue; import org.hibernate.engine.CollectionEntry; @@ -127,6 +128,7 @@ import org.hibernate.proxy.LazyInitializer; import org.hibernate.stat.SessionStatistics; import org.hibernate.stat.SessionStatisticsImpl; import org.hibernate.tuple.DynamicMapInstantiator; +import org.hibernate.tuple.entity.PojoEntityTuplizer; import org.hibernate.type.Type; import org.hibernate.util.ArrayHelper; import org.hibernate.util.CollectionHelper; @@ -175,6 +177,30 @@ public final class SessionImpl extends AbstractSessionImpl private transient Session rootSession; private transient Map childSessionsByEntityMode; + private EntityNameResolver entityNameResolver = new EntityNameResolver() { + public String resolveEntityName(Object entity) { + String entityName = interceptor.getEntityName( entity ); + if ( entityName != null ) { + return entityName; + } + + Iterator itr = factory.iterateEntityNameResolvers( entityMode ); + while ( itr.hasNext() ) { + final EntityNameResolver resolver = ( EntityNameResolver ) itr.next(); + entityName = resolver.resolveEntityName( entity ); + if ( entityName != null ) { + break; + } + } + if ( entityName != null ) { + return entityName; + } + + // the old-time stand-by... + return entity.getClass().getName(); + } + }; + /** * Constructor used in building "child sessions". * @@ -1724,23 +1750,7 @@ public final class SessionImpl extends AbstractSessionImpl public String guessEntityName(Object object) throws HibernateException { errorIfClosed(); - String entity = interceptor.getEntityName( object ); - if ( entity == null ) { - if ( object instanceof Map ) { - entity = (String) ( (Map) object ).get( DynamicMapInstantiator.KEY ); - if ( entity == null ) { - throw new HibernateException( "could not determine type of dynamic entity" ); - } - } - else if ( object instanceof Element ) { - // TODO : really need to keep a map of nodeName -> entityName, but that would mean nodeName being distinct - entity = ( (Element) object ).getName(); - } - else { - entity = object.getClass().getName(); - } - } - return entity; + return entityNameResolver.resolveEntityName( object ); } public void cancelQuery() throws HibernateException { diff --git a/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index c6b94ec4c3..548d8de882 100644 --- a/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -132,7 +132,6 @@ public abstract class AbstractEntityPersister private final boolean isLazyPropertiesCacheable; private final CacheEntryStructure cacheEntryStructure; private final EntityMetamodel entityMetamodel; - private final Map entityNameBySubclass = new HashMap(); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private final String[] rootTableKeyColumnNames; @@ -456,15 +455,6 @@ public abstract class AbstractEntityPersister (CacheEntryStructure) new UnstructuredCacheEntry(); this.entityMetamodel = new EntityMetamodel( persistentClass, factory ); - - if ( persistentClass.hasPojoRepresentation() ) { - //TODO: this is currently specific to pojos, but need to be available for all entity-modes - Iterator iter = persistentClass.getSubclassIterator(); - while ( iter.hasNext() ) { - PersistentClass pc = ( PersistentClass ) iter.next(); - entityNameBySubclass.put( pc.getMappedClass(), pc.getEntityName() ); - } - } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ int batch = persistentClass.getBatchSize(); @@ -3302,10 +3292,6 @@ public abstract class AbstractEntityPersister return entityMetamodel.getEntityType(); } - private String getSubclassEntityName(Class clazz) { - return ( String ) entityNameBySubclass.get( clazz ); - } - public boolean isPolymorphic() { return entityMetamodel.isPolymorphic(); } @@ -3694,29 +3680,17 @@ public abstract class AbstractEntityPersister getTuplizer( entityMode ).resetIdentifier( entity, currentId, currentVersion ); } - public EntityPersister getSubclassEntityPersister(Object instance, SessionFactoryImplementor factory, EntityMode entityMode) { + public EntityPersister getSubclassEntityPersister( + Object instance, + SessionFactoryImplementor factory, + EntityMode entityMode) { if ( !hasSubclasses() ) { return this; } else { - // TODO : really need a way to do something like : - // getTuplizer(entityMode).determineConcreteSubclassEntityName(instance) - Class clazz = instance.getClass(); - if ( clazz == getMappedClass( entityMode ) ) { - return this; - } - else { - String subclassEntityName = getSubclassEntityName( clazz ); - if ( subclassEntityName == null ) { - throw new HibernateException( - "instance not of expected entity type: " + clazz.getName() + - " is not a: " + getEntityName() - ); - } - else { - return factory.getEntityPersister( subclassEntityName ); - } - } + final String concreteEntityName = getTuplizer( entityMode ) + .determineConcreteSubclassEntityName( instance, factory ); + return factory.getEntityPersister( concreteEntityName ); } } diff --git a/core/src/main/java/org/hibernate/persister/entity/EntityPersister.java b/core/src/main/java/org/hibernate/persister/entity/EntityPersister.java index ee74854437..e07df29d6f 100644 --- a/core/src/main/java/org/hibernate/persister/entity/EntityPersister.java +++ b/core/src/main/java/org/hibernate/persister/entity/EntityPersister.java @@ -668,15 +668,35 @@ public interface EntityPersister extends OptimisticCacheSource { public boolean hasUninitializedLazyProperties(Object object, EntityMode entityMode); /** - * Set the identifier and version of the given instance back - * to its "unsaved" value, returning the id - * @param currentId TODO - * @param currentVersion TODO + * Set the identifier and version of the given instance back to its "unsaved" value. + * + * @param entity The entity instance + * @param currentId The currently assigned identifier value. + * @param currentVersion The currently assigned version value. + * @param entityMode The entity mode represented by the entity instance. */ public void resetIdentifier(Object entity, Serializable currentId, Object currentVersion, EntityMode entityMode); /** - * Get the persister for an instance of this class or a subclass + * A request has already identified the entity-name of this persister as the mapping for the given instance. + * However, we still need to account for possible subclassing and potentially re-route to the more appropriate + * persister. + *
+ * For example, a request names Animal as the entity-name which gets resolved to this persister. But the + * actual instance is really an instance of Cat which is a subclass of Animal. So, here the + * Animal persister is being asked to return the persister specific to Cat. + * + * It is also possible that the instance is actually an Animal instance in the above example in which + * case we would retrn this from this method. + * + * @param instance The entity instance + * @param factory Reference to the SessionFactory + * @param entityMode The entity mode represented by the entity instance. + * + * @return The appropriate persister + * + * @throws HibernateException Indicates that instance was deemed to not be a subclass of the entity mapped by + * this persister. */ public EntityPersister getSubclassEntityPersister(Object instance, SessionFactoryImplementor factory, EntityMode entityMode); } diff --git a/core/src/main/java/org/hibernate/tuple/EntityModeToTuplizerMapping.java b/core/src/main/java/org/hibernate/tuple/EntityModeToTuplizerMapping.java index 48d006631e..faf4aacf7b 100644 --- a/core/src/main/java/org/hibernate/tuple/EntityModeToTuplizerMapping.java +++ b/core/src/main/java/org/hibernate/tuple/EntityModeToTuplizerMapping.java @@ -54,6 +54,15 @@ public abstract class EntityModeToTuplizerMapping implements Serializable { tuplizers.put( entityMode, tuplizer ); } + /** + * Allow iteration over all defined {@link Tuplizer Tuplizers}. + * + * @return Iterator over defined tuplizers + */ + public Iterator iterateTuplizers() { + return tuplizers.values().iterator(); + } + /** * Given a supposed instance of an entity/component, guess its entity mode. * diff --git a/core/src/main/java/org/hibernate/tuple/entity/AbstractEntityTuplizer.java b/core/src/main/java/org/hibernate/tuple/entity/AbstractEntityTuplizer.java index e3d35b3b2c..a7c5f6293b 100644 --- a/core/src/main/java/org/hibernate/tuple/entity/AbstractEntityTuplizer.java +++ b/core/src/main/java/org/hibernate/tuple/entity/AbstractEntityTuplizer.java @@ -73,13 +73,6 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer { private final AbstractComponentType identifierMapperType; - /** - * Return the entity-mode handled by this tuplizer instance. - * - * @return The entity-mode - */ - protected abstract EntityMode getEntityMode(); - /** * Build an appropriate Getter for the given property. * diff --git a/core/src/main/java/org/hibernate/tuple/entity/Dom4jEntityTuplizer.java b/core/src/main/java/org/hibernate/tuple/entity/Dom4jEntityTuplizer.java index 1865b1891e..a1948b8e5d 100644 --- a/core/src/main/java/org/hibernate/tuple/entity/Dom4jEntityTuplizer.java +++ b/core/src/main/java/org/hibernate/tuple/entity/Dom4jEntityTuplizer.java @@ -35,6 +35,8 @@ import org.hibernate.property.Getter; import org.hibernate.property.Setter; import org.hibernate.EntityMode; import org.hibernate.HibernateException; +import org.hibernate.EntityNameResolver; +import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.tuple.Instantiator; import org.hibernate.tuple.Dom4jInstantiator; import org.hibernate.type.AbstractComponentType; @@ -44,8 +46,9 @@ import org.slf4j.LoggerFactory; import java.io.Serializable; import java.util.HashSet; -import java.util.Set; import java.util.Iterator; +import java.util.Map; +import java.util.HashMap; /** * An {@link EntityTuplizer} specific to the dom4j entity mode. @@ -57,17 +60,21 @@ public class Dom4jEntityTuplizer extends AbstractEntityTuplizer { static final Logger log = LoggerFactory.getLogger( Dom4jEntityTuplizer.class ); - private Set subclassNodeNames = new HashSet(); + private Map inheritenceNodeNameMap = new HashMap(); Dom4jEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) { - super(entityMetamodel, mappedEntity); + super( entityMetamodel, mappedEntity ); + inheritenceNodeNameMap.put( mappedEntity.getNodeName(), mappedEntity.getEntityName() ); Iterator itr = mappedEntity.getSubclassClosureIterator(); while( itr.hasNext() ) { final PersistentClass mapping = ( PersistentClass ) itr.next(); - subclassNodeNames.add( mapping.getNodeName() ); + inheritenceNodeNameMap.put( mapping.getNodeName(), mapping.getEntityName() ); } } + /** + * {@inheritDoc} + */ public EntityMode getEntityMode() { return EntityMode.DOM4J; } @@ -85,18 +92,30 @@ public class Dom4jEntityTuplizer extends AbstractEntityTuplizer { } } + /** + * {@inheritDoc} + */ protected Getter buildPropertyGetter(Property mappedProperty, PersistentClass mappedEntity) { return buildPropertyAccessor(mappedProperty).getGetter( null, mappedProperty.getName() ); } + /** + * {@inheritDoc} + */ protected Setter buildPropertySetter(Property mappedProperty, PersistentClass mappedEntity) { return buildPropertyAccessor(mappedProperty).getSetter( null, mappedProperty.getName() ); } + /** + * {@inheritDoc} + */ protected Instantiator buildInstantiator(PersistentClass persistentClass) { return new Dom4jInstantiator( persistentClass ); } + /** + * {@inheritDoc} + */ public Serializable getIdentifier(Object entityOrId) throws HibernateException { if (entityOrId instanceof Element) { return super.getIdentifier(entityOrId); @@ -107,6 +126,9 @@ public class Dom4jEntityTuplizer extends AbstractEntityTuplizer { } } + /** + * {@inheritDoc} + */ protected ProxyFactory buildProxyFactory(PersistentClass mappingInfo, Getter idGetter, Setter idSetter) { HashSet proxyInterfaces = new HashSet(); proxyInterfaces.add( HibernateProxy.class ); @@ -132,15 +154,73 @@ public class Dom4jEntityTuplizer extends AbstractEntityTuplizer { return pf; } + /** + * {@inheritDoc} + */ public Class getMappedClass() { return Element.class; } + /** + * {@inheritDoc} + */ public Class getConcreteProxyClass() { return Element.class; } + /** + * {@inheritDoc} + */ public boolean isInstrumented() { return false; } + + /** + * {@inheritDoc} + */ + public EntityNameResolver[] getEntityNameResolvers() { + return new EntityNameResolver[] { new BasicEntityNameResolver( getEntityName(), inheritenceNodeNameMap ) }; + } + + /** + * {@inheritDoc} + */ + public String determineConcreteSubclassEntityName(Object entityInstance, SessionFactoryImplementor factory) { + return ( String ) inheritenceNodeNameMap.get( extractNodeName( ( Element ) entityInstance ) ); + } + + public static String extractNodeName(Element element) { + return element.getName(); + } + + public static class BasicEntityNameResolver implements EntityNameResolver { + private final String rootEntityName; + private final Map nodeNameToEntityNameMap; + + public BasicEntityNameResolver(String rootEntityName, Map nodeNameToEntityNameMap) { + this.rootEntityName = rootEntityName; + this.nodeNameToEntityNameMap = nodeNameToEntityNameMap; + } + + /** + * {@inheritDoc} + */ + public String resolveEntityName(Object entity) { + return ( String ) nodeNameToEntityNameMap.get( extractNodeName( ( Element ) entity ) ); + } + + /** + * {@inheritDoc} + */ + public boolean equals(Object obj) { + return rootEntityName.equals( ( ( BasicEntityNameResolver ) obj ).rootEntityName ); + } + + /** + * {@inheritDoc} + */ + public int hashCode() { + return rootEntityName.hashCode(); + } + } } diff --git a/core/src/main/java/org/hibernate/tuple/entity/DynamicMapEntityTuplizer.java b/core/src/main/java/org/hibernate/tuple/entity/DynamicMapEntityTuplizer.java index 43355b2690..b5c03f6881 100644 --- a/core/src/main/java/org/hibernate/tuple/entity/DynamicMapEntityTuplizer.java +++ b/core/src/main/java/org/hibernate/tuple/entity/DynamicMapEntityTuplizer.java @@ -28,6 +28,8 @@ import java.util.Map; import org.hibernate.EntityMode; import org.hibernate.HibernateException; +import org.hibernate.EntityNameResolver; +import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.tuple.Instantiator; import org.hibernate.tuple.DynamicMapInstantiator; import org.hibernate.mapping.PersistentClass; @@ -112,4 +114,37 @@ public class DynamicMapEntityTuplizer extends AbstractEntityTuplizer { public boolean isInstrumented() { return false; } + + public EntityNameResolver[] getEntityNameResolvers() { + return new EntityNameResolver[] { BasicEntityNameResolver.INSTANCE }; + } + + public String determineConcreteSubclassEntityName(Object entityInstance, SessionFactoryImplementor factory) { + // TODO : do we need an explicit isInstance check here, or is that asserted prior to here? + return extractEmbeddedEntityName( ( Map ) entityInstance ); + } + + public static String extractEmbeddedEntityName(Map entity) { + return ( String ) entity.get( DynamicMapInstantiator.KEY ); + } + + public static class BasicEntityNameResolver implements EntityNameResolver { + public static final BasicEntityNameResolver INSTANCE = new BasicEntityNameResolver(); + + public String resolveEntityName(Object entity) { + final String entityName = extractEmbeddedEntityName( ( Map ) entity ); + if ( entityName == null ) { + throw new HibernateException( "Could not determine type of dynamic map entity" ); + } + return entityName; + } + + public boolean equals(Object obj) { + return getClass().equals( obj.getClass() ); + } + + public int hashCode() { + return getClass().hashCode(); + } + } } diff --git a/core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java b/core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java index 3cbef10f7c..2bda01cf07 100644 --- a/core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java +++ b/core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java @@ -122,21 +122,10 @@ public class EntityMetamodel implements Serializable { private final boolean inherited; private final boolean hasSubclasses; private final Set subclassEntityNames = new HashSet(); + private final Map entityNameByInheritenceClassNameMap = new HashMap(); private final EntityEntityModeToTuplizerMapping tuplizerMapping; - public EntityTuplizer getTuplizer(EntityMode entityMode) { - return (EntityTuplizer) tuplizerMapping.getTuplizer( entityMode ); - } - - public EntityTuplizer getTuplizerOrNull(EntityMode entityMode) { - return ( EntityTuplizer ) tuplizerMapping.getTuplizerOrNull( entityMode ); - } - - public EntityMode guessEntityMode(Object object) { - return tuplizerMapping.guessEntityMode( object ); - } - public EntityMetamodel(PersistentClass persistentClass, SessionFactoryImplementor sessionFactory) { this.sessionFactory = sessionFactory; @@ -322,6 +311,15 @@ public class EntityMetamodel implements Serializable { } subclassEntityNames.add( name ); + if ( persistentClass.hasPojoRepresentation() ) { + entityNameByInheritenceClassNameMap.put( persistentClass.getMappedClass(), persistentClass.getEntityName() ); + iter = persistentClass.getSubclassIterator(); + while ( iter.hasNext() ) { + final PersistentClass pc = ( PersistentClass ) iter.next(); + entityNameByInheritenceClassNameMap.put( pc.getMappedClass(), pc.getEntityName() ); + } + } + tuplizerMapping = new EntityEntityModeToTuplizerMapping( persistentClass, this ); } @@ -395,6 +393,22 @@ public class EntityMetamodel implements Serializable { } } + public EntityEntityModeToTuplizerMapping getTuplizerMapping() { + return tuplizerMapping; + } + + public EntityTuplizer getTuplizer(EntityMode entityMode) { + return (EntityTuplizer) tuplizerMapping.getTuplizer( entityMode ); + } + + public EntityTuplizer getTuplizerOrNull(EntityMode entityMode) { + return ( EntityTuplizer ) tuplizerMapping.getTuplizerOrNull( entityMode ); + } + + public EntityMode guessEntityMode(Object object) { + return tuplizerMapping.guessEntityMode( object ); + } + public int[] getNaturalIdentifierProperties() { return naturalIdPropertyNumbers; } @@ -555,6 +569,16 @@ public class EntityMetamodel implements Serializable { return isAbstract; } + /** + * Return the entity-name mapped to the given class within our inheritence hierarchy, if any. + * + * @param inheritenceClass The class for which to resolve the entity-name. + * @return The mapped entity-name, or null if no such mapping was found. + */ + public String findEntityNameByEntityClass(Class inheritenceClass) { + return ( String ) entityNameByInheritenceClassNameMap.get( inheritenceClass.getName() ); + } + public String toString() { return "EntityMetamodel(" + name + ':' + ArrayHelper.toString(properties) + ')'; } diff --git a/core/src/main/java/org/hibernate/tuple/entity/EntityTuplizer.java b/core/src/main/java/org/hibernate/tuple/entity/EntityTuplizer.java index 207c83bfd2..7f58a592ce 100644 --- a/core/src/main/java/org/hibernate/tuple/entity/EntityTuplizer.java +++ b/core/src/main/java/org/hibernate/tuple/entity/EntityTuplizer.java @@ -28,8 +28,11 @@ import java.io.Serializable; import java.util.Map; import org.hibernate.HibernateException; +import org.hibernate.EntityNameResolver; +import org.hibernate.EntityMode; import org.hibernate.tuple.Tuplizer; import org.hibernate.engine.SessionImplementor; +import org.hibernate.engine.SessionFactoryImplementor; /** * Defines further responsibilities reagarding tuplization based on @@ -42,6 +45,12 @@ import org.hibernate.engine.SessionImplementor; * @author Steve Ebersole */ public interface EntityTuplizer extends Tuplizer { + /** + * Return the entity-mode handled by this tuplizer instance. + * + * @return The entity-mode + */ + public EntityMode getEntityMode(); /** * Create an entity instance initialized with the given identifier. @@ -176,11 +185,11 @@ public interface EntityTuplizer extends Tuplizer { */ public boolean isValidatableImplementor(); - // TODO: getConcreteProxyClass() is solely used (externally) to perform narrowProxy() - // would be great to fully encapsulate that narrowProxy() functionality within the - // Tuplizer, itself, with a Tuplizer.narrowProxy(..., PersistentContext) method /** * Returns the java class to which generated proxies will be typed. + * + * todo : look at fully encapsulating {@link org.hibernate.engine.PersistenceContext#narrowProxy} here, + * since that is the only external use of this method * * @return The java class to which generated proxies will be typed */ @@ -198,4 +207,35 @@ public interface EntityTuplizer extends Tuplizer { * Is it an instrumented POJO? */ public boolean isInstrumented(); + + /** + * Get any {@link EntityNameResolver EntityNameResolvers} associated with this {@link Tuplizer}. + * + * @return The associated resolvers. May be null or empty. + */ + public EntityNameResolver[] getEntityNameResolvers(); + + /** + * Given an entity instance, determine the most appropriate (most targeted) entity-name which represents it. + * This is called in situations where we already know an entity name for the given entityInstance; we are being + * asked to determine if there is a more appropriate entity-name to use, specifically within an inheritence + * hierarchy. + * + * For example, consider a case where a user calls session.update( "Animal", cat );. Here, the + * user has explicitly provided Animal as the entity-name. However, they have passed in an instance + * of Cat which is a subclass of Animal. In this case, we would return Cat as the + * entity-name. + * + * null may be returned from calls to this method. The meaining of null in that case is assumed + * to be that we should use whatever explicit entity-name the user provided (Animal rather than Cat + * in the example above). + * + * @param entityInstance The entity instance. + * @param factory Reference to the SessionFactory. + * + * @return The most appropriate entity name to use. + * + * @throws HibernateException If we are unable to determine an entity-name within the inheritence hierarchy. + */ + public String determineConcreteSubclassEntityName(Object entityInstance, SessionFactoryImplementor factory); } diff --git a/core/src/main/java/org/hibernate/tuple/entity/PojoEntityTuplizer.java b/core/src/main/java/org/hibernate/tuple/entity/PojoEntityTuplizer.java index 2b3d10b953..001ebe63d7 100644 --- a/core/src/main/java/org/hibernate/tuple/entity/PojoEntityTuplizer.java +++ b/core/src/main/java/org/hibernate/tuple/entity/PojoEntityTuplizer.java @@ -36,6 +36,7 @@ import org.slf4j.LoggerFactory; import org.hibernate.EntityMode; import org.hibernate.HibernateException; import org.hibernate.MappingException; +import org.hibernate.EntityNameResolver; import org.hibernate.tuple.Instantiator; import org.hibernate.tuple.PojoInstantiator; import org.hibernate.bytecode.ReflectionOptimizer; @@ -43,6 +44,7 @@ import org.hibernate.cfg.Environment; import org.hibernate.classic.Lifecycle; import org.hibernate.classic.Validatable; import org.hibernate.engine.SessionImplementor; +import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.intercept.FieldInterceptor; import org.hibernate.intercept.FieldInterceptionHelper; import org.hibernate.mapping.PersistentClass; @@ -70,7 +72,7 @@ public class PojoEntityTuplizer extends AbstractEntityTuplizer { private final boolean lifecycleImplementor; private final boolean validatableImplementor; private final Set lazyPropertyNames = new HashSet(); - private ReflectionOptimizer optimizer; + private final ReflectionOptimizer optimizer; public PojoEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) { super( entityMetamodel, mappedEntity ); @@ -109,6 +111,9 @@ public class PojoEntityTuplizer extends AbstractEntityTuplizer { } + /** + * {@inheritDoc} + */ protected ProxyFactory buildProxyFactory(PersistentClass persistentClass, Getter idGetter, Setter idSetter) { // determine the id getter and setter methods from the proxy interface (if any) // determine all interfaces needed by the resulting proxy @@ -199,11 +204,14 @@ public class PojoEntityTuplizer extends AbstractEntityTuplizer { } protected ProxyFactory buildProxyFactoryInternal(PersistentClass persistentClass, Getter idGetter, Setter idSetter) { - // TODO : YUCK!!! finx after HHH-1907 is complete + // TODO : YUCK!!! fix after HHH-1907 is complete return Environment.getBytecodeProvider().getProxyFactoryFactory().buildProxyFactory(); // return getFactory().getSettings().getBytecodeProvider().getProxyFactoryFactory().buildProxyFactory(); } + /** + * {@inheritDoc} + */ protected Instantiator buildInstantiator(PersistentClass persistentClass) { if ( optimizer == null ) { return new PojoInstantiator( persistentClass, null ); @@ -213,6 +221,9 @@ public class PojoEntityTuplizer extends AbstractEntityTuplizer { } } + /** + * {@inheritDoc} + */ public void setPropertyValues(Object entity, Object[] values) throws HibernateException { if ( !getEntityMetamodel().hasLazyProperties() && optimizer != null && optimizer.getAccessOptimizer() != null ) { setPropertyValuesWithOptimizer( entity, values ); @@ -222,6 +233,9 @@ public class PojoEntityTuplizer extends AbstractEntityTuplizer { } } + /** + * {@inheritDoc} + */ public Object[] getPropertyValues(Object entity) throws HibernateException { if ( shouldGetAllProperties( entity ) && optimizer != null && optimizer.getAccessOptimizer() != null ) { return getPropertyValuesWithOptimizer( entity ); @@ -231,6 +245,9 @@ public class PojoEntityTuplizer extends AbstractEntityTuplizer { } } + /** + * {@inheritDoc} + */ public Object[] getPropertyValuesToInsert(Object entity, Map mergeMap, SessionImplementor session) throws HibernateException { if ( shouldGetAllProperties( entity ) && optimizer != null && optimizer.getAccessOptimizer() != null ) { return getPropertyValuesWithOptimizer( entity ); @@ -248,36 +265,60 @@ public class PojoEntityTuplizer extends AbstractEntityTuplizer { return optimizer.getAccessOptimizer().getPropertyValues( object ); } + /** + * {@inheritDoc} + */ public EntityMode getEntityMode() { return EntityMode.POJO; } + /** + * {@inheritDoc} + */ public Class getMappedClass() { return mappedClass; } + /** + * {@inheritDoc} + */ public boolean isLifecycleImplementor() { return lifecycleImplementor; } + /** + * {@inheritDoc} + */ public boolean isValidatableImplementor() { return validatableImplementor; } + /** + * {@inheritDoc} + */ protected Getter buildPropertyGetter(Property mappedProperty, PersistentClass mappedEntity) { return mappedProperty.getGetter( mappedEntity.getMappedClass() ); } + /** + * {@inheritDoc} + */ protected Setter buildPropertySetter(Property mappedProperty, PersistentClass mappedEntity) { return mappedProperty.getSetter( mappedEntity.getMappedClass() ); } + /** + * {@inheritDoc} + */ public Class getConcreteProxyClass() { return proxyInterface; } //TODO: need to make the majority of this functionality into a top-level support class for custom impl support + /** + * {@inheritDoc} + */ public void afterInitialize(Object entity, boolean lazyPropertiesAreUnfetched, SessionImplementor session) { if ( isInstrumented() ) { Set lazyProps = lazyPropertiesAreUnfetched && getEntityMetamodel().hasLazyProperties() ? @@ -288,6 +329,9 @@ public class PojoEntityTuplizer extends AbstractEntityTuplizer { } } + /** + * {@inheritDoc} + */ public boolean hasUninitializedLazyProperties(Object entity) { if ( getEntityMetamodel().hasLazyProperties() ) { FieldInterceptor callback = FieldInterceptionHelper.extractFieldInterceptor( entity ); @@ -298,8 +342,37 @@ public class PojoEntityTuplizer extends AbstractEntityTuplizer { } } + /** + * {@inheritDoc} + */ public boolean isInstrumented() { return FieldInterceptionHelper.isInstrumented( getMappedClass() ); } + /** + * {@inheritDoc} + */ + public String determineConcreteSubclassEntityName(Object entityInstance, SessionFactoryImplementor factory) { + final Class concreteEntityClass = entityInstance.getClass(); + if ( concreteEntityClass == getMappedClass() ) { + return getEntityName(); + } + else { + String entityName = getEntityMetamodel().findEntityNameByEntityClass( concreteEntityClass ); + if ( entityName == null ) { + throw new HibernateException( + "Unable to resolve entity name from Class [" + concreteEntityClass.getName() + "]" + + " expected instance/subclass of [" + getEntityName() + "]" + ); + } + return entityName; + } + } + + /** + * {@inheritDoc} + */ + public EntityNameResolver[] getEntityNameResolvers() { + return null; + } } diff --git a/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer2/Customer.hbm.xml b/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer2/Customer.hbm.xml new file mode 100644 index 0000000000..0dc7220cfd --- /dev/null +++ b/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer2/Customer.hbm.xml @@ -0,0 +1,72 @@ + + + + + +