HHH-16214 Use a more efficient Map implementation in SqmFunctionRegistry

This commit is contained in:
Sanne Grinovero 2023-02-21 14:25:23 +00:00 committed by Sanne Grinovero
parent dd5a8c97af
commit ac9f47ae43
3 changed files with 98 additions and 16 deletions

View File

@ -0,0 +1,78 @@
/*
* 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;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import org.hibernate.Internal;
/**
* Wraps a ConcurrentHashMap having all keys as Strings
* and ensures all keys are lowercased.
* It does assume keys and arguments are never null, preferring to throw a NPE
* over adding unnecessary checks.
* The public exposed methods are similar to the ones on Map, but
* not all Map methods are exposed - only a selection we actually need; this
* implies it doesn't implement Map; nothing stops us to make it implement Map
* but at time of writing it seems unnecessary for our purposes.
* @param <V> the type for the stored values.
*/
@Internal
public final class CaseInsensitiveDictionary<V> {
private final Map<String, V> map = new ConcurrentHashMap<>();
public V get(final String key) {
return map.get( trueKey( key ) );
}
/**
* Contrary to traditional Map, we make the return unmodifiable.
* @return the map's keySet
*/
public Set<String> unmodifiableKeySet() {
return Collections.unmodifiableSet( map.keySet() );
}
/**
* Contrary to traditional Map, we make the return unmodifiable.
* @return the map's entrySet
*/
public Set<Map.Entry<String, V>> unmodifiableEntrySet() {
return Collections.unmodifiableSet( map.entrySet() );
}
public V put(final String key, V value) {
return map.put( trueKey( key ), value );
}
public V remove(final String key) {
return map.remove( trueKey( key ) );
}
public boolean containsKey(final String key) {
return map.containsKey( trueKey( key ) );
}
private static String trueKey(final String key) {
return key.toLowerCase( Locale.ROOT );
}
public void clear() {
map.clear();
}
public void forEach(final BiConsumer<? super String, ? super V> action) {
map.forEach( action );
}
}

View File

@ -6,11 +6,12 @@
*/
package org.hibernate.query.sqm.function;
import java.util.AbstractMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Stream;
import org.hibernate.internal.util.collections.CaseInsensitiveDictionary;
import org.hibernate.query.sqm.produce.function.FunctionParameterType;
import org.hibernate.query.sqm.produce.function.NamedFunctionDescriptorBuilder;
import org.hibernate.query.sqm.produce.function.PatternFunctionDescriptorBuilder;
@ -35,27 +36,31 @@ import static org.hibernate.query.sqm.produce.function.StandardFunctionReturnTyp
public class SqmFunctionRegistry {
private static final Logger log = Logger.getLogger( SqmFunctionRegistry.class );
private final Map<String, SqmFunctionDescriptor> functionMap = new TreeMap<>( CASE_INSENSITIVE_ORDER );
private final Map<String,String> alternateKeyMap = new TreeMap<>( CASE_INSENSITIVE_ORDER );
private final CaseInsensitiveDictionary<SqmFunctionDescriptor> functionMap = new CaseInsensitiveDictionary<>();
private final CaseInsensitiveDictionary<String> alternateKeyMap = new CaseInsensitiveDictionary<>();
public SqmFunctionRegistry() {
log.trace( "SqmFunctionRegistry created" );
}
public Map<String, SqmFunctionDescriptor> getFunctions() {
return functionMap;
public Set<String> getValidFunctionKeys() {
return functionMap.unmodifiableKeySet();
}
/**
* Useful for diagnostics - not efficient: do not use in production code.
*
* @return
*/
public Stream<Map.Entry<String, SqmFunctionDescriptor>> getFunctionsByName() {
return Stream.concat(
functionMap.entrySet().stream(),
alternateKeyMap.entrySet().stream().map(
entry -> new AbstractMap.SimpleEntry<>(
entry.getKey(),
functionMap.get( entry.getValue() )
)
)
);
final Map<String, SqmFunctionDescriptor> sortedFunctionMap = new TreeMap<>( CASE_INSENSITIVE_ORDER );
for ( Map.Entry<String, SqmFunctionDescriptor> e : functionMap.unmodifiableEntrySet() ) {
sortedFunctionMap.put( e.getKey(), e.getValue() );
}
for ( Map.Entry<String, String> e : alternateKeyMap.unmodifiableEntrySet() ) {
sortedFunctionMap.put( e.getKey(), functionMap.get( e.getValue() ) );
}
return sortedFunctionMap.entrySet().stream();
}
/**

View File

@ -35,8 +35,7 @@ public abstract class SpatialSessionFactoryAware extends SpatialTestDataProvider
this.supportedFunctions = scope.getSessionFactory()
.getQueryEngine()
.getSqmFunctionRegistry()
.getFunctions()
.keySet();
.getValidFunctionKeys();
if ( DialectContext.getDialect() instanceof H2Dialect ) {
initH2GISExtensionsForInMemDb();
}