HHH-16545 PersistenceUtil.MetadataCache needs to be threadsafe

This commit is contained in:
Sanne Grinovero 2023-05-04 21:41:21 +03:00 committed by Sanne Grinovero
parent c4a3fbe550
commit 11982572bd
1 changed files with 33 additions and 14 deletions

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.jpa.internal.util; package org.hibernate.jpa.internal.util;
import java.io.ObjectStreamException;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@ -16,7 +17,7 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.WeakHashMap;
import jakarta.persistence.spi.LoadState; import jakarta.persistence.spi.LoadState;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
@ -411,24 +412,42 @@ public final class PersistenceUtilHelper {
} }
/** /**
* Cache hierarchy and member resolution in a weak hash map * Cache hierarchy and member resolution, taking care to not leak
* references to Class instances.
*/ */
//TODO not really thread-safe public static final class MetadataCache implements Serializable {
public static class MetadataCache implements Serializable {
private transient Map<Class<?>, ClassMetadataCache> classCache = new WeakHashMap<>();
private final ClassValue<ClassMetadataCache> metadataCacheClassValue;
private void readObject(java.io.ObjectInputStream stream) { public MetadataCache() {
classCache = new WeakHashMap<>(); this( new MetadataClassValue() );
} }
ClassMetadataCache getClassMetadata(Class<?> clazz) { //To help with serialization: no need to serialize the actual metadataCacheClassValue field
ClassMetadataCache classMetadataCache = classCache.get( clazz ); private MetadataCache(ClassValue<ClassMetadataCache> metadataCacheClassValue) {
if ( classMetadataCache == null ) { this.metadataCacheClassValue = metadataCacheClassValue;
classMetadataCache = new ClassMetadataCache( clazz ); }
classCache.put( clazz, classMetadataCache );
} Object writeReplace() throws ObjectStreamException {
return classMetadataCache; //Writing a different instance which doesn't include the cache
return new MetadataCache(null);
}
private Object readResolve() throws ObjectStreamException {
//Ensure we do instantiate a new cache instance on deserialization
return new MetadataCache();
}
ClassMetadataCache getClassMetadata(final Class<?> clazz) {
return metadataCacheClassValue.get( clazz );
}
}
private static final class MetadataClassValue extends ClassValue<ClassMetadataCache> {
@Override
protected ClassMetadataCache computeValue(final Class type) {
return new ClassMetadataCache( type );
} }
} }