HHH-16695 make fetch profiles actually work for natural id loading
This commit is contained in:
parent
3dfa70a781
commit
87a320615c
|
@ -110,7 +110,7 @@ public interface NaturalIdLoadAccess<T> {
|
|||
* Determines if cached natural id cross-references are synchronized
|
||||
* before query execution with unflushed modifications made in memory
|
||||
* to {@linkplain org.hibernate.annotations.NaturalId#mutable mutable}
|
||||
* natural ids .
|
||||
* natural ids.
|
||||
* <p>
|
||||
* By default, every cached cross-reference is updated to reflect any
|
||||
* modification made in memory.
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
package org.hibernate.loader.ast.internal;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.BiConsumer;
|
||||
|
@ -49,17 +48,15 @@ import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
|
|||
import org.hibernate.sql.exec.spi.JdbcParameterBinding;
|
||||
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.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParent;
|
||||
import org.hibernate.sql.results.graph.Fetchable;
|
||||
import org.hibernate.sql.results.graph.FetchableContainer;
|
||||
import org.hibernate.sql.results.graph.*;
|
||||
import org.hibernate.sql.results.graph.internal.ImmutableFetchList;
|
||||
import org.hibernate.sql.results.spi.ListResultsConsumer;
|
||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
/**
|
||||
* Base support for NaturalIdLoader implementations
|
||||
* Base support for {@link NaturalIdLoader} implementations
|
||||
*/
|
||||
public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
|
||||
|
||||
|
@ -91,35 +88,82 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
|
|||
|
||||
@Override
|
||||
public T load(Object naturalIdValue, NaturalIdLoadOptions options, SharedSessionContractImplementor session) {
|
||||
return selectByNaturalId(
|
||||
naturalIdMapping().normalizeInput( naturalIdValue ),
|
||||
options,
|
||||
(tableGroup, creationState) -> entityDescriptor.createDomainResult(
|
||||
new NavigablePath( entityDescriptor().getRootPathName() ),
|
||||
tableGroup,
|
||||
final SessionFactoryImplementor sessionFactory = session.getFactory();
|
||||
|
||||
final LockOptions lockOptions = options.getLockOptions() == null ? LockOptions.NONE : options.getLockOptions();
|
||||
|
||||
final SelectStatement sqlSelect = LoaderSelectBuilder.createSelect(
|
||||
getLoadable(),
|
||||
// null here means to select everything
|
||||
null,
|
||||
creationState
|
||||
),
|
||||
true,
|
||||
emptyList(), // we're going to add the restrictions ourselves
|
||||
null,
|
||||
1,
|
||||
session.getLoadQueryInfluencers(),
|
||||
lockOptions,
|
||||
JdbcParametersList.newBuilder()::add,
|
||||
sessionFactory
|
||||
);
|
||||
|
||||
// we have to add the restrictions ourselves manually because we want special null handling
|
||||
final JdbcParameterBindings jdbcParamBindings = new JdbcParameterBindingsImpl( naturalIdMapping.getJdbcTypeCount() );
|
||||
applyNaturalIdRestriction(
|
||||
naturalIdMapping().normalizeInput( naturalIdValue ),
|
||||
sqlSelect.getQuerySpec().getFromClause().getRoots().get(0),
|
||||
sqlSelect.getQuerySpec()::applyPredicate,
|
||||
jdbcParamBindings::addBinding,
|
||||
new LoaderSqlAstCreationState(
|
||||
sqlSelect.getQuerySpec(),
|
||||
new SqlAliasBaseManager(),
|
||||
new SimpleFromClauseAccessImpl(),
|
||||
lockOptions,
|
||||
AbstractNaturalIdLoader::visitFetches,
|
||||
(statsEnabled) -> {
|
||||
// entityDescriptor().getPreLoadListener().startingLoad( entityDescriptor, naturalIdValue, KeyType.NATURAL_ID, LoadSource.DATABASE );
|
||||
return statsEnabled ? System.nanoTime() : -1;
|
||||
},
|
||||
(result,startToken) -> {
|
||||
true,
|
||||
new LoadQueryInfluencers( sessionFactory ),
|
||||
sessionFactory
|
||||
),
|
||||
session
|
||||
);
|
||||
|
||||
final QueryOptions queryOptions = new SimpleQueryOptions( lockOptions, false );
|
||||
final JdbcOperationQuerySelect jdbcSelect =
|
||||
sessionFactory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory()
|
||||
.buildSelectTranslator( sessionFactory, sqlSelect )
|
||||
.translate( jdbcParamBindings, queryOptions );
|
||||
|
||||
final long startToken = sessionFactory.getStatistics().isStatisticsEnabled() ? System.nanoTime() : -1;
|
||||
|
||||
//noinspection unchecked
|
||||
final List<T> results = session.getFactory().getJdbcServices().getJdbcSelectExecutor().list(
|
||||
jdbcSelect,
|
||||
jdbcParamBindings,
|
||||
new NaturalIdLoaderWithOptionsExecutionContext( session, queryOptions ),
|
||||
row -> (T) row[0],
|
||||
ListResultsConsumer.UniqueSemantic.FILTER
|
||||
);
|
||||
|
||||
if ( results.size() > 1 ) {
|
||||
throw new HibernateException(
|
||||
String.format(
|
||||
"Loading by natural-id returned more that one row : %s",
|
||||
entityDescriptor.getEntityName()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
final T result = results.isEmpty() ? null : results.get(0);
|
||||
// entityDescriptor().getPostLoadListener().completedLoad( result, entityDescriptor(), naturalIdValue, KeyType.NATURAL_ID, LoadSource.DATABASE );
|
||||
if ( startToken > 0 ) {
|
||||
session.getFactory().getStatistics().naturalIdQueryExecuted(
|
||||
entityDescriptor().getEntityPersister().getRootEntityName(),
|
||||
System.nanoTime() - startToken
|
||||
);
|
||||
// // todo (6.0) : need a "load-by-natural-id" stat
|
||||
// // e.g.,
|
||||
// // todo (6.0) : need a "load-by-natural-id" stat, e.g.,
|
||||
// // final Object identifier = entityDescriptor().getIdentifierMapping().getIdentifier( result, session );
|
||||
// // session.getFactory().getStatistics().entityLoadedByNaturalId( entityDescriptor(), identifier );
|
||||
}
|
||||
},
|
||||
session
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -134,17 +178,8 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
|
|||
BiConsumer<Object,Long> statementCompletionHandler,
|
||||
SharedSessionContractImplementor session) {
|
||||
final SessionFactoryImplementor sessionFactory = session.getFactory();
|
||||
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
|
||||
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
|
||||
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
|
||||
|
||||
final LockOptions lockOptions;
|
||||
if ( options.getLockOptions() != null ) {
|
||||
lockOptions = options.getLockOptions();
|
||||
}
|
||||
else {
|
||||
lockOptions = LockOptions.NONE;
|
||||
}
|
||||
final LockOptions lockOptions = options.getLockOptions() != null ? options.getLockOptions() : LockOptions.NONE;
|
||||
|
||||
final NavigablePath entityPath = new NavigablePath( entityDescriptor.getRootPathName() );
|
||||
final QuerySpec rootQuerySpec = new QuerySpec( true );
|
||||
|
@ -174,7 +209,7 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
|
|||
|
||||
final DomainResult<?> domainResult = domainResultProducer.apply( rootTableGroup, sqlAstCreationState );
|
||||
|
||||
final SelectStatement sqlSelect = new SelectStatement( rootQuerySpec, Collections.singletonList( domainResult ) );
|
||||
final SelectStatement sqlSelect = new SelectStatement( rootQuerySpec, singletonList( domainResult ) );
|
||||
|
||||
final JdbcParameterBindings jdbcParamBindings = new JdbcParameterBindingsImpl( naturalIdMapping.getJdbcTypeCount() );
|
||||
|
||||
|
@ -188,11 +223,12 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
|
|||
);
|
||||
|
||||
final QueryOptions queryOptions = new SimpleQueryOptions( lockOptions, false );
|
||||
final JdbcOperationQuerySelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory, sqlSelect )
|
||||
final JdbcOperationQuerySelect jdbcSelect =
|
||||
sessionFactory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory()
|
||||
.buildSelectTranslator( sessionFactory, sqlSelect )
|
||||
.translate( jdbcParamBindings, queryOptions );
|
||||
|
||||
final StatisticsImplementor statistics = sessionFactory.getStatistics();
|
||||
final Long startToken = statementStartHandler.apply( statistics.isStatisticsEnabled() );
|
||||
final Long startToken = statementStartHandler.apply( sessionFactory.getStatistics().isStatisticsEnabled() );
|
||||
|
||||
//noinspection unchecked
|
||||
final List<L> results = session.getFactory().getJdbcServices().getJdbcSelectExecutor().list(
|
||||
|
@ -212,16 +248,8 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
|
|||
);
|
||||
}
|
||||
|
||||
final L result;
|
||||
if ( results.isEmpty() ) {
|
||||
result = null;
|
||||
}
|
||||
else {
|
||||
result = results.get( 0 );
|
||||
}
|
||||
|
||||
final L result = results.isEmpty() ? null : results.get(0);
|
||||
statementCompletionHandler.accept( result, startToken );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -246,7 +274,11 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
|
|||
SelectableMapping selectableMapping,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
@SuppressWarnings("unused") SessionFactoryImplementor sessionFactory) {
|
||||
final TableReference tableReference = rootTableGroup.getTableReference( rootTableGroup.getNavigablePath(), selectableMapping.getContainingTableExpression() );
|
||||
final TableReference tableReference =
|
||||
rootTableGroup.getTableReference(
|
||||
rootTableGroup.getNavigablePath(),
|
||||
selectableMapping.getContainingTableExpression()
|
||||
);
|
||||
if ( tableReference == null ) {
|
||||
throw new IllegalStateException(
|
||||
String.format(
|
||||
|
@ -300,7 +332,7 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
|
|||
JdbcParametersList.Builder jdbcParametersBuilder = JdbcParametersList.newBuilder();
|
||||
final SelectStatement sqlSelect = LoaderSelectBuilder.createSelect(
|
||||
entityDescriptor(),
|
||||
Collections.singletonList( naturalIdMapping() ),
|
||||
singletonList( naturalIdMapping() ),
|
||||
entityDescriptor().getIdentifierMapping(),
|
||||
null,
|
||||
1,
|
||||
|
@ -324,7 +356,8 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
|
|||
session
|
||||
);
|
||||
assert offset == jdbcParameters.size();
|
||||
final JdbcOperationQuerySelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory, sqlSelect )
|
||||
final JdbcOperationQuerySelect jdbcSelect =
|
||||
sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory, sqlSelect )
|
||||
.translate( jdbcParamBindings, QueryOptions.NONE );
|
||||
|
||||
final List<Object> results = session.getFactory().getJdbcServices().getJdbcSelectExecutor().list(
|
||||
|
@ -339,11 +372,12 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
|
|||
ListResultsConsumer.UniqueSemantic.FILTER
|
||||
);
|
||||
|
||||
if ( results.isEmpty() ) {
|
||||
switch ( results.size() ) {
|
||||
case 0:
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( results.size() > 1 ) {
|
||||
case 1:
|
||||
return results.get( 0 );
|
||||
default:
|
||||
throw new HibernateException(
|
||||
String.format(
|
||||
"Resolving id to natural-id returned more that one row : %s #%s",
|
||||
|
@ -352,8 +386,6 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
return results.get( 0 );
|
||||
}
|
||||
|
||||
private static ImmutableFetchList visitFetches(
|
||||
|
|
|
@ -282,6 +282,36 @@ public class LoaderSelectBuilder {
|
|||
return process.generateSelect();
|
||||
}
|
||||
|
||||
// TODO: this method is probably unnecessary if we make
|
||||
// determineWhetherToForceIdSelection() a bit smarter
|
||||
static SelectStatement createSelect(
|
||||
Loadable loadable,
|
||||
List<ModelPart> partsToSelect,
|
||||
boolean forceIdentifierSelection,
|
||||
List<ModelPart> restrictedParts,
|
||||
DomainResult<?> cachedDomainResult,
|
||||
int numberOfKeysToLoad,
|
||||
LoadQueryInfluencers loadQueryInfluencers,
|
||||
LockOptions lockOptions,
|
||||
Consumer<JdbcParameter> jdbcParameterConsumer,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
final LoaderSelectBuilder process = new LoaderSelectBuilder(
|
||||
sessionFactory,
|
||||
loadable,
|
||||
partsToSelect,
|
||||
restrictedParts,
|
||||
cachedDomainResult,
|
||||
numberOfKeysToLoad,
|
||||
loadQueryInfluencers,
|
||||
lockOptions,
|
||||
determineGraphTraversalState( loadQueryInfluencers, sessionFactory ),
|
||||
forceIdentifierSelection,
|
||||
jdbcParameterConsumer
|
||||
);
|
||||
|
||||
return process.generateSelect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an SQL AST select-statement used for subselect-based CollectionLoader
|
||||
*
|
||||
|
|
|
@ -8,7 +8,6 @@ package org.hibernate.loader.ast.internal;
|
|||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
|
|
|
@ -37,13 +37,13 @@ public class SingleIdEntityLoaderStandardImpl<T> extends SingleIdEntityLoaderSup
|
|||
// see org.hibernate.persister.entity.AbstractEntityPersister#createLoaders
|
||||
// we should preload a few - maybe LockMode.NONE and LockMode.READ
|
||||
final LockOptions lockOptions = LockOptions.NONE;
|
||||
final LoadQueryInfluencers queryInfluencers = new LoadQueryInfluencers( sessionFactory );
|
||||
final LoadQueryInfluencers influencers = new LoadQueryInfluencers( sessionFactory );
|
||||
final SingleIdLoadPlan<T> plan = createLoadPlan(
|
||||
lockOptions,
|
||||
queryInfluencers,
|
||||
influencers,
|
||||
sessionFactory
|
||||
);
|
||||
if ( isLoadPlanReusable( lockOptions, queryInfluencers ) ) {
|
||||
if ( isLoadPlanReusable( lockOptions, influencers ) ) {
|
||||
selectByLockMode.put( lockOptions.getLockMode(), plan );
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ public class SingleIdEntityLoaderStandardImpl<T> extends SingleIdEntityLoaderSup
|
|||
}
|
||||
}
|
||||
else {
|
||||
return createLoadPlan(lockOptions, loadQueryInfluencers, sessionFactory);
|
||||
return createLoadPlan( lockOptions, loadQueryInfluencers, sessionFactory );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ import org.hibernate.query.internal.SimpleQueryOptions;
|
|||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.query.spi.QueryOptionsAdapter;
|
||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||
import org.hibernate.sql.exec.internal.BaseExecutionContext;
|
||||
import org.hibernate.sql.exec.internal.CallbackImpl;
|
||||
|
@ -34,14 +33,14 @@ import org.hibernate.sql.results.spi.ListResultsConsumer;
|
|||
import org.hibernate.sql.results.spi.RowTransformer;
|
||||
|
||||
/**
|
||||
* todo (6.0) : this can generically define a load-by-uk as well. only the SQL AST and `restrictivePart` vary and they are passed as ctor args
|
||||
*
|
||||
* Describes a plan for loading an entity by identifier.
|
||||
*
|
||||
* @implNote Made up of (1) a SQL AST for the SQL SELECT and (2) the `ModelPart` used as the restriction
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
// todo (6.0) : this can generically define a load-by-uk as well.
|
||||
// only the SQL AST and `restrictivePart` vary and they are passed as constructor args
|
||||
public class SingleIdLoadPlan<T> implements SingleEntityLoadPlan {
|
||||
private final EntityMappingType entityMappingType;
|
||||
private final ModelPart restrictivePart;
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
package org.hibernate.loader.ast.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -24,7 +23,6 @@ import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
|||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||
import org.hibernate.sql.exec.internal.BaseExecutionContext;
|
||||
import org.hibernate.sql.exec.internal.CallbackImpl;
|
||||
|
|
|
@ -8,7 +8,6 @@ package org.hibernate.loader.internal;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
|
@ -98,18 +97,18 @@ public abstract class BaseNaturalIdLoadAccessImpl<T> implements NaturalIdLoadOpt
|
|||
protected void synchronizationEnabled(boolean synchronizationEnabled) {
|
||||
this.synchronizationEnabled = synchronizationEnabled;
|
||||
}
|
||||
|
||||
protected final Object resolveNaturalId(Map<String, Object> naturalIdParameters) {
|
||||
performAnyNeededCrossReferenceSynchronizations();
|
||||
|
||||
final Object resolvedId = entityPersister()
|
||||
.getNaturalIdLoader()
|
||||
.resolveNaturalIdToId( naturalIdParameters, context.getSession() );
|
||||
|
||||
return resolvedId == INVALID_NATURAL_ID_REFERENCE
|
||||
? null
|
||||
: resolvedId;
|
||||
}
|
||||
//
|
||||
// protected final Object resolveNaturalId(Map<String, Object> naturalIdParameters) {
|
||||
// performAnyNeededCrossReferenceSynchronizations();
|
||||
//
|
||||
// final Object resolvedId = entityPersister()
|
||||
// .getNaturalIdLoader()
|
||||
// .resolveNaturalIdToId( naturalIdParameters, context.getSession() );
|
||||
//
|
||||
// return resolvedId == INVALID_NATURAL_ID_REFERENCE
|
||||
// ? null
|
||||
// : resolvedId;
|
||||
// }
|
||||
|
||||
protected void performAnyNeededCrossReferenceSynchronizations() {
|
||||
if ( !synchronizationEnabled ) {
|
||||
|
|
|
@ -232,7 +232,7 @@ public class DeferredResultSetAccess extends AbstractResultSetAccess {
|
|||
.getEventListenerManager();
|
||||
|
||||
long executeStartNanos = 0;
|
||||
if ( this.sqlStatementLogger.getLogSlowQuery() > 0 ) {
|
||||
if ( sqlStatementLogger.getLogSlowQuery() > 0 ) {
|
||||
executeStartNanos = System.nanoTime();
|
||||
}
|
||||
try {
|
||||
|
|
|
@ -97,9 +97,9 @@ public class ListResultsConsumer<R> implements ResultsConsumer<List<R>, R> {
|
|||
|
||||
private static class Results<R> {
|
||||
private final List<R> results = new ArrayList<>();
|
||||
private final JavaType resultJavaType;
|
||||
private final JavaType<R> resultJavaType;
|
||||
|
||||
public Results(JavaType resultJavaType) {
|
||||
public Results(JavaType<R> resultJavaType) {
|
||||
this.resultJavaType = resultJavaType;
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,7 @@ public class ListResultsConsumer<R> implements ResultsConsumer<List<R>, R> {
|
|||
|
||||
private final IdentityHashMap<R, Object> added = new IdentityHashMap<>();
|
||||
|
||||
public EntityResult(JavaType resultJavaType) {
|
||||
public EntityResult(JavaType<R> resultJavaType) {
|
||||
super( resultJavaType );
|
||||
}
|
||||
|
||||
|
@ -162,25 +162,27 @@ public class ListResultsConsumer<R> implements ResultsConsumer<List<R>, R> {
|
|||
typeConfiguration
|
||||
);
|
||||
|
||||
final boolean isEnityResultType = domainResultJavaType instanceof EntityJavaType;
|
||||
final boolean isEntityResultType = domainResultJavaType instanceof EntityJavaType;
|
||||
|
||||
final Results<R> results;
|
||||
if ( ( uniqueSemantic == UniqueSemantic.ALLOW || uniqueSemantic == UniqueSemantic.FILTER ) && isEnityResultType ) {
|
||||
if ( isEntityResultType
|
||||
&& ( uniqueSemantic == UniqueSemantic.ALLOW
|
||||
|| uniqueSemantic == UniqueSemantic.FILTER ) ) {
|
||||
results = new EntityResult<>( domainResultJavaType );
|
||||
}
|
||||
else {
|
||||
results = new Results<>( domainResultJavaType );
|
||||
}
|
||||
|
||||
if ( this.uniqueSemantic == UniqueSemantic.FILTER
|
||||
|| this.uniqueSemantic == UniqueSemantic.ASSERT && rowProcessingState.hasCollectionInitializers()
|
||||
|| this.uniqueSemantic == UniqueSemantic.ALLOW && isEnityResultType ) {
|
||||
if ( uniqueSemantic == UniqueSemantic.FILTER
|
||||
|| uniqueSemantic == UniqueSemantic.ASSERT && rowProcessingState.hasCollectionInitializers()
|
||||
|| uniqueSemantic == UniqueSemantic.ALLOW && isEntityResultType ) {
|
||||
while ( rowProcessingState.next() ) {
|
||||
results.addUnique( rowReader.readRow( rowProcessingState, processingOptions ) );
|
||||
rowProcessingState.finishRowProcessing();
|
||||
}
|
||||
}
|
||||
else if ( this.uniqueSemantic == UniqueSemantic.ASSERT ) {
|
||||
else if ( uniqueSemantic == UniqueSemantic.ASSERT ) {
|
||||
while ( rowProcessingState.next() ) {
|
||||
if ( !results.addUnique( rowReader.readRow( rowProcessingState, processingOptions ) ) ) {
|
||||
throw new HibernateException(
|
||||
|
@ -210,7 +212,8 @@ public class ListResultsConsumer<R> implements ResultsConsumer<List<R>, R> {
|
|||
}
|
||||
|
||||
//noinspection unchecked
|
||||
final ResultListTransformer<R> resultListTransformer = (ResultListTransformer<R>) queryOptions.getResultListTransformer();
|
||||
final ResultListTransformer<R> resultListTransformer =
|
||||
(ResultListTransformer<R>) queryOptions.getResultListTransformer();
|
||||
if ( resultListTransformer != null ) {
|
||||
return resultListTransformer.transformList( results.getResults() );
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import jakarta.persistence.ManyToOne;
|
|||
import jakarta.persistence.OneToMany;
|
||||
import org.hibernate.annotations.FetchProfile;
|
||||
import org.hibernate.annotations.FetchProfileOverride;
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
|
@ -60,7 +61,7 @@ public class NewFetchTest {
|
|||
s.persist(e);
|
||||
});
|
||||
|
||||
F f = scope.fromSession( s -> s.find(F.class, 1));
|
||||
F f = scope.fromSession( s -> s.find(F.class, 1) );
|
||||
assertFalse( isInitialized( f.g ) );
|
||||
assertFalse( isInitialized( f.es ) );
|
||||
F ff = scope.fromSession( s -> {
|
||||
|
@ -70,7 +71,7 @@ public class NewFetchTest {
|
|||
assertTrue( isInitialized( ff.g ) );
|
||||
assertTrue( isInitialized( ff.es ) );
|
||||
|
||||
E e = scope.fromSession( s -> s.find(E.class, 1));
|
||||
E e = scope.fromSession( s -> s.find(E.class, 1) );
|
||||
assertFalse( isInitialized( e.f ) );
|
||||
E ee = scope.fromSession( s -> {
|
||||
s.enableFetchProfile(OLD_PROFILE);
|
||||
|
@ -79,6 +80,85 @@ public class NewFetchTest {
|
|||
assertTrue( isInitialized( ee.f ) );
|
||||
}
|
||||
|
||||
@Test void testById(SessionFactoryScope scope) {
|
||||
scope.inTransaction( s-> {
|
||||
G g = new G();
|
||||
F f = new F();
|
||||
E e = new E();
|
||||
f.g = g;
|
||||
e.f = f;
|
||||
s.persist(g);
|
||||
s.persist(f);
|
||||
s.persist(e);
|
||||
});
|
||||
|
||||
F f = scope.fromSession( s -> s.byId(F.class).load(1) );
|
||||
assertFalse( isInitialized( f.g ) );
|
||||
assertFalse( isInitialized( f.es ) );
|
||||
F ff = scope.fromSession( s -> s.byId(F.class).enableFetchProfile(NEW_PROFILE).load(1) );
|
||||
assertTrue( isInitialized( ff.g ) );
|
||||
assertTrue( isInitialized( ff.es ) );
|
||||
|
||||
E e = scope.fromSession( s -> s.byId(E.class).load(1) );
|
||||
assertFalse( isInitialized( e.f ) );
|
||||
E ee = scope.fromSession( s -> s.byId(E.class).enableFetchProfile(OLD_PROFILE).load(1) );
|
||||
assertTrue( isInitialized( ee.f ) );
|
||||
}
|
||||
|
||||
@Test void testBySimpleNaturalId(SessionFactoryScope scope) {
|
||||
scope.inTransaction( s-> {
|
||||
G g = new G();
|
||||
F f = new F();
|
||||
E e = new E();
|
||||
f.g = g;
|
||||
e.f = f;
|
||||
e.s = "1";
|
||||
f.s = "1";
|
||||
s.persist(g);
|
||||
s.persist(f);
|
||||
s.persist(e);
|
||||
});
|
||||
|
||||
F f = scope.fromSession( s -> s.bySimpleNaturalId(F.class).load("1") );
|
||||
assertFalse( isInitialized( f.g ) );
|
||||
assertFalse( isInitialized( f.es ) );
|
||||
F ff = scope.fromSession( s -> s.bySimpleNaturalId(F.class).enableFetchProfile(NEW_PROFILE).load("1") );
|
||||
assertTrue( isInitialized( ff.g ) );
|
||||
assertTrue( isInitialized( ff.es ) );
|
||||
|
||||
E e = scope.fromSession( s -> s.bySimpleNaturalId(E.class).load("1") );
|
||||
assertFalse( isInitialized( e.f ) );
|
||||
E ee = scope.fromSession( s -> s.bySimpleNaturalId(E.class).enableFetchProfile(OLD_PROFILE).load("1") );
|
||||
assertTrue( isInitialized( ee.f ) );
|
||||
}
|
||||
|
||||
@Test void testByNaturalId(SessionFactoryScope scope) {
|
||||
scope.inTransaction( s-> {
|
||||
G g = new G();
|
||||
F f = new F();
|
||||
E e = new E();
|
||||
f.g = g;
|
||||
e.f = f;
|
||||
e.s = "2";
|
||||
f.s = "2";
|
||||
s.persist(g);
|
||||
s.persist(f);
|
||||
s.persist(e);
|
||||
});
|
||||
|
||||
F f = scope.fromSession( s -> s.byNaturalId(F.class).using("s", "2").load() );
|
||||
assertFalse( isInitialized( f.g ) );
|
||||
assertFalse( isInitialized( f.es ) );
|
||||
F ff = scope.fromSession( s -> s.byNaturalId(F.class).using("s", "2").enableFetchProfile(NEW_PROFILE).load() );
|
||||
assertTrue( isInitialized( ff.g ) );
|
||||
assertTrue( isInitialized( ff.es ) );
|
||||
|
||||
E e = scope.fromSession( s -> s.byNaturalId(E.class).using("s", "2").load() );
|
||||
assertFalse( isInitialized( e.f ) );
|
||||
E ee = scope.fromSession( s -> s.byNaturalId(E.class).using("s", "2").enableFetchProfile(OLD_PROFILE).load() );
|
||||
assertTrue( isInitialized( ee.f ) );
|
||||
}
|
||||
|
||||
@Test void testQuery(SessionFactoryScope scope) {
|
||||
scope.inTransaction( s-> {
|
||||
G g = new G();
|
||||
|
@ -268,6 +348,7 @@ public class NewFetchTest {
|
|||
Long id;
|
||||
@ManyToOne(fetch = LAZY)
|
||||
F f;
|
||||
@NaturalId String s;
|
||||
}
|
||||
@Entity(name = "F")
|
||||
static class F {
|
||||
|
@ -284,6 +365,7 @@ public class NewFetchTest {
|
|||
@FetchProfileOverride(mode = SELECT, fetch = EAGER, profile = EAGER_SELECT_PROFILE)
|
||||
@FetchProfileOverride(mode = JOIN, profile = JOIN_PROFILE)
|
||||
Set<E> es;
|
||||
@NaturalId String s;
|
||||
}
|
||||
@Entity(name = "G")
|
||||
static class G {
|
||||
|
|
Loading…
Reference in New Issue