HHH-18271 Improve fast path for row reading
This commit is contained in:
parent
e9513b1db5
commit
cf44c30bf2
|
@ -40,7 +40,7 @@ import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
|||
import org.hibernate.sql.exec.spi.JdbcParametersList;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.internal.ImmutableFetchList;
|
||||
import org.hibernate.sql.results.internal.RowTransformerDatabaseSnapshotImpl;
|
||||
import org.hibernate.sql.results.internal.RowTransformerArrayImpl;
|
||||
import org.hibernate.sql.results.spi.ListResultsConsumer;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
|
@ -176,7 +176,7 @@ class DatabaseSnapshotExecutor {
|
|||
jdbcSelect,
|
||||
jdbcParameterBindings,
|
||||
new BaseExecutionContext( session ),
|
||||
RowTransformerDatabaseSnapshotImpl.instance(),
|
||||
RowTransformerArrayImpl.instance(),
|
||||
null,
|
||||
ListResultsConsumer.UniqueSemantic.FILTER,
|
||||
1
|
||||
|
|
|
@ -6,16 +6,13 @@
|
|||
*/
|
||||
package org.hibernate.loader.ast.internal;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||
import org.hibernate.sql.exec.spi.JdbcParametersList;
|
||||
import org.hibernate.sql.results.internal.RowTransformerDatabaseSnapshotImpl;
|
||||
import org.hibernate.sql.results.internal.RowTransformerArrayImpl;
|
||||
import org.hibernate.sql.results.spi.RowTransformer;
|
||||
|
||||
/**
|
||||
|
@ -37,7 +34,7 @@ public class SingleIdArrayLoadPlan extends SingleIdLoadPlan<Object[]> {
|
|||
|
||||
@Override
|
||||
protected RowTransformer<Object[]> getRowTransformer() {
|
||||
return RowTransformerDatabaseSnapshotImpl.instance();
|
||||
return RowTransformerArrayImpl.instance();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ public class LoadingCollectionEntryImpl implements LoadingCollectionEntry {
|
|||
|
||||
final boolean hasNoQueuedAdds = collectionInstance.endRead();
|
||||
final SharedSessionContractImplementor session = executionContext.getSession();
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContext();
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
||||
final CollectionPersister collectionDescriptor = getCollectionDescriptor();
|
||||
|
||||
ResultsHelper.finalizeCollectionLoading(
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* 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 org.hibernate.sql.results.spi.RowTransformer;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class RowTransformerDatabaseSnapshotImpl<T> implements RowTransformer<T> {
|
||||
/**
|
||||
* Singleton access
|
||||
*/
|
||||
public static final RowTransformerDatabaseSnapshotImpl INSTANCE = new RowTransformerDatabaseSnapshotImpl();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> RowTransformerDatabaseSnapshotImpl<T> instance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private RowTransformerDatabaseSnapshotImpl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T transformRow(Object[] row) {
|
||||
return (T) row;
|
||||
}
|
||||
}
|
|
@ -39,7 +39,7 @@ public class StandardRowReader<T> implements RowReader<T> {
|
|||
private final Initializer<InitializerData>[] sortedForResolveInstance;
|
||||
private final InitializerData[] sortedForResolveInstanceData;
|
||||
private final boolean hasCollectionInitializers;
|
||||
private final RowTransformer<T> rowTransformer;
|
||||
private final @Nullable RowTransformer<T> rowTransformer;
|
||||
private final Class<T> domainResultJavaType;
|
||||
|
||||
private final ComponentType componentType;
|
||||
|
@ -78,7 +78,11 @@ public class StandardRowReader<T> implements RowReader<T> {
|
|||
this.sortedForResolveInstance = (Initializer<InitializerData>[]) sortedForResolveInitializers;
|
||||
this.sortedForResolveInstanceData = new InitializerData[sortedForResolveInstance.length];
|
||||
this.hasCollectionInitializers = hasCollectionInitializers;
|
||||
this.rowTransformer = rowTransformer;
|
||||
this.rowTransformer = rowTransformer == RowTransformerArrayImpl.INSTANCE && resultAssemblers.length != 1
|
||||
|| rowTransformer == RowTransformerStandardImpl.INSTANCE
|
||||
|| rowTransformer == RowTransformerSingularReturnImpl.INSTANCE && resultAssemblers.length == 1
|
||||
? null
|
||||
: rowTransformer;
|
||||
this.domainResultJavaType = domainResultJavaType;
|
||||
if ( domainResultJavaType == null
|
||||
|| domainResultJavaType == Object[].class
|
||||
|
@ -136,6 +140,32 @@ public class StandardRowReader<T> implements RowReader<T> {
|
|||
public T readRow(RowProcessingState rowProcessingState) {
|
||||
coordinateInitializers( rowProcessingState );
|
||||
|
||||
final T result;
|
||||
if ( componentType != ComponentType.OBJECT ) {
|
||||
result = readPrimitiveRow( rowProcessingState );
|
||||
}
|
||||
else {
|
||||
if ( resultAssemblers.length == 1 && rowTransformer == null ) {
|
||||
//noinspection unchecked
|
||||
result = (T) resultAssemblers[0].assemble( rowProcessingState );
|
||||
}
|
||||
else {
|
||||
final Object[] resultRow = (Object[]) Array.newInstance( resultElementClass, resultAssemblers.length );
|
||||
for ( int i = 0; i < resultAssemblers.length; i++ ) {
|
||||
resultRow[i] = resultAssemblers[i].assemble( rowProcessingState );
|
||||
}
|
||||
//noinspection unchecked
|
||||
result = rowTransformer == null
|
||||
? (T) resultRow
|
||||
: rowTransformer.transformRow( resultRow );
|
||||
}
|
||||
}
|
||||
|
||||
finishUpRow();
|
||||
return result;
|
||||
}
|
||||
|
||||
private T readPrimitiveRow(RowProcessingState rowProcessingState) {
|
||||
// The following is ugly, but unfortunately necessary to not hurt performance.
|
||||
// This implementation was micro-benchmarked and discussed with Francesco Nigro,
|
||||
// who hinted that using this style instead of the reflective Array.getLength(), Array.set()
|
||||
|
@ -143,110 +173,57 @@ public class StandardRowReader<T> implements RowReader<T> {
|
|||
switch ( componentType ) {
|
||||
case BOOLEAN:
|
||||
final boolean[] resultBooleanRow = new boolean[resultAssemblers.length];
|
||||
|
||||
for ( int i = 0; i < resultAssemblers.length; i++ ) {
|
||||
final DomainResultAssembler assembler = resultAssemblers[i];
|
||||
resultBooleanRow[i] = (boolean) assembler.assemble( rowProcessingState );
|
||||
resultBooleanRow[i] = (boolean) resultAssemblers[i].assemble( rowProcessingState );
|
||||
}
|
||||
|
||||
afterRow( rowProcessingState );
|
||||
|
||||
return (T) resultBooleanRow;
|
||||
case BYTE:
|
||||
final byte[] resultByteRow = new byte[resultAssemblers.length];
|
||||
|
||||
for ( int i = 0; i < resultAssemblers.length; i++ ) {
|
||||
final DomainResultAssembler assembler = resultAssemblers[i];
|
||||
resultByteRow[i] = (byte) assembler.assemble( rowProcessingState );
|
||||
resultByteRow[i] = (byte) resultAssemblers[i].assemble( rowProcessingState );
|
||||
}
|
||||
|
||||
afterRow( rowProcessingState );
|
||||
|
||||
return (T) resultByteRow;
|
||||
case CHAR:
|
||||
final char[] resultCharRow = new char[resultAssemblers.length];
|
||||
|
||||
for ( int i = 0; i < resultAssemblers.length; i++ ) {
|
||||
final DomainResultAssembler assembler = resultAssemblers[i];
|
||||
resultCharRow[i] = (char) assembler.assemble( rowProcessingState );
|
||||
resultCharRow[i] = (char) resultAssemblers[i].assemble( rowProcessingState );
|
||||
}
|
||||
|
||||
afterRow( rowProcessingState );
|
||||
|
||||
return (T) resultCharRow;
|
||||
case SHORT:
|
||||
final short[] resultShortRow = new short[resultAssemblers.length];
|
||||
|
||||
for ( int i = 0; i < resultAssemblers.length; i++ ) {
|
||||
final DomainResultAssembler assembler = resultAssemblers[i];
|
||||
resultShortRow[i] = (short) assembler.assemble( rowProcessingState );
|
||||
resultShortRow[i] = (short) resultAssemblers[i].assemble( rowProcessingState );
|
||||
}
|
||||
|
||||
afterRow( rowProcessingState );
|
||||
|
||||
return (T) resultShortRow;
|
||||
case INT:
|
||||
final int[] resultIntRow = new int[resultAssemblers.length];
|
||||
|
||||
for ( int i = 0; i < resultAssemblers.length; i++ ) {
|
||||
final DomainResultAssembler assembler = resultAssemblers[i];
|
||||
resultIntRow[i] = (int) assembler.assemble( rowProcessingState );
|
||||
resultIntRow[i] = (int) resultAssemblers[i].assemble( rowProcessingState );
|
||||
}
|
||||
|
||||
afterRow( rowProcessingState );
|
||||
|
||||
return (T) resultIntRow;
|
||||
case LONG:
|
||||
final long[] resultLongRow = new long[resultAssemblers.length];
|
||||
|
||||
for ( int i = 0; i < resultAssemblers.length; i++ ) {
|
||||
final DomainResultAssembler assembler = resultAssemblers[i];
|
||||
resultLongRow[i] = (long) assembler.assemble( rowProcessingState );
|
||||
resultLongRow[i] = (long) resultAssemblers[i].assemble( rowProcessingState );
|
||||
}
|
||||
|
||||
afterRow( rowProcessingState );
|
||||
|
||||
return (T) resultLongRow;
|
||||
case FLOAT:
|
||||
final float[] resultFloatRow = new float[resultAssemblers.length];
|
||||
|
||||
for ( int i = 0; i < resultAssemblers.length; i++ ) {
|
||||
final DomainResultAssembler assembler = resultAssemblers[i];
|
||||
resultFloatRow[i] = (float) assembler.assemble( rowProcessingState );
|
||||
resultFloatRow[i] = (float) resultAssemblers[i].assemble( rowProcessingState );
|
||||
}
|
||||
|
||||
afterRow( rowProcessingState );
|
||||
|
||||
return (T) resultFloatRow;
|
||||
case DOUBLE:
|
||||
final double[] resultDoubleRow = new double[resultAssemblers.length];
|
||||
|
||||
for ( int i = 0; i < resultAssemblers.length; i++ ) {
|
||||
final DomainResultAssembler assembler = resultAssemblers[i];
|
||||
resultDoubleRow[i] = (double) assembler.assemble( rowProcessingState );
|
||||
resultDoubleRow[i] = (double) resultAssemblers[i].assemble( rowProcessingState );
|
||||
}
|
||||
|
||||
afterRow( rowProcessingState );
|
||||
|
||||
return (T) resultDoubleRow;
|
||||
default:
|
||||
final Object[] resultRow = (Object[]) Array.newInstance( resultElementClass, resultAssemblers.length );
|
||||
|
||||
for ( int i = 0; i < resultAssemblers.length; i++ ) {
|
||||
final DomainResultAssembler assembler = resultAssemblers[i];
|
||||
resultRow[i] = assembler.assemble( rowProcessingState );
|
||||
}
|
||||
|
||||
afterRow( rowProcessingState );
|
||||
|
||||
return rowTransformer.transformRow( resultRow );
|
||||
throw new AssertionError( "Object should be handled specially" );
|
||||
}
|
||||
}
|
||||
|
||||
private void afterRow(RowProcessingState rowProcessingState) {
|
||||
finishUpRow();
|
||||
}
|
||||
|
||||
private void finishUpRow() {
|
||||
for ( InitializerData data : initializersData ) {
|
||||
data.setState( Initializer.State.UNINITIALIZED );
|
||||
|
|
|
@ -105,7 +105,7 @@ public class ListResultsConsumer<R> implements ResultsConsumer<List<R>, R> {
|
|||
}
|
||||
|
||||
private static class Results<R> {
|
||||
private final List<R> results;
|
||||
private final ArrayList<R> results;
|
||||
private final JavaType<R> resultJavaType;
|
||||
|
||||
public Results(JavaType<R> resultJavaType, int initialSize) {
|
||||
|
@ -188,37 +188,17 @@ public class ListResultsConsumer<R> implements ResultsConsumer<List<R>, R> {
|
|||
results = new Results<>( domainResultJavaType, initialCollectionSize );
|
||||
}
|
||||
|
||||
int readRows = 0;
|
||||
final int readRows;
|
||||
if ( uniqueSemantic == UniqueSemantic.FILTER
|
||||
|| uniqueSemantic == UniqueSemantic.ASSERT && rowReader.hasCollectionInitializers()
|
||||
|| uniqueSemantic == UniqueSemantic.ALLOW && isEntityResultType ) {
|
||||
while ( rowProcessingState.next() ) {
|
||||
final boolean added = results.addUnique( rowReader.readRow( rowProcessingState ) );
|
||||
rowProcessingState.finishRowProcessing( added );
|
||||
readRows++;
|
||||
}
|
||||
readRows = readUnique( rowProcessingState, rowReader, results );
|
||||
}
|
||||
else if ( uniqueSemantic == UniqueSemantic.ASSERT ) {
|
||||
while ( rowProcessingState.next() ) {
|
||||
if ( !results.addUnique( rowReader.readRow( rowProcessingState ) ) ) {
|
||||
throw new HibernateException(
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"Duplicate row was found and `%s` was specified",
|
||||
UniqueSemantic.ASSERT
|
||||
)
|
||||
);
|
||||
}
|
||||
rowProcessingState.finishRowProcessing( true );
|
||||
readRows++;
|
||||
}
|
||||
readRows = readUniqueAssert( rowProcessingState, rowReader, results );
|
||||
}
|
||||
else {
|
||||
while ( rowProcessingState.next() ) {
|
||||
results.add( rowReader.readRow( rowProcessingState ) );
|
||||
rowProcessingState.finishRowProcessing( true );
|
||||
readRows++;
|
||||
}
|
||||
readRows = read( rowProcessingState, rowReader, results );
|
||||
}
|
||||
|
||||
rowReader.finishUp( rowProcessingState );
|
||||
|
@ -260,6 +240,53 @@ public class ListResultsConsumer<R> implements ResultsConsumer<List<R>, R> {
|
|||
throw new IllegalStateException( "Should not reach this" );
|
||||
}
|
||||
|
||||
private static <R> int read(
|
||||
RowProcessingStateStandardImpl rowProcessingState,
|
||||
RowReader<R> rowReader,
|
||||
Results<R> results) {
|
||||
int readRows = 0;
|
||||
while ( rowProcessingState.next() ) {
|
||||
results.add( rowReader.readRow( rowProcessingState ) );
|
||||
rowProcessingState.finishRowProcessing( true );
|
||||
readRows++;
|
||||
}
|
||||
return readRows;
|
||||
}
|
||||
|
||||
private static <R> int readUniqueAssert(
|
||||
RowProcessingStateStandardImpl rowProcessingState,
|
||||
RowReader<R> rowReader,
|
||||
Results<R> results) {
|
||||
int readRows = 0;
|
||||
while ( rowProcessingState.next() ) {
|
||||
if ( !results.addUnique( rowReader.readRow( rowProcessingState ) ) ) {
|
||||
throw new HibernateException(
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"Duplicate row was found and `%s` was specified",
|
||||
UniqueSemantic.ASSERT
|
||||
)
|
||||
);
|
||||
}
|
||||
rowProcessingState.finishRowProcessing( true );
|
||||
readRows++;
|
||||
}
|
||||
return readRows;
|
||||
}
|
||||
|
||||
private static <R> int readUnique(
|
||||
RowProcessingStateStandardImpl rowProcessingState,
|
||||
RowReader<R> rowReader,
|
||||
Results<R> results) {
|
||||
int readRows = 0;
|
||||
while ( rowProcessingState.next() ) {
|
||||
final boolean added = results.addUnique( rowReader.readRow( rowProcessingState ) );
|
||||
rowProcessingState.finishRowProcessing( added );
|
||||
readRows++;
|
||||
}
|
||||
return readRows;
|
||||
}
|
||||
|
||||
private JavaType<R> resolveDomainResultJavaType(
|
||||
Class<R> domainResultResultJavaType,
|
||||
List<@Nullable JavaType<?>> resultJavaTypes,
|
||||
|
|
Loading…
Reference in New Issue