Skip to content

Commit

Permalink
HHH-16911 Save some memory in CallbackRegistryImpl
Browse files Browse the repository at this point in the history
  • Loading branch information
Sanne committed Aug 1, 2023
1 parent 724e376 commit 9d118a5
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -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
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* @author Sanne Grinovero
* @since 6.2
*/
public final class MapBackedClassValue<V> {
public final class MapBackedClassValue<V> implements ReadOnlyMap<Class,V> {

private volatile Map<Class<?>, V> map;

Expand All @@ -45,14 +45,16 @@ public MapBackedClassValue(final Map<Class<?>, V> map) {
this.map = Map.copyOf( map );
}

public V get(Class<?> key) {
@Override
public V get(Class key) {
return classValue.get( key );
}

/**
* Use this to wipe the backing map, important
* to avoid classloader leaks.
*/
@Override
public void dispose() {
Map<Class<?>, V> existing = this.map;
this.map = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -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();

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import org.hibernate.internal.util.collections.ArrayHelper;
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.CallbackRegistry;
import org.hibernate.jpa.event.spi.CallbackType;
Expand All @@ -26,13 +27,13 @@
*/
final class CallbackRegistryImpl implements CallbackRegistry {

private final MapBackedClassValue<Callback[]> preCreates;
private final MapBackedClassValue<Callback[]> postCreates;
private final MapBackedClassValue<Callback[]> preRemoves;
private final MapBackedClassValue<Callback[]> postRemoves;
private final MapBackedClassValue<Callback[]> preUpdates;
private final MapBackedClassValue<Callback[]> postUpdates;
private final MapBackedClassValue<Callback[]> postLoads;
private final ReadOnlyMap<Class,Callback[]> preCreates;
private final ReadOnlyMap<Class,Callback[]> postCreates;
private final ReadOnlyMap<Class,Callback[]> preRemoves;
private final ReadOnlyMap<Class,Callback[]> postRemoves;
private final ReadOnlyMap<Class,Callback[]> preUpdates;
private final ReadOnlyMap<Class,Callback[]> postUpdates;
private final ReadOnlyMap<Class,Callback[]> postLoads;

public CallbackRegistryImpl(
Map<Class<?>, Callback[]> preCreates,
Expand All @@ -42,18 +43,27 @@ public CallbackRegistryImpl(
Map<Class<?>, Callback[]> preUpdates,
Map<Class<?>, Callback[]> postUpdates,
Map<Class<?>, Callback[]> postLoads) {
this.preCreates = new MapBackedClassValue<>( preCreates );
this.postCreates = new MapBackedClassValue<>( postCreates );
this.preRemoves = new MapBackedClassValue<>( preRemoves );
this.postRemoves = new MapBackedClassValue<>( postRemoves );
this.preUpdates = new MapBackedClassValue<>( preUpdates );
this.postUpdates = new MapBackedClassValue<>( postUpdates );
this.postLoads = new MapBackedClassValue<>( postLoads );
this.preCreates = createBackingMap( preCreates );
this.postCreates = createBackingMap( postCreates );
this.preRemoves = createBackingMap( preRemoves );
this.postRemoves = createBackingMap( postRemoves );
this.preUpdates = createBackingMap( preUpdates );
this.postUpdates = createBackingMap( postUpdates );
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
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 ) );
}

Expand Down Expand Up @@ -119,7 +129,7 @@ private boolean callback(Callback[] callbacks, Object bean) {
}
}

private MapBackedClassValue<Callback[]> determineAppropriateCallbackMap(CallbackType callbackType) {
private ReadOnlyMap<Class,Callback[]> determineAppropriateCallbackMap(CallbackType callbackType) {
if ( callbackType == CallbackType.PRE_PERSIST ) {
return preCreates;
}
Expand Down

0 comments on commit 9d118a5

Please sign in to comment.