HHH-3515 : EntityNameResolver

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@15258 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2008-10-07 15:54:42 +00:00
parent 86eeb9be34
commit c3d4758f1e
16 changed files with 815 additions and 85 deletions

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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 {

View File

@ -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 );
}
}

View File

@ -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.
* <p/>
* For example, a request names <tt>Animal</tt> as the entity-name which gets resolved to this persister. But the
* actual instance is really an instance of <tt>Cat</tt> which is a subclass of <tt>Animal</tt>. So, here the
* <tt>Animal</tt> persister is being asked to return the persister specific to <tt>Cat</tt>.
* <p/>
* It is also possible that the instance is actually an <tt>Animal</tt> instance in the above example in which
* case we would retrn <tt>this</tt> 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);
}

View File

@ -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.
*

View File

@ -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.
*

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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) + ')';
}

View File

@ -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.
* <p/>
* 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.
* <p/>
* For example, consider a case where a user calls <tt>session.update( "Animal", cat );</tt>. Here, the
* user has explicitly provided <tt>Animal</tt> as the entity-name. However, they have passed in an instance
* of <tt>Cat</tt> which is a subclass of <tt>Animal</tt>. In this case, we would return <tt>Cat</tt> as the
* entity-name.
* <p/>
* <tt>null</tt> may be returned from calls to this method. The meaining of <tt>null</tt> in that case is assumed
* to be that we should use whatever explicit entity-name the user provided (<tt>Animal</tt> rather than <tt>Cat</tt>
* 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);
}

View File

@ -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;
}
}

View File

@ -0,0 +1,72 @@
<?xml version="1.0"?>
<!--
~ 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
~
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.test.dynamicentity">
<class name="Person" table="t_person" discriminator-value="person" abstract="false">
<tuplizer class="org.hibernate.test.dynamicentity.tuplizer2.MyEntityTuplizer" entity-mode="pojo"/>
<id name="id">
<generator class="native"/>
</id>
<discriminator force="false"/>
<property name="name"/>
<many-to-one name="address" cascade="all" column="addr_id"/>
<set name="family" lazy="true" cascade="all">
<key column="pers_id"/>
<one-to-many class="Person"/>
</set>
<subclass name="Customer" discriminator-value="customer" abstract="false">
<tuplizer class="org.hibernate.test.dynamicentity.tuplizer2.MyEntityTuplizer" entity-mode="pojo"/>
<many-to-one name="company" cascade="none" column="comp_id"/>
</subclass>
</class>
<class name="Company" table="t_company" abstract="false">
<tuplizer class="org.hibernate.test.dynamicentity.tuplizer2.MyEntityTuplizer" entity-mode="pojo"/>
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
</class>
<class name="Address" table="t_address" abstract="false">
<tuplizer class="org.hibernate.test.dynamicentity.tuplizer2.MyEntityTuplizer" entity-mode="pojo"/>
<id name="id">
<generator class="native"/>
</id>
<property name="street"/>
<property name="city"/>
<property name="postalCode"/>
</class>
</hibernate-mapping>

View File

@ -0,0 +1,149 @@
/*
* 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.test.dynamicentity.tuplizer2;
import org.hibernate.test.dynamicentity.Company;
import org.hibernate.test.dynamicentity.ProxyHelper;
import org.hibernate.test.dynamicentity.Customer;
import org.hibernate.test.dynamicentity.Address;
import org.hibernate.test.dynamicentity.Person;
import org.hibernate.Session;
import org.hibernate.Hibernate;
import org.hibernate.cfg.Configuration;
import org.hibernate.junit.functional.FunctionalTestCase;
import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
import junit.framework.TestSuite;
import java.util.HashSet;
/**
* Demonstrates use of Tuplizers to allow the use of JDK
* {@link java.lang.reflect.Proxy dynamic proxies} as our
* domain model.
* <p/>
* Here we plug a custom Interceptor into the session simply to
* allow us to not have to explicitly supply the appropriate entity
* name to the Session calls.
*
* @author Steve Ebersole
*/
public class ImprovedTuplizerDynamicEntityTest extends FunctionalTestCase {
public ImprovedTuplizerDynamicEntityTest(String x) {
super( x );
}
public String[] getMappings() {
return new String[] { "dynamicentity/tuplizer2/Customer.hbm.xml" };
}
public void configure(Configuration cfg) {
super.configure( cfg );
}
public static TestSuite suite() {
return new FunctionalTestClassTestSuite( ImprovedTuplizerDynamicEntityTest.class );
}
public void testIt() {
// Test saving these dyna-proxies
Session session = openSession();
session.beginTransaction();
Company company = ProxyHelper.newCompanyProxy();
company.setName( "acme" );
session.save( company );
Customer customer = ProxyHelper.newCustomerProxy();
customer.setName( "Steve" );
customer.setCompany( company );
Address address = ProxyHelper.newAddressProxy();
address.setStreet( "somewhere over the rainbow" );
address.setCity( "lawerence, kansas" );
address.setPostalCode( "toto");
customer.setAddress( address );
customer.setFamily( new HashSet() );
Person son = ProxyHelper.newPersonProxy();
son.setName( "son" );
customer.getFamily().add( son );
Person wife = ProxyHelper.newPersonProxy();
wife.setName( "wife" );
customer.getFamily().add( wife );
session.save( customer );
session.getTransaction().commit();
session.close();
assertNotNull( "company id not assigned", company.getId() );
assertNotNull( "customer id not assigned", customer.getId() );
assertNotNull( "address id not assigned", address.getId() );
assertNotNull( "son:Person id not assigned", son.getId() );
assertNotNull( "wife:Person id not assigned", wife.getId() );
// Test loading these dyna-proxies, along with flush processing
session = openSession();
session.beginTransaction();
customer = ( Customer ) session.load( Customer.class, customer.getId() );
assertFalse( "should-be-proxy was initialized", Hibernate.isInitialized( customer ) );
customer.setName( "other" );
session.flush();
assertFalse( "should-be-proxy was initialized", Hibernate.isInitialized( customer.getCompany() ) );
session.refresh( customer );
assertEquals( "name not updated", "other", customer.getName() );
assertEquals( "company association not correct", "acme", customer.getCompany().getName() );
session.getTransaction().commit();
session.close();
// Test detached entity re-attachment with these dyna-proxies
customer.setName( "Steve" );
session = openSession();
session.beginTransaction();
session.update( customer );
session.flush();
session.refresh( customer );
assertEquals( "name not updated", "Steve", customer.getName() );
session.getTransaction().commit();
session.close();
// Test querying
session = openSession();
session.beginTransaction();
int count = session.createQuery( "from Customer" ).list().size();
assertEquals( "querying dynamic entity", 1, count );
session.clear();
count = session.createQuery( "from Person" ).list().size();
assertEquals( "querying dynamic entity", 3, count );
session.getTransaction().commit();
session.close();
// test deleteing
session = openSession();
session.beginTransaction();
session.delete( company );
session.delete( customer );
session.getTransaction().commit();
session.close();
}
}

View File

@ -0,0 +1,78 @@
/*
* 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.test.dynamicentity.tuplizer2;
import org.hibernate.tuple.Instantiator;
import org.hibernate.test.dynamicentity.Customer;
import org.hibernate.test.dynamicentity.ProxyHelper;
import org.hibernate.test.dynamicentity.Company;
import org.hibernate.test.dynamicentity.Address;
import org.hibernate.test.dynamicentity.Person;
import org.hibernate.util.ReflectHelper;
import org.hibernate.HibernateException;
import java.io.Serializable;
/**
* @author Steve Ebersole
*/
public class MyEntityInstantiator implements Instantiator {
private final String entityName;
public MyEntityInstantiator(String entityName) {
this.entityName = entityName;
}
public Object instantiate(Serializable id) {
if ( Person.class.getName().equals( entityName ) ) {
return ProxyHelper.newPersonProxy( id );
}
if ( Customer.class.getName().equals( entityName ) ) {
return ProxyHelper.newCustomerProxy( id );
}
else if ( Company.class.getName().equals( entityName ) ) {
return ProxyHelper.newCompanyProxy( id );
}
else if ( Address.class.getName().equals( entityName ) ) {
return ProxyHelper.newAddressProxy( id );
}
else {
throw new IllegalArgumentException( "unknown entity for instantiation [" + entityName + "]" );
}
}
public Object instantiate() {
return instantiate( null );
}
public boolean isInstance(Object object) {
try {
return ReflectHelper.classForName( entityName ).isInstance( object );
}
catch( Throwable t ) {
throw new HibernateException( "could not get handle to entity-name as interface : " + t );
}
}
}

View File

@ -0,0 +1,87 @@
/*
* 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.test.dynamicentity.tuplizer2;
import org.hibernate.tuple.entity.PojoEntityTuplizer;
import org.hibernate.tuple.entity.EntityMetamodel;
import org.hibernate.tuple.Instantiator;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.proxy.ProxyFactory;
import org.hibernate.property.Getter;
import org.hibernate.property.Setter;
import org.hibernate.test.dynamicentity.tuplizer.MyEntityInstantiator;
import org.hibernate.test.dynamicentity.ProxyHelper;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.EntityNameResolver;
/**
* @author Steve Ebersole
*/
public class MyEntityTuplizer extends PojoEntityTuplizer {
public MyEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
super( entityMetamodel, mappedEntity );
}
public EntityNameResolver[] getEntityNameResolvers() {
return new EntityNameResolver[] { MyEntityNameResolver.INSTANCE };
}
protected Instantiator buildInstantiator(PersistentClass persistentClass) {
return new MyEntityInstantiator( persistentClass.getEntityName() );
}
public String determineConcreteSubclassEntityName(Object entityInstance, SessionFactoryImplementor factory) {
String entityName = ProxyHelper.extractEntityName( entityInstance );
if ( entityName == null ) {
entityName = super.determineConcreteSubclassEntityName( entityInstance, factory );
}
return entityName;
}
protected ProxyFactory buildProxyFactory(PersistentClass persistentClass, Getter idGetter, Setter idSetter) {
// allows defining a custom proxy factory, which is responsible for
// generating lazy proxies for a given entity.
//
// Here we simply use the default...
return super.buildProxyFactory( persistentClass, idGetter, idSetter );
}
public static class MyEntityNameResolver implements EntityNameResolver {
public static final MyEntityNameResolver INSTANCE = new MyEntityNameResolver();
public String resolveEntityName(Object entity) {
return ProxyHelper.extractEntityName( entity );
}
public boolean equals(Object obj) {
return getClass().equals( obj.getClass() );
}
public int hashCode() {
return getClass().hashCode();
}
}
}