diff --git a/entitymanager/src/main/java/org/hibernate/ejb/EntityManagerFactoryImpl.java b/entitymanager/src/main/java/org/hibernate/ejb/EntityManagerFactoryImpl.java index fe468931d7..3d096c8dab 100755 --- a/entitymanager/src/main/java/org/hibernate/ejb/EntityManagerFactoryImpl.java +++ b/entitymanager/src/main/java/org/hibernate/ejb/EntityManagerFactoryImpl.java @@ -65,6 +65,7 @@ public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory { private final Metamodel metamodel; private final HibernatePersistenceUnitUtil util; private final Map properties; + private final PersistenceUtilHelper.MetadataCache cache = new PersistenceUtilHelper.MetadataCache(); @SuppressWarnings( "unchecked" ) public EntityManagerFactoryImpl( @@ -182,13 +183,15 @@ public void evictAll() { private static class HibernatePersistenceUnitUtil implements PersistenceUnitUtil, Serializable { private final HibernateEntityManagerFactory emf; + private transient PersistenceUtilHelper.MetadataCache cache; private HibernatePersistenceUnitUtil(EntityManagerFactoryImpl emf) { this.emf = emf; + this.cache = emf.cache; } public boolean isLoaded(Object entity, String attributeName) { - LoadState state = PersistenceUtilHelper.isLoadedWithoutReference( entity, attributeName ); + LoadState state = PersistenceUtilHelper.isLoadedWithoutReference( entity, attributeName, cache ); if (state == LoadState.LOADED) { return true; } @@ -196,7 +199,7 @@ else if (state == LoadState.NOT_LOADED ) { return false; } else { - return PersistenceUtilHelper.isLoadedWithReference( entity, attributeName ) != LoadState.NOT_LOADED; + return PersistenceUtilHelper.isLoadedWithReference( entity, attributeName, cache ) != LoadState.NOT_LOADED; } } diff --git a/entitymanager/src/main/java/org/hibernate/ejb/HibernatePersistence.java b/entitymanager/src/main/java/org/hibernate/ejb/HibernatePersistence.java index b2a57c8b9f..63817d1085 100755 --- a/entitymanager/src/main/java/org/hibernate/ejb/HibernatePersistence.java +++ b/entitymanager/src/main/java/org/hibernate/ejb/HibernatePersistence.java @@ -38,6 +38,7 @@ * @author Gavin King */ public class HibernatePersistence extends AvailableSettings implements PersistenceProvider { + private final PersistenceUtilHelper.MetadataCache cache = new PersistenceUtilHelper.MetadataCache(); /** * Get an entity manager factory by its entity manager name, using the specified @@ -85,11 +86,11 @@ public EntityManagerFactory createEntityManagerFactory(Map properties) { private final ProviderUtil providerUtil = new ProviderUtil() { public LoadState isLoadedWithoutReference(Object proxy, String property) { - return PersistenceUtilHelper.isLoadedWithoutReference( proxy, property ); + return PersistenceUtilHelper.isLoadedWithoutReference( proxy, property, cache ); } public LoadState isLoadedWithReference(Object proxy, String property) { - return PersistenceUtilHelper.isLoadedWithReference( proxy, property ); + return PersistenceUtilHelper.isLoadedWithReference( proxy, property, cache ); } public LoadState isLoaded(Object o) { diff --git a/entitymanager/src/main/java/org/hibernate/ejb/util/PersistenceUtilHelper.java b/entitymanager/src/main/java/org/hibernate/ejb/util/PersistenceUtilHelper.java index 88aebf5c17..51c47fc093 100644 --- a/entitymanager/src/main/java/org/hibernate/ejb/util/PersistenceUtilHelper.java +++ b/entitymanager/src/main/java/org/hibernate/ejb/util/PersistenceUtilHelper.java @@ -1,14 +1,21 @@ package org.hibernate.ejb.util; +import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Modifier; import java.lang.reflect.AccessibleObject; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; import javax.persistence.spi.LoadState; import javax.persistence.PersistenceException; +import org.hibernate.AssertionFailure; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; import org.hibernate.intercept.FieldInterceptionHelper; @@ -19,7 +26,7 @@ * @author Emmanuel Bernard */ public class PersistenceUtilHelper { - public static LoadState isLoadedWithoutReference(Object proxy, String property) { + public static LoadState isLoadedWithoutReference(Object proxy, String property, MetadataCache cache) { Object entity; boolean sureFromUs = false; if ( proxy instanceof HibernateProxy ) { @@ -44,7 +51,7 @@ public static LoadState isLoadedWithoutReference(Object proxy, String property) if (isInitialized && interceptor != null) { //property is loaded according to bytecode enhancement, but is it loaded as far as association? //it's ours, we can read - state = isLoaded( get( entity, property ) ); + state = isLoaded( get( entity, property, cache ) ); //it's ours so we know it's loaded if (state == LoadState.UNKNOWN) state = LoadState.LOADED; } @@ -54,7 +61,7 @@ else if ( interceptor != null && (! isInitialized)) { else if ( sureFromUs ) { //interceptor == null //property is loaded according to bytecode enhancement, but is it loaded as far as association? //it's ours, we can read - state = isLoaded( get( entity, property ) ); + state = isLoaded( get( entity, property, cache ) ); //it's ours so we know it's loaded if (state == LoadState.UNKNOWN) state = LoadState.LOADED; } @@ -71,31 +78,25 @@ else if ( sureFromUs ) { //interceptor == null } } - public static LoadState isLoadedWithReference(Object proxy, String property) { + public static LoadState isLoadedWithReference(Object proxy, String property, MetadataCache cache) { //for sure we don't instrument and for sure it's not a lazy proxy - Object object = get(proxy, property); + Object object = get(proxy, property, cache); return isLoaded( object ); } - private static Object get(Object proxy, String property) { + private static Object get(Object proxy, String property, MetadataCache cache) { final Class clazz = proxy.getClass(); + try { - try { - final Field field = clazz.getField( property ); - setAccessibility( field ); - return field.get( proxy ); + Member member = cache.getMember( clazz, property ); + if (member instanceof Field) { + return ( (Field) member ).get( proxy ); } - catch ( NoSuchFieldException e ) { - final Method method = getMethod( clazz, property ); - if (method != null) { - setAccessibility( method ); - return method.invoke( proxy ); - } - else { - throw new PersistenceException( "Unable to find field or method: " - + clazz + "#" - + property); - } + else if (member instanceof Method) { + return ( (Method) member ).invoke( proxy ); + } + else { + throw new AssertionFailure( "Member object neither Field nor Method: " + member); } } catch ( IllegalAccessException e ) { @@ -110,6 +111,27 @@ private static Object get(Object proxy, String property) { } } + private static void setAccessibility(Member member) { + if ( !Modifier.isPublic( member.getModifiers() ) ) { + //Sun's ease of use, sigh... + ( ( AccessibleObject ) member ).setAccessible( true ); + } + } + + public static LoadState isLoaded(Object o) { + if ( o instanceof HibernateProxy ) { + final boolean isInitialized = !( ( HibernateProxy ) o ).getHibernateLazyInitializer().isUninitialized(); + return isInitialized ? LoadState.LOADED : LoadState.NOT_LOADED; + } + else if ( o instanceof PersistentCollection ) { + final boolean isInitialized = ( ( PersistentCollection ) o ).wasInitialized(); + return isInitialized ? LoadState.LOADED : LoadState.NOT_LOADED; + } + else { + return LoadState.UNKNOWN; + } + } + /** * Returns the method with the specified name or null if it does not exist. * @@ -135,24 +157,83 @@ private static Method getMethod(Class clazz, String methodName) { } } - private static void setAccessibility(Member member) { - if ( !Modifier.isPublic( member.getModifiers() ) ) { - //Sun's ease of use, sigh... - ( ( AccessibleObject ) member ).setAccessible( true ); - } - } + /** + * Cache hierarchy and member resolution in a weak hash map + */ + //TODO not really thread-safe + public static class MetadataCache implements Serializable { + private transient Map, ClassCache> classCache = new WeakHashMap, ClassCache>(); - public static LoadState isLoaded(Object o) { - if ( o instanceof HibernateProxy ) { - final boolean isInitialized = !( ( HibernateProxy ) o ).getHibernateLazyInitializer().isUninitialized(); - return isInitialized ? LoadState.LOADED : LoadState.NOT_LOADED; + + private void readObject(java.io.ObjectInputStream stream) { + classCache = new WeakHashMap, ClassCache>(); } - else if ( o instanceof PersistentCollection ) { - final boolean isInitialized = ( ( PersistentCollection ) o ).wasInitialized(); - return isInitialized ? LoadState.LOADED : LoadState.NOT_LOADED; + + Member getMember(Class clazz, String property) { + ClassCache cache = classCache.get( clazz ); + if (cache == null) { + cache = new ClassCache(clazz); + classCache.put( clazz, cache ); + } + Member member = cache.members.get( property ); + if ( member == null ) { + member = findMember( clazz, property ); + cache.members.put( property, member ); + } + return member; } - else { - return LoadState.UNKNOWN; + + private Member findMember(Class clazz, String property) { + final List> classes = getClassHierarchy( clazz ); + + for (Class current : classes) { + final Field field; + try { + field = current.getDeclaredField( property ); + setAccessibility( field ); + return field; + } + catch ( NoSuchFieldException e ) { + final Method method = getMethod( clazz, property ); + if (method != null) { + setAccessibility( method ); + return method; + } + } + } + //we could not find any match + throw new PersistenceException( "Unable to find field or method: " + + clazz + "#" + + property); + } + + private List> getClassHierarchy(Class clazz) { + ClassCache cache = classCache.get( clazz ); + if (cache == null) { + cache = new ClassCache(clazz); + classCache.put( clazz, cache ); + } + return cache.classHierarchy; + } + + private static List> findClassHierarchy(Class clazz) { + List> classes = new ArrayList>(); + Class current = clazz; + do { + classes.add( current ); + current = current.getSuperclass(); + } + while ( current != null ); + return classes; + } + + private static class ClassCache { + List> classHierarchy; + Map members = new HashMap(); + + public ClassCache(Class clazz) { + classHierarchy = findClassHierarchy( clazz ); + } } }