HHH-16911 Save some memory in CallbackRegistryImpl

This commit is contained in:
Sanne Grinovero 2023-07-18 19:18:22 +01:00 committed by Sanne Grinovero
parent 724e376b7c
commit 9d118a5482
4 changed files with 79 additions and 18 deletions

View File

@ -0,0 +1,21 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.internal.util.collections;
final class EmptyReadOnlyMap<K,V> implements ReadOnlyMap<K,V> {
@Override
public V get(K key) {
return null;
}
@Override
public void dispose() {
//no-op
}
}

View File

@ -20,7 +20,7 @@ import java.util.Map;
* @author Sanne Grinovero * @author Sanne Grinovero
* @since 6.2 * @since 6.2
*/ */
public final class MapBackedClassValue<V> { public final class MapBackedClassValue<V> implements ReadOnlyMap<Class,V> {
private volatile Map<Class<?>, V> map; private volatile Map<Class<?>, V> map;
@ -45,7 +45,8 @@ public final class MapBackedClassValue<V> {
this.map = Map.copyOf( map ); this.map = Map.copyOf( map );
} }
public V get(Class<?> key) { @Override
public V get(Class key) {
return classValue.get( key ); return classValue.get( key );
} }
@ -53,6 +54,7 @@ public final class MapBackedClassValue<V> {
* Use this to wipe the backing map, important * Use this to wipe the backing map, important
* to avoid classloader leaks. * to avoid classloader leaks.
*/ */
@Override
public void dispose() { public void dispose() {
Map<Class<?>, V> existing = this.map; Map<Class<?>, V> existing = this.map;
this.map = null; this.map = null;

View File

@ -0,0 +1,28 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.internal.util.collections;
public interface ReadOnlyMap<K, V> {
//To help saving memory
public static final ReadOnlyMap EMPTY = new EmptyReadOnlyMap();
/**
* The main operation.
* @param key
* @return the corresponding object, or null if there is no association with any entry.
*/
V get(K key);
/**
* Some implementations might hold on to references,
* which could be just heavy or potentially harmful,
* such as ClassLoader leaks: allow for proper cleanup.
*/
void dispose();
}

View File

@ -13,6 +13,7 @@ import jakarta.persistence.PersistenceException;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.collections.MapBackedClassValue; import org.hibernate.internal.util.collections.MapBackedClassValue;
import org.hibernate.internal.util.collections.ReadOnlyMap;
import org.hibernate.jpa.event.spi.Callback; import org.hibernate.jpa.event.spi.Callback;
import org.hibernate.jpa.event.spi.CallbackRegistry; import org.hibernate.jpa.event.spi.CallbackRegistry;
import org.hibernate.jpa.event.spi.CallbackType; import org.hibernate.jpa.event.spi.CallbackType;
@ -26,13 +27,13 @@ import org.hibernate.jpa.event.spi.CallbackType;
*/ */
final class CallbackRegistryImpl implements CallbackRegistry { final class CallbackRegistryImpl implements CallbackRegistry {
private final MapBackedClassValue<Callback[]> preCreates; private final ReadOnlyMap<Class,Callback[]> preCreates;
private final MapBackedClassValue<Callback[]> postCreates; private final ReadOnlyMap<Class,Callback[]> postCreates;
private final MapBackedClassValue<Callback[]> preRemoves; private final ReadOnlyMap<Class,Callback[]> preRemoves;
private final MapBackedClassValue<Callback[]> postRemoves; private final ReadOnlyMap<Class,Callback[]> postRemoves;
private final MapBackedClassValue<Callback[]> preUpdates; private final ReadOnlyMap<Class,Callback[]> preUpdates;
private final MapBackedClassValue<Callback[]> postUpdates; private final ReadOnlyMap<Class,Callback[]> postUpdates;
private final MapBackedClassValue<Callback[]> postLoads; private final ReadOnlyMap<Class,Callback[]> postLoads;
public CallbackRegistryImpl( public CallbackRegistryImpl(
Map<Class<?>, Callback[]> preCreates, Map<Class<?>, Callback[]> preCreates,
@ -42,18 +43,27 @@ final class CallbackRegistryImpl implements CallbackRegistry {
Map<Class<?>, Callback[]> preUpdates, Map<Class<?>, Callback[]> preUpdates,
Map<Class<?>, Callback[]> postUpdates, Map<Class<?>, Callback[]> postUpdates,
Map<Class<?>, Callback[]> postLoads) { Map<Class<?>, Callback[]> postLoads) {
this.preCreates = new MapBackedClassValue<>( preCreates ); this.preCreates = createBackingMap( preCreates );
this.postCreates = new MapBackedClassValue<>( postCreates ); this.postCreates = createBackingMap( postCreates );
this.preRemoves = new MapBackedClassValue<>( preRemoves ); this.preRemoves = createBackingMap( preRemoves );
this.postRemoves = new MapBackedClassValue<>( postRemoves ); this.postRemoves = createBackingMap( postRemoves );
this.preUpdates = new MapBackedClassValue<>( preUpdates ); this.preUpdates = createBackingMap( preUpdates );
this.postUpdates = new MapBackedClassValue<>( postUpdates ); this.postUpdates = createBackingMap( postUpdates );
this.postLoads = new MapBackedClassValue<>( postLoads ); this.postLoads = createBackingMap( postLoads );
}
private static ReadOnlyMap<Class, Callback[]> createBackingMap(final Map<Class<?>, Callback[]> src) {
if ( src == null || src.isEmpty() ) {
return ReadOnlyMap.EMPTY;
}
else {
return new MapBackedClassValue<>( src );
}
} }
@Override @Override
public boolean hasRegisteredCallbacks(Class<?> entityClass, CallbackType callbackType) { public boolean hasRegisteredCallbacks(Class<?> entityClass, CallbackType callbackType) {
final MapBackedClassValue<Callback[]> map = determineAppropriateCallbackMap( callbackType ); final ReadOnlyMap<Class,Callback[]> map = determineAppropriateCallbackMap( callbackType );
return notEmpty( map.get( entityClass ) ); return notEmpty( map.get( entityClass ) );
} }
@ -119,7 +129,7 @@ final class CallbackRegistryImpl implements CallbackRegistry {
} }
} }
private MapBackedClassValue<Callback[]> determineAppropriateCallbackMap(CallbackType callbackType) { private ReadOnlyMap<Class,Callback[]> determineAppropriateCallbackMap(CallbackType callbackType) {
if ( callbackType == CallbackType.PRE_PERSIST ) { if ( callbackType == CallbackType.PRE_PERSIST ) {
return preCreates; return preCreates;
} }