diff --git a/documentation/manual/src/main/docbook/en-US/content/persistent_classes.xml b/documentation/manual/src/main/docbook/en-US/content/persistent_classes.xml index c625a0bc06..198cd645d4 100644 --- a/documentation/manual/src/main/docbook/en-US/content/persistent_classes.xml +++ b/documentation/manual/src/main/docbook/en-US/content/persistent_classes.xml @@ -551,11 +551,157 @@ public class CustomMapTuplizerImpl return new CustomMap(); } } -}]]>m +}]]> + + EntityNameResolvers + + + The org.hibernate.EntityNameResolver interface is a contract for resolving the + entity name of a given entity instance. The interface defines a single method resolveEntityName + which is passed the entity instance and is expected to return the appropriate entity name (null is allowed and + would indicate that the resolver does not know how to resolve the entity name of the given entity instance). + Generally speaking, an org.hibernate.EntityNameResolver is going to be most + useful in the case of dynamic models. One example might be using proxied interfaces as your domain model. The + hibernate test suite has an example of this exact style of usage under the + org.hibernate.test.dynamicentity.tuplizer2. Here is some of the code from that package + for illustration. + + + +/** + * A very trivial JDK Proxy InvocationHandler implementation where we proxy an interface as + * the domain model and simply store persistent state in an internal Map. This is an extremely + * trivial example meant only for illustration. + */ +public final class DataProxyHandler implements InvocationHandler { + private String entityName; + private HashMap data = new HashMap(); + + public DataProxyHandler(String entityName, Serializable id) { + this.entityName = entityName; + data.put( "Id", id ); + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String methodName = method.getName(); + if ( methodName.startsWith( "set" ) ) { + String propertyName = methodName.substring( 3 ); + data.put( propertyName, args[0] ); + } + else if ( methodName.startsWith( "get" ) ) { + String propertyName = methodName.substring( 3 ); + return data.get( propertyName ); + } + else if ( "toString".equals( methodName ) ) { + return entityName + "#" + data.get( "Id" ); + } + else if ( "hashCode".equals( methodName ) ) { + return new Integer( this.hashCode() ); + } + return null; + } + + public String getEntityName() { + return entityName; + } + + public HashMap getData() { + return data; + } +} + +/** + * + */ +public class ProxyHelper { + public static String extractEntityName(Object object) { + // Our custom java.lang.reflect.Proxy instances actually bundle + // their appropriate entity name, so we simply extract it from there + // if this represents one of our proxies; otherwise, we return null + if ( Proxy.isProxyClass( object.getClass() ) ) { + InvocationHandler handler = Proxy.getInvocationHandler( object ); + if ( DataProxyHandler.class.isAssignableFrom( handler.getClass() ) ) { + DataProxyHandler myHandler = ( DataProxyHandler ) handler; + return myHandler.getEntityName(); + } + } + return null; + } + + // various other utility methods .... + +} + +/** + * The EntityNameResolver implementation. + * IMPL NOTE : An EntityNameResolver really defines a strategy for how entity names should be + * resolved. Since this particular impl can handle resolution for all of our entities we want to + * take advantage of the fact that SessionFactoryImpl keeps these in a Set so that we only ever + * have one instance registered. Why? Well, when it comes time to resolve an entity name, + * Hibernate must iterate over all the registered resolvers. So keeping that number down + * helps that process be as speedy as possible. Hence the equals and hashCode impls + */ +public 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(); + } +} + +public class MyEntityTuplizer extends PojoEntityTuplizer { + public MyEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) { + super( entityMetamodel, mappedEntity ); + } + + public EntityNameResolver[] getEntityNameResolvers() { + return new EntityNameResolver[] { MyEntityNameResolver.INSTANCE }; + } + + public String determineConcreteSubclassEntityName(Object entityInstance, SessionFactoryImplementor factory) { + String entityName = ProxyHelper.extractEntityName( entityInstance ); + if ( entityName == null ) { + entityName = super.determineConcreteSubclassEntityName( entityInstance, factory ); + } + return entityName; + } + + ... +} + + + + In order to register an org.hibernate.EntityNameResolver users must either: + + + + Implement a custom Tuplizer, implementing + the getEntityNameResolvers method. + + + + + Register it with the org.hibernate.impl.SessionFactoryImpl (which is the + implementation class for org.hibernate.SessionFactory) using the + registerEntityNameResolver method. + + + + + +