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; package org.hibernate.query.sqm.function;
import java.util.AbstractMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.stream.Stream; 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.FunctionParameterType;
import org.hibernate.query.sqm.produce.function.NamedFunctionDescriptorBuilder; import org.hibernate.query.sqm.produce.function.NamedFunctionDescriptorBuilder;
import org.hibernate.query.sqm.produce.function.PatternFunctionDescriptorBuilder; import org.hibernate.query.sqm.produce.function.PatternFunctionDescriptorBuilder;
@ -35,27 +36,31 @@ import static org.hibernate.query.sqm.produce.function.StandardFunctionReturnTyp
public class SqmFunctionRegistry { public class SqmFunctionRegistry {
private static final Logger log = Logger.getLogger( SqmFunctionRegistry.class ); private static final Logger log = Logger.getLogger( SqmFunctionRegistry.class );
private final Map<String, SqmFunctionDescriptor> functionMap = new TreeMap<>( CASE_INSENSITIVE_ORDER ); private final CaseInsensitiveDictionary<SqmFunctionDescriptor> functionMap = new CaseInsensitiveDictionary<>();
private final Map<String,String> alternateKeyMap = new TreeMap<>( CASE_INSENSITIVE_ORDER ); private final CaseInsensitiveDictionary<String> alternateKeyMap = new CaseInsensitiveDictionary<>();
public SqmFunctionRegistry() { public SqmFunctionRegistry() {
log.trace( "SqmFunctionRegistry created" ); log.trace( "SqmFunctionRegistry created" );
} }
public Map<String, SqmFunctionDescriptor> getFunctions() { public Set<String> getValidFunctionKeys() {
return functionMap; return functionMap.unmodifiableKeySet();
} }
/**
* Useful for diagnostics - not efficient: do not use in production code.
*
* @return
*/
public Stream<Map.Entry<String, SqmFunctionDescriptor>> getFunctionsByName() { public Stream<Map.Entry<String, SqmFunctionDescriptor>> getFunctionsByName() {
return Stream.concat( final Map<String, SqmFunctionDescriptor> sortedFunctionMap = new TreeMap<>( CASE_INSENSITIVE_ORDER );
functionMap.entrySet().stream(), for ( Map.Entry<String, SqmFunctionDescriptor> e : functionMap.unmodifiableEntrySet() ) {
alternateKeyMap.entrySet().stream().map( sortedFunctionMap.put( e.getKey(), e.getValue() );
entry -> new AbstractMap.SimpleEntry<>( }
entry.getKey(), for ( Map.Entry<String, String> e : alternateKeyMap.unmodifiableEntrySet() ) {
functionMap.get( entry.getValue() ) 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() this.supportedFunctions = scope.getSessionFactory()
.getQueryEngine() .getQueryEngine()
.getSqmFunctionRegistry() .getSqmFunctionRegistry()
.getFunctions() .getValidFunctionKeys();
.keySet();
if ( DialectContext.getDialect() instanceof H2Dialect ) { if ( DialectContext.getDialect() instanceof H2Dialect ) {
initH2GISExtensionsForInMemDb(); initH2GISExtensionsForInMemDb();
} }