HHH-3517 : default Tuplizer impls

git-svn-id: https://svn.jboss.org/repos/hibernate/core/branches/Branch_3_2@15289 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2008-10-08 17:23:01 +00:00
parent c24cab0092
commit 969ba9a900
10 changed files with 653 additions and 147 deletions

View File

@ -38,6 +38,7 @@ import org.hibernate.InvalidMappingException;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.MappingNotFoundException; import org.hibernate.MappingNotFoundException;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
import org.hibernate.tuple.entity.EntityTuplizerFactory;
import org.hibernate.proxy.EntityNotFoundDelegate; import org.hibernate.proxy.EntityNotFoundDelegate;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.MySQLDialect;
@ -133,6 +134,10 @@ public class Configuration implements Serializable {
protected Map sqlFunctions; protected Map sqlFunctions;
protected Map namedQueries; protected Map namedQueries;
protected Map namedSqlQueries; protected Map namedSqlQueries;
private EntityTuplizerFactory entityTuplizerFactory;
// private ComponentTuplizerFactory componentTuplizerFactory; todo : HHH-3517 and HHH-1907
/** /**
* Map<String, SqlResultSetMapping> result set name, result set description * Map<String, SqlResultSetMapping> result set name, result set description
*/ */
@ -158,6 +163,17 @@ public class Configuration implements Serializable {
protected final SettingsFactory settingsFactory; protected final SettingsFactory settingsFactory;
private transient Mapping mapping = buildMapping();
protected Configuration(SettingsFactory settingsFactory) {
this.settingsFactory = settingsFactory;
reset();
}
public Configuration() {
this( new SettingsFactory() );
}
protected void reset() { protected void reset() {
classes = new HashMap(); classes = new HashMap();
imports = new HashMap(); imports = new HashMap();
@ -182,20 +198,17 @@ public class Configuration implements Serializable {
columnNameBindingPerTable = new HashMap(); columnNameBindingPerTable = new HashMap();
namingStrategy = DefaultNamingStrategy.INSTANCE; namingStrategy = DefaultNamingStrategy.INSTANCE;
sqlFunctions = new HashMap(); sqlFunctions = new HashMap();
entityTuplizerFactory = new EntityTuplizerFactory();
// componentTuplizerFactory = new ComponentTuplizerFactory();
} }
private transient Mapping mapping = buildMapping(); public EntityTuplizerFactory getEntityTuplizerFactory() {
return entityTuplizerFactory;
protected Configuration(SettingsFactory settingsFactory) {
this.settingsFactory = settingsFactory;
reset();
} }
public Configuration() { // public ComponentTuplizerFactory getComponentTuplizerFactory() {
this( new SettingsFactory() ); // return componentTuplizerFactory;
} // }
/** /**
* Iterate the entity mappings * Iterate the entity mappings
@ -2066,11 +2079,18 @@ public class Configuration implements Serializable {
public Settings buildSettings() throws HibernateException { public Settings buildSettings() throws HibernateException {
Properties clone = ( Properties ) properties.clone(); Properties clone = ( Properties ) properties.clone();
PropertiesHelper.resolvePlaceHolders( clone ); PropertiesHelper.resolvePlaceHolders( clone );
return settingsFactory.buildSettings( clone ); return buildSettingsInternal( clone );
} }
public Settings buildSettings(Properties props) throws HibernateException { public Settings buildSettings(Properties props) throws HibernateException {
return settingsFactory.buildSettings( props ); return buildSettingsInternal( props );
}
private Settings buildSettingsInternal(Properties props) {
final Settings settings = settingsFactory.buildSettings( props );
settings.setEntityTuplizerFactory( this.getEntityTuplizerFactory() );
// settings.setComponentTuplizerFactory( this.getComponentTuplizerFactory() );
return settings;
} }
public Map getNamedSQLQueries() { public Map getNamedSQLQueries() {

View File

@ -14,6 +14,7 @@ import org.hibernate.transaction.TransactionManagerLookup;
import org.hibernate.exception.SQLExceptionConverter; import org.hibernate.exception.SQLExceptionConverter;
import org.hibernate.EntityMode; import org.hibernate.EntityMode;
import org.hibernate.ConnectionReleaseMode; import org.hibernate.ConnectionReleaseMode;
import org.hibernate.tuple.entity.EntityTuplizerFactory;
/** /**
* Settings that affect the behaviour of Hibernate at runtime. * Settings that affect the behaviour of Hibernate at runtime.
@ -67,6 +68,8 @@ public final class Settings {
private boolean dataDefinitionInTransactionSupported; private boolean dataDefinitionInTransactionSupported;
private boolean strictJPAQLCompliance; private boolean strictJPAQLCompliance;
private boolean namedQueryStartupCheckingEnabled; private boolean namedQueryStartupCheckingEnabled;
private EntityTuplizerFactory entityTuplizerFactory;
// private ComponentTuplizerFactory componentTuplizerFactory; todo : HHH-3517 and HHH-1907
// private BytecodeProvider bytecodeProvider; // private BytecodeProvider bytecodeProvider;
/** /**
@ -257,6 +260,14 @@ public final class Settings {
return namedQueryStartupCheckingEnabled; return namedQueryStartupCheckingEnabled;
} }
public EntityTuplizerFactory getEntityTuplizerFactory() {
return entityTuplizerFactory;
}
// public ComponentTuplizerFactory getComponentTuplizerFactory() {
// return componentTuplizerFactory;
// }
// package protected setters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // package protected setters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -440,6 +451,14 @@ public final class Settings {
this.namedQueryStartupCheckingEnabled = namedQueryStartupCheckingEnabled; this.namedQueryStartupCheckingEnabled = namedQueryStartupCheckingEnabled;
} }
void setEntityTuplizerFactory(EntityTuplizerFactory entityTuplizerFactory) {
this.entityTuplizerFactory = entityTuplizerFactory;
}
// void setComponentTuplizerFactory(ComponentTuplizerFactory componentTuplizerFactory) {
// this.componentTuplizerFactory = componentTuplizerFactory;
// }
// public BytecodeProvider getBytecodeProvider() { // public BytecodeProvider getBytecodeProvider() {
// return bytecodeProvider; // return bytecodeProvider;

View File

@ -5,8 +5,6 @@ import org.hibernate.tuple.Tuplizer;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.EntityMode; import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.util.ReflectHelper;
import java.util.Map; import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
@ -23,7 +21,8 @@ import java.io.Serializable;
*/ */
class ComponentEntityModeToTuplizerMapping extends EntityModeToTuplizerMapping implements Serializable { class ComponentEntityModeToTuplizerMapping extends EntityModeToTuplizerMapping implements Serializable {
private static final Class[] COMPONENT_TUP_CTOR_SIG = new Class[] { Component.class }; // todo : move this to SF per HHH-3517; also see HHH-1907 and ComponentMetamodel
private ComponentTuplizerFactory componentTuplizerFactory = new ComponentTuplizerFactory();
public ComponentEntityModeToTuplizerMapping(Component component) { public ComponentEntityModeToTuplizerMapping(Component component) {
PersistentClass owner = component.getOwner(); PersistentClass owner = component.getOwner();
@ -35,24 +34,24 @@ class ComponentEntityModeToTuplizerMapping extends EntityModeToTuplizerMapping i
} }
// Build the dynamic-map tuplizer... // Build the dynamic-map tuplizer...
Tuplizer dynamicMapTuplizer = null; Tuplizer dynamicMapTuplizer;
String tuplizerImpl = ( String ) userSuppliedTuplizerImpls.remove( EntityMode.MAP ); String tuplizerClassName = ( String ) userSuppliedTuplizerImpls.remove( EntityMode.MAP );
if ( tuplizerImpl == null ) { if ( tuplizerClassName == null ) {
dynamicMapTuplizer = new DynamicMapComponentTuplizer( component ); dynamicMapTuplizer = componentTuplizerFactory.constructDefaultTuplizer( EntityMode.MAP, component );
} }
else { else {
dynamicMapTuplizer = buildComponentTuplizer( tuplizerImpl, component ); dynamicMapTuplizer = componentTuplizerFactory.constructTuplizer( tuplizerClassName, component );
} }
// then the pojo tuplizer, using the dynamic-map tuplizer if no pojo representation is available // then the pojo tuplizer, using the dynamic-map tuplizer if no pojo representation is available
tuplizerImpl = ( String ) userSuppliedTuplizerImpls.remove( EntityMode.POJO ); tuplizerClassName = ( String ) userSuppliedTuplizerImpls.remove( EntityMode.POJO );
Tuplizer pojoTuplizer = null; Tuplizer pojoTuplizer;
if ( owner.hasPojoRepresentation() && component.hasPojoRepresentation() ) { if ( owner.hasPojoRepresentation() && component.hasPojoRepresentation() ) {
if ( tuplizerImpl == null ) { if ( tuplizerClassName == null ) {
pojoTuplizer = new PojoComponentTuplizer( component ); pojoTuplizer = componentTuplizerFactory.constructDefaultTuplizer( EntityMode.POJO, component );
} }
else { else {
pojoTuplizer = buildComponentTuplizer( tuplizerImpl, component ); pojoTuplizer = componentTuplizerFactory.constructTuplizer( tuplizerClassName, component );
} }
} }
else { else {
@ -60,14 +59,14 @@ class ComponentEntityModeToTuplizerMapping extends EntityModeToTuplizerMapping i
} }
// then dom4j tuplizer, if dom4j representation is available // then dom4j tuplizer, if dom4j representation is available
Tuplizer dom4jTuplizer = null; Tuplizer dom4jTuplizer;
tuplizerImpl = ( String ) userSuppliedTuplizerImpls.remove( EntityMode.DOM4J ); tuplizerClassName = ( String ) userSuppliedTuplizerImpls.remove( EntityMode.DOM4J );
if ( owner.hasDom4jRepresentation() ) { if ( owner.hasDom4jRepresentation() ) {
if ( tuplizerImpl == null ) { if ( tuplizerClassName == null ) {
dom4jTuplizer = new Dom4jComponentTuplizer( component ); dom4jTuplizer = componentTuplizerFactory.constructDefaultTuplizer( EntityMode.DOM4J, component );
} }
else { else {
dom4jTuplizer = buildComponentTuplizer( tuplizerImpl, component ); dom4jTuplizer = componentTuplizerFactory.constructTuplizer( tuplizerClassName, component );
} }
} }
else { else {
@ -89,21 +88,12 @@ class ComponentEntityModeToTuplizerMapping extends EntityModeToTuplizerMapping i
if ( !userSuppliedTuplizerImpls.isEmpty() ) { if ( !userSuppliedTuplizerImpls.isEmpty() ) {
Iterator itr = userSuppliedTuplizerImpls.entrySet().iterator(); Iterator itr = userSuppliedTuplizerImpls.entrySet().iterator();
while ( itr.hasNext() ) { while ( itr.hasNext() ) {
Map.Entry entry = ( Map.Entry ) itr.next(); final Map.Entry entry = ( Map.Entry ) itr.next();
EntityMode entityMode = ( EntityMode ) entry.getKey(); final EntityMode entityMode = ( EntityMode ) entry.getKey();
ComponentTuplizer tuplizer = buildComponentTuplizer( ( String ) entry.getValue(), component ); final String userTuplizerClassName = ( String ) entry.getValue();
ComponentTuplizer tuplizer = componentTuplizerFactory.constructTuplizer( userTuplizerClassName, component );
addTuplizer( entityMode, tuplizer ); addTuplizer( entityMode, tuplizer );
} }
} }
} }
private ComponentTuplizer buildComponentTuplizer(String tuplizerImpl, Component component) {
try {
Class implClass = ReflectHelper.classForName( tuplizerImpl );
return ( ComponentTuplizer ) implClass.getConstructor( COMPONENT_TUP_CTOR_SIG ).newInstance( new Object[] { component } );
}
catch( Throwable t ) {
throw new HibernateException( "Could not build tuplizer [" + tuplizerImpl + "]", t );
}
}
} }

View File

@ -0,0 +1,160 @@
/*
* 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.tuple.component;
import java.util.Map;
import java.lang.reflect.Constructor;
import java.io.Serializable;
import org.hibernate.util.FastHashMap;
import org.hibernate.util.ReflectHelper;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.mapping.Component;
/**
* A registry allowing users to define the default {@link ComponentTuplizer} class to use per {@link EntityMode}.
*
* @author Steve Ebersole
*/
public class ComponentTuplizerFactory implements Serializable {
private static final Class[] COMPONENT_TUP_CTOR_SIG = new Class[] { Component.class };
private Map defaultImplClassByMode = buildBaseMapping();
/**
* Method allowing registration of the tuplizer class to use as default for a particular entity-mode.
*
* @param entityMode The entity-mode for which to register the tuplizer class
* @param tuplizerClass The class to use as the default tuplizer for the given entity-mode.
*/
public void registerDefaultTuplizerClass(EntityMode entityMode, Class tuplizerClass) {
assert isComponentTuplizerImplementor( tuplizerClass )
: "Specified tuplizer class [" + tuplizerClass.getName() + "] does not implement " + ComponentTuplizer.class.getName();
assert hasProperConstructor( tuplizerClass )
: "Specified tuplizer class [" + tuplizerClass.getName() + "] is not properly instantiatable";
defaultImplClassByMode.put( entityMode, tuplizerClass );
}
/**
* Construct an instance of the given tuplizer class.
*
* @param tuplizerClassName The name of the tuplizer class to instantiate
* @param metadata The metadata for the component.
*
* @return The instantiated tuplizer
*
* @throws HibernateException If class name cannot be resolved to a class reference, or if the
* {@link Constructor#newInstance} call fails.
*/
public ComponentTuplizer constructTuplizer(String tuplizerClassName, Component metadata) {
try {
Class tuplizerClass = ReflectHelper.classForName( tuplizerClassName );
return constructTuplizer( tuplizerClass, metadata );
}
catch ( ClassNotFoundException e ) {
throw new HibernateException( "Could not locate specified tuplizer class [" + tuplizerClassName + "]" );
}
}
/**
* Construct an instance of the given tuplizer class.
*
* @param tuplizerClass The tuplizer class to instantiate
* @param metadata The metadata for the component.
*
* @return The instantiated tuplizer
*
* @throws HibernateException if the {@link java.lang.reflect.Constructor#newInstance} call fails.
*/
public ComponentTuplizer constructTuplizer(Class tuplizerClass, Component metadata) {
Constructor ctor = getProperConstructor( tuplizerClass );
assert ctor != null : "Unable to locate proper constructor for tuplizer [" + tuplizerClass.getName() + "]";
try {
return ( ComponentTuplizer ) ctor.newInstance( new Object[] { metadata } );
}
catch ( Throwable t ) {
throw new HibernateException( "Unable to instantiate default tuplizer [" + tuplizerClass.getName() + "]", t );
}
}
/**
* Construct am instance of the default tuplizer for the given entity-mode.
*
* @param entityMode The entity mode for which to build a default tuplizer.
* @param metadata The metadata for the component.
*
* @return The instantiated tuplizer
*
* @throws HibernateException If no default tuplizer found for that entity-mode; may be re-thrown from
* {@link #constructTuplizer} too.
*/
public ComponentTuplizer constructDefaultTuplizer(EntityMode entityMode, Component metadata) {
Class tuplizerClass = ( Class ) defaultImplClassByMode.get( entityMode );
if ( tuplizerClass == null ) {
throw new HibernateException( "could not determine default tuplizer class to use [" + entityMode + "]" );
}
return constructTuplizer( tuplizerClass, metadata );
}
private boolean isComponentTuplizerImplementor(Class tuplizerClass) {
return ReflectHelper.implementsInterface( tuplizerClass, ComponentTuplizer.class );
}
private boolean hasProperConstructor(Class tuplizerClass) {
return getProperConstructor( tuplizerClass ) != null;
}
private Constructor getProperConstructor(Class clazz) {
Constructor ctor = null;
try {
ctor = clazz.getDeclaredConstructor( COMPONENT_TUP_CTOR_SIG );
if ( ! ReflectHelper.isPublic( ctor ) ) {
try {
// found a ctor, but it was not publicly accessible so try to request accessibility
ctor.setAccessible( true );
}
catch ( SecurityException e ) {
ctor = null;
}
}
}
catch ( NoSuchMethodException ignore ) {
}
return ctor;
}
private static Map buildBaseMapping() {
Map map = new FastHashMap();
map.put( EntityMode.POJO, PojoComponentTuplizer.class );
map.put( EntityMode.DOM4J, Dom4jComponentTuplizer.class );
map.put( EntityMode.MAP, DynamicMapComponentTuplizer.class );
return map;
}
}

View File

@ -4,8 +4,6 @@ import org.hibernate.tuple.EntityModeToTuplizerMapping;
import org.hibernate.tuple.Tuplizer; import org.hibernate.tuple.Tuplizer;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.EntityMode; import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.util.ReflectHelper;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
@ -22,8 +20,6 @@ import java.io.Serializable;
*/ */
public class EntityEntityModeToTuplizerMapping extends EntityModeToTuplizerMapping implements Serializable { public class EntityEntityModeToTuplizerMapping extends EntityModeToTuplizerMapping implements Serializable {
private static final Class[] ENTITY_TUP_CTOR_SIG = new Class[] { EntityMetamodel.class, PersistentClass.class };
/** /**
* Instantiates a EntityEntityModeToTuplizerMapping based on the given * Instantiates a EntityEntityModeToTuplizerMapping based on the given
* entity mapping and metamodel definitions. * entity mapping and metamodel definitions.
@ -32,6 +28,10 @@ public class EntityEntityModeToTuplizerMapping extends EntityModeToTuplizerMappi
* @param em The entity metamodel definition. * @param em The entity metamodel definition.
*/ */
public EntityEntityModeToTuplizerMapping(PersistentClass mappedEntity, EntityMetamodel em) { public EntityEntityModeToTuplizerMapping(PersistentClass mappedEntity, EntityMetamodel em) {
final EntityTuplizerFactory entityTuplizerFactory = em.getSessionFactory()
.getSettings()
.getEntityTuplizerFactory();
// create our own copy of the user-supplied tuplizer impl map // create our own copy of the user-supplied tuplizer impl map
Map userSuppliedTuplizerImpls = new HashMap(); Map userSuppliedTuplizerImpls = new HashMap();
if ( mappedEntity.getTuplizerMap() != null ) { if ( mappedEntity.getTuplizerMap() != null ) {
@ -39,24 +39,24 @@ public class EntityEntityModeToTuplizerMapping extends EntityModeToTuplizerMappi
} }
// Build the dynamic-map tuplizer... // Build the dynamic-map tuplizer...
Tuplizer dynamicMapTuplizer = null; Tuplizer dynamicMapTuplizer;
String tuplizerImpl = ( String ) userSuppliedTuplizerImpls.remove( EntityMode.MAP ); String tuplizerImplClassName = ( String ) userSuppliedTuplizerImpls.remove( EntityMode.MAP );
if ( tuplizerImpl == null ) { if ( tuplizerImplClassName == null ) {
dynamicMapTuplizer = new DynamicMapEntityTuplizer( em, mappedEntity ); dynamicMapTuplizer = entityTuplizerFactory.constructDefaultTuplizer( EntityMode.MAP, em, mappedEntity );
} }
else { else {
dynamicMapTuplizer = buildEntityTuplizer( tuplizerImpl, mappedEntity, em ); dynamicMapTuplizer = entityTuplizerFactory.constructTuplizer( tuplizerImplClassName, em, mappedEntity );
} }
// then the pojo tuplizer, using the dynamic-map tuplizer if no pojo representation is available // then the pojo tuplizer, using the dynamic-map tuplizer if no pojo representation is available
Tuplizer pojoTuplizer = null; Tuplizer pojoTuplizer;
tuplizerImpl = ( String ) userSuppliedTuplizerImpls.remove( EntityMode.POJO ); tuplizerImplClassName = ( String ) userSuppliedTuplizerImpls.remove( EntityMode.POJO );
if ( mappedEntity.hasPojoRepresentation() ) { if ( mappedEntity.hasPojoRepresentation() ) {
if ( tuplizerImpl == null ) { if ( tuplizerImplClassName == null ) {
pojoTuplizer = new PojoEntityTuplizer( em, mappedEntity ); pojoTuplizer = entityTuplizerFactory.constructDefaultTuplizer( EntityMode.POJO, em, mappedEntity );
} }
else { else {
pojoTuplizer = buildEntityTuplizer( tuplizerImpl, mappedEntity, em ); pojoTuplizer = entityTuplizerFactory.constructTuplizer( tuplizerImplClassName, em, mappedEntity );
} }
} }
else { else {
@ -64,14 +64,14 @@ public class EntityEntityModeToTuplizerMapping extends EntityModeToTuplizerMappi
} }
// then dom4j tuplizer, if dom4j representation is available // then dom4j tuplizer, if dom4j representation is available
Tuplizer dom4jTuplizer = null; Tuplizer dom4jTuplizer;
tuplizerImpl = ( String ) userSuppliedTuplizerImpls.remove( EntityMode.DOM4J ); tuplizerImplClassName = ( String ) userSuppliedTuplizerImpls.remove( EntityMode.DOM4J );
if ( mappedEntity.hasDom4jRepresentation() ) { if ( mappedEntity.hasDom4jRepresentation() ) {
if ( tuplizerImpl == null ) { if ( tuplizerImplClassName == null ) {
dom4jTuplizer = new Dom4jEntityTuplizer( em, mappedEntity ); dom4jTuplizer = entityTuplizerFactory.constructDefaultTuplizer( EntityMode.DOM4J, em, mappedEntity );
} }
else { else {
dom4jTuplizer = buildEntityTuplizer( tuplizerImpl, mappedEntity, em ); dom4jTuplizer = entityTuplizerFactory.constructTuplizer( tuplizerImplClassName, em, mappedEntity );
} }
} }
else { else {
@ -93,21 +93,12 @@ public class EntityEntityModeToTuplizerMapping extends EntityModeToTuplizerMappi
if ( !userSuppliedTuplizerImpls.isEmpty() ) { if ( !userSuppliedTuplizerImpls.isEmpty() ) {
Iterator itr = userSuppliedTuplizerImpls.entrySet().iterator(); Iterator itr = userSuppliedTuplizerImpls.entrySet().iterator();
while ( itr.hasNext() ) { while ( itr.hasNext() ) {
Map.Entry entry = ( Map.Entry ) itr.next(); final Map.Entry entry = ( Map.Entry ) itr.next();
EntityMode entityMode = ( EntityMode ) entry.getKey(); final EntityMode entityMode = ( EntityMode ) entry.getKey();
EntityTuplizer tuplizer = buildEntityTuplizer( ( String ) entry.getValue(), mappedEntity, em ); final String tuplizerClassName = ( String ) entry.getValue();
final EntityTuplizer tuplizer = entityTuplizerFactory.constructTuplizer( tuplizerClassName, em, mappedEntity );
addTuplizer( entityMode, tuplizer ); addTuplizer( entityMode, tuplizer );
} }
} }
} }
private static EntityTuplizer buildEntityTuplizer(String className, PersistentClass pc, EntityMetamodel em) {
try {
Class implClass = ReflectHelper.classForName( className );
return ( EntityTuplizer ) implClass.getConstructor( ENTITY_TUP_CTOR_SIG ).newInstance( new Object[] { em, pc } );
}
catch( Throwable t ) {
throw new HibernateException( "Could not build tuplizer [" + className + "]", t );
}
}
} }

View File

@ -215,4 +215,4 @@ public interface EntityTuplizer extends Tuplizer {
* @throws HibernateException If we are unable to determine an entity-name within the inheritence hierarchy. * @throws HibernateException If we are unable to determine an entity-name within the inheritence hierarchy.
*/ */
public String determineConcreteSubclassEntityName(Object entityInstance, SessionFactoryImplementor factory); public String determineConcreteSubclassEntityName(Object entityInstance, SessionFactoryImplementor factory);
} }

View File

@ -0,0 +1,173 @@
/*
* 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.tuple.entity;
import java.util.Map;
import java.lang.reflect.Constructor;
import java.io.Serializable;
import org.hibernate.util.FastHashMap;
import org.hibernate.util.ReflectHelper;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.mapping.PersistentClass;
/**
* A registry allowing users to define the default {@link EntityTuplizer} class to use per {@link EntityMode}.
*
* @author Steve Ebersole
*/
public class EntityTuplizerFactory implements Serializable {
public static final Class[] ENTITY_TUP_CTOR_SIG = new Class[] { EntityMetamodel.class, PersistentClass.class };
private Map defaultImplClassByMode = buildBaseMapping();
/**
* Method allowing registration of the tuplizer class to use as default for a particular entity-mode.
*
* @param entityMode The entity-mode for which to register the tuplizer class
* @param tuplizerClass The class to use as the default tuplizer for the given entity-mode.
*/
public void registerDefaultTuplizerClass(EntityMode entityMode, Class tuplizerClass) {
assert isEntityTuplizerImplementor( tuplizerClass )
: "Specified tuplizer class [" + tuplizerClass.getName() + "] does not implement " + EntityTuplizer.class.getName();
assert hasProperConstructor( tuplizerClass )
: "Specified tuplizer class [" + tuplizerClass.getName() + "] is not properly instantiatable";
defaultImplClassByMode.put( entityMode, tuplizerClass );
}
/**
* Construct an instance of the given tuplizer class.
*
* @param tuplizerClassName The name of the tuplizer class to instantiate
* @param metamodel The metadata for the entity.
* @param persistentClass The mapping info for the entity.
*
* @return The instantiated tuplizer
*
* @throws HibernateException If class name cannot be resolved to a class reference, or if the
* {@link Constructor#newInstance} call fails.
*/
public EntityTuplizer constructTuplizer(
String tuplizerClassName,
EntityMetamodel metamodel,
PersistentClass persistentClass) {
try {
Class tuplizerClass = ReflectHelper.classForName( tuplizerClassName );
return constructTuplizer( tuplizerClass, metamodel, persistentClass );
}
catch ( ClassNotFoundException e ) {
throw new HibernateException( "Could not locate specified tuplizer class [" + tuplizerClassName + "]" );
}
}
/**
* Construct an instance of the given tuplizer class.
*
* @param tuplizerClass The tuplizer class to instantiate
* @param metamodel The metadata for the entity.
* @param persistentClass The mapping info for the entity.
*
* @return The instantiated tuplizer
*
* @throws HibernateException if the {@link Constructor#newInstance} call fails.
*/
public EntityTuplizer constructTuplizer(
Class tuplizerClass,
EntityMetamodel metamodel,
PersistentClass persistentClass) {
Constructor ctor = getProperConstructor( tuplizerClass );
assert ctor != null : "Unable to locate proper constructor for tuplizer [" + tuplizerClass.getName() + "]";
try {
return ( EntityTuplizer ) ctor.newInstance( new Object[] { metamodel, persistentClass } );
}
catch ( Throwable t ) {
throw new HibernateException( "Unable to instantiate default tuplizer [" + tuplizerClass.getName() + "]", t );
}
}
/**
* Construct am instance of the default tuplizer for the given entity-mode.
*
* @param entityMode The entity mode for which to build a default tuplizer.
* @param metamodel The entity metadata.
* @param persistentClass The entity mapping info.
*
* @return The instantiated tuplizer
*
* @throws HibernateException If no default tuplizer found for that entity-mode; may be re-thrown from
* {@link #constructTuplizer} too.
*/
public EntityTuplizer constructDefaultTuplizer(
EntityMode entityMode,
EntityMetamodel metamodel,
PersistentClass persistentClass) {
Class tuplizerClass = ( Class ) defaultImplClassByMode.get( entityMode );
if ( tuplizerClass == null ) {
throw new HibernateException( "could not determine default tuplizer class to use [" + entityMode + "]" );
}
return constructTuplizer( tuplizerClass, metamodel, persistentClass );
}
private boolean isEntityTuplizerImplementor(Class tuplizerClass) {
return ReflectHelper.implementsInterface( tuplizerClass, EntityTuplizer.class );
}
private boolean hasProperConstructor(Class tuplizerClass) {
return getProperConstructor( tuplizerClass ) != null;
}
private Constructor getProperConstructor(Class clazz) {
Constructor ctor = null;
try {
ctor = clazz.getDeclaredConstructor( ENTITY_TUP_CTOR_SIG );
if ( ! ReflectHelper.isPublic( ctor ) ) {
try {
// found a ctor, but it was not publicly accessible so try to request accessibility
ctor.setAccessible( true );
}
catch ( SecurityException e ) {
ctor = null;
}
}
}
catch ( NoSuchMethodException ignore ) {
}
return ctor;
}
private static Map buildBaseMapping() {
Map map = new FastHashMap();
map.put( EntityMode.POJO, PojoEntityTuplizer.class );
map.put( EntityMode.DOM4J, Dom4jEntityTuplizer.class );
map.put( EntityMode.MAP, DynamicMapEntityTuplizer.class );
return map;
}
}

View File

@ -16,90 +16,132 @@ import org.hibernate.property.PropertyAccessor;
import org.hibernate.type.PrimitiveType; import org.hibernate.type.PrimitiveType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
/**
* Utility class for various reflection operations.
*
* @author Gavin King
* @author Steve Ebersole
*/
public final class ReflectHelper { public final class ReflectHelper {
//TODO: this dependency is kinda Bad //TODO: this dependency is kinda Bad
private static final PropertyAccessor BASIC_PROPERTY_ACCESSOR = new BasicPropertyAccessor(); private static final PropertyAccessor BASIC_PROPERTY_ACCESSOR = new BasicPropertyAccessor();
private static final PropertyAccessor DIRECT_PROPERTY_ACCESSOR = new DirectPropertyAccessor(); private static final PropertyAccessor DIRECT_PROPERTY_ACCESSOR = new DirectPropertyAccessor();
private static final Class[] NO_CLASSES = new Class[0]; public static final Class[] NO_PARAM_SIGNATURE = new Class[0];
private static final Class[] OBJECT = new Class[] { Object.class }; public static final Object[] NO_PARAMS = new Object[0];
private static final Method OBJECT_EQUALS;
private static final Class[] NO_PARAM = new Class[] { };
public static final Class[] SINGLE_OBJECT_PARAM_SIGNATURE = new Class[] { Object.class };
private static final Method OBJECT_EQUALS;
private static final Method OBJECT_HASHCODE; private static final Method OBJECT_HASHCODE;
static { static {
Method eq; Method eq;
Method hash; Method hash;
try { try {
eq = Object.class.getMethod("equals", OBJECT); eq = extractEqualsMethod( Object.class );
hash = Object.class.getMethod("hashCode", NO_PARAM); hash = extractHashCodeMethod( Object.class );
} }
catch (Exception e) { catch ( Exception e ) {
throw new AssertionFailure("Could not find Object.equals() or Object.hashCode()", e); throw new AssertionFailure( "Could not find Object.equals() or Object.hashCode()", e );
} }
OBJECT_EQUALS = eq; OBJECT_EQUALS = eq;
OBJECT_HASHCODE = hash; OBJECT_HASHCODE = hash;
} }
/**
* Disallow instantiation of ReflectHelper.
*/
private ReflectHelper() {
}
/**
* Encapsulation of getting hold of a class's {@link Object#equals equals} method.
*
* @param clazz The class from which to extract the equals method.
* @return The equals method reference
* @throws NoSuchMethodException Should indicate an attempt to extract equals method from interface.
*/
public static Method extractEqualsMethod(Class clazz) throws NoSuchMethodException {
return clazz.getMethod( "equals", SINGLE_OBJECT_PARAM_SIGNATURE );
}
/**
* Encapsulation of getting hold of a class's {@link Object#hashCode hashCode} method.
*
* @param clazz The class from which to extract the hashCode method.
* @return The hashCode method reference
* @throws NoSuchMethodException Should indicate an attempt to extract hashCode method from interface.
*/
public static Method extractHashCodeMethod(Class clazz) throws NoSuchMethodException {
return clazz.getMethod( "hashCode", NO_PARAM_SIGNATURE );
}
/**
* Determine if the given class defines an {@link Object#equals} override.
*
* @param clazz The class to check
* @return True if clazz defines an equals override.
*/
public static boolean overridesEquals(Class clazz) { public static boolean overridesEquals(Class clazz) {
Method equals; Method equals;
try { try {
equals = clazz.getMethod("equals", OBJECT); equals = extractEqualsMethod( clazz );
} }
catch (NoSuchMethodException nsme) { catch ( NoSuchMethodException nsme ) {
return false; //its an interface so we can't really tell anything... return false; //its an interface so we can't really tell anything...
} }
return !OBJECT_EQUALS.equals(equals); return !OBJECT_EQUALS.equals( equals );
} }
/**
* Determine if the given class defines a {@link Object#hashCode} override.
*
* @param clazz The class to check
* @return True if clazz defines an hashCode override.
*/
public static boolean overridesHashCode(Class clazz) { public static boolean overridesHashCode(Class clazz) {
Method hashCode; Method hashCode;
try { try {
hashCode = clazz.getMethod("hashCode", NO_PARAM); hashCode = extractHashCodeMethod( clazz );
} }
catch (NoSuchMethodException nsme) { catch ( NoSuchMethodException nsme ) {
return false; //its an interface so we can't really tell anything... return false; //its an interface so we can't really tell anything...
} }
return !OBJECT_HASHCODE.equals(hashCode); return !OBJECT_HASHCODE.equals( hashCode );
} }
public static Class reflectedPropertyClass(String className, String name) throws MappingException { /**
try { * Determine if the given class implements the given interface.
Class clazz = ReflectHelper.classForName(className); *
return getter(clazz, name).getReturnType(); * @param clazz The class to check
} * @param intf The interface to check it against.
catch (ClassNotFoundException cnfe) { * @return True if the class does implement the interface, false otherwise.
throw new MappingException("class " + className + " not found while looking for property: " + name, cnfe); */
} public static boolean implementsInterface(Class clazz, Class intf) {
} assert intf.isInterface() : "Interface to check was not an interface";
private static Getter getter(Class clazz, String name) throws MappingException { Class[] interfaces = clazz.getInterfaces();
try { for ( int i = 0; i < interfaces.length; i++ ) {
return BASIC_PROPERTY_ACCESSOR.getGetter(clazz, name); if ( intf.isAssignableFrom( interfaces[i] ) ) {
} return true;
catch (PropertyNotFoundException pnfe) {
return DIRECT_PROPERTY_ACCESSOR.getGetter(clazz, name);
}
}
public static Getter getGetter(Class theClass, String name) throws MappingException {
return BASIC_PROPERTY_ACCESSOR.getGetter(theClass, name);
}
public static Class classForName(String name) throws ClassNotFoundException {
try {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
if ( contextClassLoader != null ) {
return contextClassLoader.loadClass(name);
} }
} }
catch ( Throwable t ) { return false;
}
return Class.forName( name );
} }
/**
* Perform resolution of a class name.
* <p/>
* Here we first check the context classloader, if one, before delegating to
* {@link Class#forName(String, boolean, ClassLoader)} using the caller's classloader
*
* @param name The class name
* @param caller The class from which this call originated (in order to access that class's loader).
* @return The class reference.
* @throws ClassNotFoundException From {@link Class#forName(String, boolean, ClassLoader)}.
*/
public static Class classForName(String name, Class caller) throws ClassNotFoundException { public static Class classForName(String name, Class caller) throws ClassNotFoundException {
try { try {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
@ -107,15 +149,101 @@ public final class ReflectHelper {
return contextClassLoader.loadClass( name ); return contextClassLoader.loadClass( name );
} }
} }
catch ( Throwable e ) { catch ( Throwable ignore ) {
} }
return Class.forName( name, true, caller.getClassLoader() ); return Class.forName( name, true, caller.getClassLoader() );
} }
/**
* Perform resolution of a class name.
* <p/>
* Same as {@link #classForName(String, Class)} except that here we delegate to
* {@link Class#forName(String)} if the context classloader lookup is unsuccessful.
*
* @param name The class name
* @return The class reference.
* @throws ClassNotFoundException From {@link Class#forName(String)}.
*/
public static Class classForName(String name) throws ClassNotFoundException {
try {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
if ( contextClassLoader != null ) {
return contextClassLoader.loadClass(name);
}
}
catch ( Throwable ignore ) {
}
return Class.forName( name );
}
/**
* Is this member publicly accessible.
* <p/>
* Short-hand for {@link #isPublic(Class, Member)} passing the member + {@link Member#getDeclaringClass()}
*
* @param member The member to check
* @return True if the member is publicly accessible.
*/
public static boolean isPublic(Member member) {
return isPublic( member.getDeclaringClass(), member );
}
/**
* Is this member publicly accessible.
*
* @param clazz The class which defines the member
* @param member The memeber.
* @return True if the member is publicly accessible, false otherwise.
*/
public static boolean isPublic(Class clazz, Member member) { public static boolean isPublic(Class clazz, Member member) {
return Modifier.isPublic( member.getModifiers() ) && Modifier.isPublic( clazz.getModifiers() ); return Modifier.isPublic( member.getModifiers() ) && Modifier.isPublic( clazz.getModifiers() );
} }
/**
* Attempt to resolve the specified property type through reflection.
*
* @param className The name of the class owning the property.
* @param name The name of the property.
* @return The type of the property.
* @throws MappingException Indicates we were unable to locate the property.
*/
public static Class reflectedPropertyClass(String className, String name) throws MappingException {
try {
Class clazz = ReflectHelper.classForName( className );
return getter( clazz, name ).getReturnType();
}
catch ( ClassNotFoundException cnfe ) {
throw new MappingException( "class " + className + " not found while looking for property: " + name, cnfe );
}
}
private static Getter getter(Class clazz, String name) throws MappingException {
try {
return BASIC_PROPERTY_ACCESSOR.getGetter( clazz, name );
}
catch ( PropertyNotFoundException pnfe ) {
return DIRECT_PROPERTY_ACCESSOR.getGetter( clazz, name );
}
}
/**
* Directly retrieve the {@link Getter} reference via the {@link BasicPropertyAccessor}.
*
* @param theClass The class owning the property
* @param name The name of the property
* @return The getter.
* @throws MappingException Indicates we were unable to locate the property.
*/
public static Getter getGetter(Class theClass, String name) throws MappingException {
return BASIC_PROPERTY_ACCESSOR.getGetter( theClass, name );
}
/**
* Resolve a constant to its actual value.
*
* @param name The name
* @return The value
*/
public static Object getConstantValue(String name) { public static Object getConstantValue(String name) {
Class clazz; Class clazz;
try { try {
@ -125,61 +253,90 @@ public final class ReflectHelper {
return null; return null;
} }
try { try {
return clazz.getField( StringHelper.unqualify( name ) ).get(null); return clazz.getField( StringHelper.unqualify( name ) ).get( null );
} }
catch ( Throwable t ) { catch ( Throwable t ) {
return null; return null;
} }
} }
/**
* Retrieve the default (no arg) constructor from the given class.
*
* @param clazz The class for which to retrieve the default ctor.
* @return The default constructor.
* @throws PropertyNotFoundException Indicates there was not publicly accessible, no-arg constructor (todo : why PropertyNotFoundException???)
*/
public static Constructor getDefaultConstructor(Class clazz) throws PropertyNotFoundException { public static Constructor getDefaultConstructor(Class clazz) throws PropertyNotFoundException {
if ( isAbstractClass( clazz ) ) {
if ( isAbstractClass(clazz) ) return null; return null;
}
try { try {
Constructor constructor = clazz.getDeclaredConstructor(NO_CLASSES); Constructor constructor = clazz.getDeclaredConstructor( NO_PARAM_SIGNATURE );
if ( !isPublic(clazz, constructor) ) { if ( !isPublic( clazz, constructor ) ) {
constructor.setAccessible(true); constructor.setAccessible( true );
} }
return constructor; return constructor;
} }
catch (NoSuchMethodException nme) { catch ( NoSuchMethodException nme ) {
throw new PropertyNotFoundException( throw new PropertyNotFoundException(
"Object class " + clazz.getName() + "Object class [" + clazz.getName() + "] must declare a default (no-argument) constructor"
" must declare a default (no-argument) constructor"
); );
} }
} }
/**
* Determine if the given class is declared abstract.
*
* @param clazz The class to check.
* @return True if the class is abstract, false otherwise.
*/
public static boolean isAbstractClass(Class clazz) { public static boolean isAbstractClass(Class clazz) {
int modifier = clazz.getModifiers(); int modifier = clazz.getModifiers();
return Modifier.isAbstract(modifier) || Modifier.isInterface(modifier); return Modifier.isAbstract(modifier) || Modifier.isInterface(modifier);
} }
/**
* Determine is the given class is declared final.
*
* @param clazz The class to check.
* @return True if the class is final, flase otherwise.
*/
public static boolean isFinalClass(Class clazz) { public static boolean isFinalClass(Class clazz) {
return Modifier.isFinal( clazz.getModifiers() ); return Modifier.isFinal( clazz.getModifiers() );
} }
/**
* Retrieve a constructor for the given class, with arguments matching the specified Hibernate mapping
* {@link Type types}.
*
* @param clazz The class needing instantiation
* @param types The types representing the required ctor param signature
* @return The matching constructor.
* @throws PropertyNotFoundException Indicates we could not locate an appropriate constructor (todo : again with PropertyNotFoundException???)
*/
public static Constructor getConstructor(Class clazz, Type[] types) throws PropertyNotFoundException { public static Constructor getConstructor(Class clazz, Type[] types) throws PropertyNotFoundException {
final Constructor[] candidates = clazz.getConstructors(); final Constructor[] candidates = clazz.getConstructors();
for ( int i=0; i<candidates.length; i++ ) { for ( int i = 0; i < candidates.length; i++ ) {
final Constructor constructor = candidates[i]; final Constructor constructor = candidates[i];
final Class[] params = constructor.getParameterTypes(); final Class[] params = constructor.getParameterTypes();
if ( params.length==types.length ) { if ( params.length == types.length ) {
boolean found = true; boolean found = true;
for ( int j=0; j<params.length; j++ ) { for ( int j = 0; j < params.length; j++ ) {
final boolean ok = params[j].isAssignableFrom( types[j].getReturnedClass() ) || ( final boolean ok = params[j].isAssignableFrom( types[j].getReturnedClass() ) || (
types[j] instanceof PrimitiveType && types[j] instanceof PrimitiveType &&
params[j] == ( (PrimitiveType) types[j] ).getPrimitiveClass() params[j] == ( ( PrimitiveType ) types[j] ).getPrimitiveClass()
); );
if (!ok) { if ( !ok ) {
found = false; found = false;
break; break;
} }
} }
if (found) { if ( found ) {
if ( !isPublic(clazz, constructor) ) constructor.setAccessible(true); if ( !isPublic( clazz, constructor ) ) {
constructor.setAccessible( true );
}
return constructor; return constructor;
} }
} }
@ -196,6 +353,4 @@ public final class ReflectHelper {
} }
} }
private ReflectHelper() {}
} }

View File

@ -31,7 +31,6 @@
<hibernate-mapping package="org.hibernate.test.dynamicentity"> <hibernate-mapping package="org.hibernate.test.dynamicentity">
<class name="Person" table="t_person" discriminator-value="person" abstract="false"> <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"> <id name="id">
<generator class="native"/> <generator class="native"/>
</id> </id>
@ -46,13 +45,11 @@
</set> </set>
<subclass name="Customer" discriminator-value="customer" abstract="false"> <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"/> <many-to-one name="company" cascade="none" column="comp_id"/>
</subclass> </subclass>
</class> </class>
<class name="Company" table="t_company" abstract="false"> <class name="Company" table="t_company" abstract="false">
<tuplizer class="org.hibernate.test.dynamicentity.tuplizer2.MyEntityTuplizer" entity-mode="pojo"/>
<id name="id"> <id name="id">
<generator class="native"/> <generator class="native"/>
</id> </id>
@ -60,7 +57,6 @@
</class> </class>
<class name="Address" table="t_address" abstract="false"> <class name="Address" table="t_address" abstract="false">
<tuplizer class="org.hibernate.test.dynamicentity.tuplizer2.MyEntityTuplizer" entity-mode="pojo"/>
<id name="id"> <id name="id">
<generator class="native"/> <generator class="native"/>
</id> </id>

View File

@ -31,6 +31,7 @@ import org.hibernate.test.dynamicentity.Address;
import org.hibernate.test.dynamicentity.Person; import org.hibernate.test.dynamicentity.Person;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.Hibernate; import org.hibernate.Hibernate;
import org.hibernate.EntityMode;
import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Configuration;
import org.hibernate.junit.functional.FunctionalTestCase; import org.hibernate.junit.functional.FunctionalTestCase;
import org.hibernate.junit.functional.FunctionalTestClassTestSuite; import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
@ -61,6 +62,7 @@ public class ImprovedTuplizerDynamicEntityTest extends FunctionalTestCase {
public void configure(Configuration cfg) { public void configure(Configuration cfg) {
super.configure( cfg ); super.configure( cfg );
cfg.getEntityTuplizerFactory().registerDefaultTuplizerClass( EntityMode.POJO, MyEntityTuplizer.class );
} }
public static TestSuite suite() { public static TestSuite suite() {