/* Hibernate, Relational Persistence for Idiomatic Java * * SPDX-License-Identifier: LGPL-2.1-or-later * Copyright: Red Hat Inc. and Hibernate Authors */ package org.hibernate.reactive.loader.collection.impl; import org.hibernate.HibernateException; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.internal.util.StringHelper; import org.hibernate.loader.JoinWalker; import org.hibernate.loader.collection.BasicCollectionJoinWalker; import org.hibernate.loader.collection.OneToManyJoinWalker; import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.reactive.util.impl.CompletionStages; import org.hibernate.type.Type; import java.io.Serializable; import java.util.Arrays; import java.util.concurrent.CompletionStage; import static org.hibernate.internal.util.StringHelper.buildBatchFetchRestrictionFragment; import static org.hibernate.pretty.MessageHelper.collectionInfoString; /** * A {@link ReactiveCollectionLoader} whose generated SQL contains a placeholder * that is interpolated with a batch of ids at runtime. * * Used when for {@link org.hibernate.loader.BatchFetchStyle#DYNAMIC} is selected. * * @see org.hibernate.loader.collection.DynamicBatchingCollectionInitializerBuilder.DynamicBatchingCollectionLoader */ class ReactiveDynamicBatchingCollectionInitializer extends ReactiveCollectionLoader { private final String sqlTemplate; private final String alias; public ReactiveDynamicBatchingCollectionInitializer( QueryableCollection collectionPersister, SessionFactoryImplementor factory, LoadQueryInfluencers influencers) { super( collectionPersister, factory, influencers ); JoinWalker walker = buildJoinWalker( collectionPersister, factory, influencers ); initFromWalker( walker ); this.sqlTemplate = walker.getSQLString(); this.alias = StringHelper.generateAlias( collectionPersister.getRole(), 0 ); postInstantiate(); if ( LOG.isDebugEnabled() ) { LOG.debugf( "SQL-template for dynamic collection [%s] batch-fetching : %s", collectionPersister.getRole(), sqlTemplate ); } } private JoinWalker buildJoinWalker( QueryableCollection collectionPersister, SessionFactoryImplementor factory, LoadQueryInfluencers influencers) { if ( collectionPersister.isOneToMany() ) { return new OneToManyJoinWalker( collectionPersister, -1, null, factory, influencers ) { @Override protected StringBuilder whereString(String alias, String[] columnNames, String subselect, int batchSize) { if ( subselect != null ) { return super.whereString( alias, columnNames, subselect, batchSize ); } return buildBatchFetchRestrictionFragment( alias, columnNames, getFactory().getDialect() ); } }; } else { return new BasicCollectionJoinWalker( collectionPersister, -1, null, factory, influencers ) { @Override protected StringBuilder whereString(String alias, String[] columnNames, String subselect, int batchSize) { if ( subselect != null ) { return super.whereString( alias, columnNames, subselect, batchSize ); } return buildBatchFetchRestrictionFragment( alias, columnNames, getFactory().getDialect() ); } }; } } public final CompletionStage<Void> doBatchedCollectionLoad( final SessionImplementor session, final Serializable[] ids, final Type type) throws HibernateException { if ( LOG.isDebugEnabled() ) { LOG.debugf( "Batch loading collection: %s", collectionInfoString( getCollectionPersisters()[0], ids, getFactory() ) ); } final Type[] idTypes = new Type[ids.length]; Arrays.fill( idTypes, type ); final QueryParameters queryParameters = new QueryParameters( idTypes, ids, ids ); final String sql = StringHelper.expandBatchIdPlaceholder( sqlTemplate, ids, alias, collectionPersister().getKeyColumnNames(), session.getJdbcServices().getJdbcEnvironment().getDialect() ); return doReactiveQueryAndInitializeNonLazyCollections( sql, session, queryParameters ) .handle( (list, err) -> { CompletionStages.logSqlException( err, () -> "could not initialize a collection batch: " + collectionInfoString( getCollectionPersisters()[0], ids, getFactory() ), getSQLString() ); LOG.debug("Done batch load"); return CompletionStages.returnNullorRethrow( err ); } ); } }