HHH-17282 Introduce a specialized Map for NavigablePath to Initializer

This commit is contained in:
Sanne Grinovero 2023-09-15 19:03:17 +02:00 committed by Sanne Grinovero
parent d59ecb633b
commit 75f173a4a0
3 changed files with 96 additions and 25 deletions

View File

@ -9,7 +9,6 @@ package org.hibernate.sql.results.internal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.exec.spi.ExecutionContext;
@ -36,14 +35,14 @@ public final class InitializersList {
private final Initializer[] sortedNonCollectionsFirst;
private final Initializer[] sortedForResolveInstance;
private final boolean hasCollectionInitializers;
private final Map<NavigablePath, Initializer> initializerMap;
private final NavigablePathMapToInitializer initializerMap;
private InitializersList(
Initializer[] initializers,
Initializer[] sortedNonCollectionsFirst,
Initializer[] sortedForResolveInstance,
boolean hasCollectionInitializers,
Map<NavigablePath, Initializer> initializerMap) {
NavigablePathMapToInitializer initializerMap) {
this.initializers = initializers;
this.sortedNonCollectionsFirst = sortedNonCollectionsFirst;
this.sortedForResolveInstance = sortedForResolveInstance;
@ -123,7 +122,7 @@ public final class InitializersList {
&& !(initializer instanceof AbstractBatchEntitySelectFetchInitializer );
}
InitializersList build(final Map<NavigablePath, Initializer> initializerMap) {
InitializersList build(final NavigablePathMapToInitializer initializerMap) {
final int size = initializers.size();
final Initializer[] sortedNonCollectionsFirst = new Initializer[size];
final Initializer[] sortedForResolveInstance = new Initializer[size];

View File

@ -0,0 +1,90 @@
/*
* 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.sql.results.internal;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.results.ResultsLogger;
import org.hibernate.sql.results.graph.Initializer;
/**
* This is in all practical terms a {@code Map<NavigablePath, Initializer>}
* but wrapping an HashMap so to keep the client code readable as we need
* to:
* a) have a way to log all initializers
* b) prevent type pollution from happening on Initializer retrieval
* I also consider it good practice to only expose the minimal set of
* operations the client actually needs.
*/
public final class NavigablePathMapToInitializer {
private HashMap<NavigablePath, InitializerHolder> map = null;
public Initializer get(final NavigablePath navigablePath) {
if ( map != null && navigablePath != null ) {
final InitializerHolder h = map.get( navigablePath );
if ( h != null ) {
return h.initializer;
}
}
return null;
}
public void put(final NavigablePath navigablePath, final Initializer initializer) {
Objects.requireNonNull( navigablePath );
Objects.requireNonNull( initializer );
if ( map == null ) {
map = new HashMap<>();
}
map.put( navigablePath, new InitializerHolder( initializer ) );
}
public void logInitializers() {
ResultsLogger logger = ResultsLogger.RESULTS_MESSAGE_LOGGER;
if ( !logger.isDebugEnabled() ) {
return;
}
if ( map == null ) {
logger.debug( "Initializer list is empty" );
}
else {
//Apparently we want to log this on multiple lines (existing code did this - not sure if that was by design):
//using a StringBuilder to avoid potentially interleaving the logs from different operations.
final StringBuilder sb = new StringBuilder( "Initializer list:\n" );
for ( Map.Entry<NavigablePath, InitializerHolder> holderEntry : map.entrySet() ) {
final NavigablePath navigablePath = holderEntry.getKey();
final Initializer initializer = holderEntry.getValue().initializer;
String formatted = String.format(
" %s -> %s@%s (%s)",
navigablePath,
initializer,
initializer.hashCode(),
initializer.getInitializedPart()
);
sb.append( '\t' );
sb.append( formatted );
sb.append( '\n' );
}
logger.debug( sb.toString() );
}
}
//Custom holder to avoid type pollution:
//we make the type explicit, and this is a concrete class.
private static final class InitializerHolder {
final Initializer initializer;
private InitializerHolder(final Initializer init) {
this.initializer = init;
}
}
}

View File

@ -6,9 +6,7 @@
*/
package org.hibernate.sql.results.internal;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import org.hibernate.CacheMode;
@ -70,7 +68,8 @@ public class ResultsHelper {
JdbcValuesMapping jdbcValuesMapping) {
final SessionFactoryImplementor sessionFactory = executionContext.getSession().getFactory();
final Map<NavigablePath, Initializer> initializerMap = new LinkedHashMap<>();
//custom Map<NavigablePath, Initializer>
final NavigablePathMapToInitializer initializerMap = new NavigablePathMapToInitializer();
final InitializersList.Builder initializersBuilder = new InitializersList.Builder();
final List<DomainResultAssembler<?>> assemblers = jdbcValuesMapping.resolveAssemblers(
@ -138,30 +137,13 @@ public class ResultsHelper {
}
);
logInitializers( initializerMap );
initializerMap.logInitializers();
final InitializersList initializersList = initializersBuilder.build( initializerMap );
return new StandardRowReader<>( assemblers, initializersList, rowTransformer, transformedResultJavaType );
}
private static void logInitializers(Map<NavigablePath, Initializer> initializerMap) {
if ( ! ResultsLogger.RESULTS_MESSAGE_LOGGER.isDebugEnabled() ) {
return;
}
ResultsLogger.RESULTS_MESSAGE_LOGGER.debug( "Initializer list" );
initializerMap.forEach( (navigablePath, initializer) -> {
ResultsLogger.RESULTS_MESSAGE_LOGGER.debugf(
" %s -> %s@%s (%s)",
navigablePath,
initializer,
initializer.hashCode(),
initializer.getInitializedPart()
);
} );
}
public static void finalizeCollectionLoading(
PersistenceContext persistenceContext,
CollectionPersister collectionDescriptor,