From b3791bc3c3f2f4e11426381fc19398b99746ae9f Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Wed, 3 Apr 2013 00:55:20 -0700 Subject: [PATCH] HHH-7841 : Redesign Loader --- .../loader/DefaultEntityAliases.java | 5 + .../org/hibernate/loader/EntityAliases.java | 5 + .../internal/AbstractEntityLoadQueryImpl.java | 114 +++++++ .../internal/AbstractLoadQueryImpl.java | 241 ++++++++++++++ .../internal/EntityLoadQueryBuilderImpl.java | 140 ++++++++ .../loader/internal/EntityLoadQueryImpl.java | 61 ++++ .../internal/JoinableAssociationImpl.java | 227 +++++++++++++ .../ResultSetProcessingContextImpl.java | 8 + .../plan/internal/EntityLoadQueryImpl.java | 66 ---- .../plan/internal/LoadPlanBuildingHelper.java | 3 +- .../loader/plan/internal/LoadPlanImpl.java | 15 +- ...ngleRootReturnLoadPlanBuilderStrategy.java | 20 +- .../loader/plan/spi/AbstractFetchOwner.java | 3 + .../spi/AbstractLoadPlanBuilderStrategy.java | 18 +- ...va => AbstractSingularAttributeFetch.java} | 4 +- .../loader/plan/spi/CollectionFetch.java | 3 +- .../loader/plan/spi/CollectionReturn.java | 2 +- .../plan/spi/CompositeElementGraph.java | 2 + .../loader/plan/spi/CompositeFetch.java | 4 +- .../loader/plan/spi/CompositeIndexGraph.java | 2 + .../loader/plan/spi/EntityElementGraph.java | 7 + .../loader/plan/spi/EntityFetch.java | 14 +- .../loader/plan/spi/EntityIndexGraph.java | 7 + .../loader/plan/spi/EntityReference.java | 12 +- .../loader/plan/spi/EntityReturn.java | 29 +- .../hibernate/loader/plan/spi/FetchOwner.java | 1 + .../hibernate/loader/plan/spi/LoadPlan.java | 4 - .../LoadQueryBuilder.java} | 4 +- .../spi/ResultSetProcessingContext.java | 3 + .../entity/AbstractEntityPersister.java | 7 + .../persister/entity/EntityPersister.java | 1 + .../AssociationResultSetProcessorTest.java | 312 ++++++++++++++++++ .../loader/SimpleResultSetProcessorTest.java | 23 +- .../loader/plan/spi/LoadPlanBuilderTest.java | 7 +- 34 files changed, 1233 insertions(+), 141 deletions(-) create mode 100755 hibernate-core/src/main/java/org/hibernate/loader/internal/AbstractEntityLoadQueryImpl.java create mode 100755 hibernate-core/src/main/java/org/hibernate/loader/internal/AbstractLoadQueryImpl.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/internal/EntityLoadQueryBuilderImpl.java create mode 100755 hibernate-core/src/main/java/org/hibernate/loader/internal/EntityLoadQueryImpl.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/internal/JoinableAssociationImpl.java delete mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/internal/EntityLoadQueryImpl.java rename hibernate-core/src/main/java/org/hibernate/loader/plan/spi/{AbstractFetch.java => AbstractSingularAttributeFetch.java} (94%) rename hibernate-core/src/main/java/org/hibernate/loader/{plan/spi/LoadQuery.java => spi/LoadQueryBuilder.java} (94%) create mode 100644 hibernate-core/src/test/java/org/hibernate/loader/AssociationResultSetProcessorTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/loader/DefaultEntityAliases.java b/hibernate-core/src/main/java/org/hibernate/loader/DefaultEntityAliases.java index 6602cfbffb..ec9fdf17a5 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/DefaultEntityAliases.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/DefaultEntityAliases.java @@ -188,6 +188,11 @@ public class DefaultEntityAliases implements EntityAliases { return rowIdAlias; } + @Override + public String getSuffix() { + return suffix; + } + private static void intern(String[] strings) { for (int i=0; i associations, + List suffixes) { + super( factory, associations, suffixes ); + this.entityReturn = entityReturn; + } + + protected final String generateSql( + final String whereString, + final String orderByString, + final LockOptions lockOptions) throws MappingException { + return generateSql( null, whereString, orderByString, "", lockOptions ); + } + + private String generateSql( + final String projection, + final String condition, + final String orderBy, + final String groupBy, + final LockOptions lockOptions) throws MappingException { + + JoinFragment ojf = mergeOuterJoins(); + + Select select = new Select( getDialect() ) + .setLockOptions( lockOptions ) + .setSelectClause( + projection == null ? + getPersister().selectFragment( getAlias(), entityReturn.getEntityAliases().getSuffix() ) + associationSelectString() : + projection + ) + .setFromClause( + getDialect().appendLockHint( lockOptions, getPersister().fromTableFragment( getAlias() ) ) + + getPersister().fromJoinFragment( getAlias(), true, true ) + ) + .setWhereClause( condition ) + .setOuterJoins( + ojf.toFromFragmentString(), + ojf.toWhereFragmentString() + getWhereFragment() + ) + .setOrderByClause( orderBy( orderBy ) ) + .setGroupByClause( groupBy ); + + if ( getFactory().getSettings().isCommentsEnabled() ) { + select.setComment( getComment() ); + } + return select.toStatementString(); + } + + protected String getWhereFragment() throws MappingException { + // here we do not bother with the discriminator. + return getPersister().whereJoinFragment( getAlias(), true, true ); + } + + public abstract String getComment(); + + public final OuterJoinLoadable getPersister() { + return (OuterJoinLoadable) entityReturn.getEntityPersister(); + } + + public final String getAlias() { + return entityReturn.getSqlTableAlias(); + } + + public String toString() { + return getClass().getName() + '(' + getPersister().getEntityName() + ')'; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/AbstractLoadQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/AbstractLoadQueryImpl.java new file mode 100755 index 0000000000..1ccd8ff2b8 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/AbstractLoadQueryImpl.java @@ -0,0 +1,241 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.loader.internal; +import java.util.Iterator; +import java.util.List; + +import org.hibernate.MappingException; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.persister.collection.QueryableCollection; +import org.hibernate.persister.entity.Joinable; +import org.hibernate.sql.ConditionFragment; +import org.hibernate.sql.DisjunctionFragment; +import org.hibernate.sql.InFragment; +import org.hibernate.sql.JoinFragment; +import org.hibernate.sql.JoinType; + +/** + * Walks the metamodel, searching for joins, and collecting + * together information needed by OuterJoinLoader. + * + * @see org.hibernate.loader.OuterJoinLoader + * @author Gavin King, Jon Lipsky + */ +public abstract class AbstractLoadQueryImpl { + + private final SessionFactoryImplementor factory; + private final List associations; + private final List suffixes; + + private String[] collectionSuffixes; + + protected AbstractLoadQueryImpl( + SessionFactoryImplementor factory, + List associations, + List suffixes) { + this.factory = factory; + this.associations = associations; + // TODO: we should be able to get the suffixes out of associations. + this.suffixes = suffixes; + } + + protected SessionFactoryImplementor getFactory() { + return factory; + } + + protected Dialect getDialect() { + return factory.getDialect(); + } + + protected String orderBy(final String orderBy) { + return mergeOrderings( orderBy( associations ), orderBy ); + } + + protected static String mergeOrderings(String ordering1, String ordering2) { + if ( ordering1.length() == 0 ) { + return ordering2; + } + else if ( ordering2.length() == 0 ) { + return ordering1; + } + else { + return ordering1 + ", " + ordering2; + } + } + + /** + * Generate a sequence of LEFT OUTER JOIN clauses for the given associations. + */ + protected final JoinFragment mergeOuterJoins() + throws MappingException { + JoinFragment outerjoin = getDialect().createOuterJoinFragment(); + JoinableAssociationImpl last = null; + for ( JoinableAssociationImpl oj : associations ) { + if ( last != null && last.isManyToManyWith( oj ) ) { + oj.addManyToManyJoin( outerjoin, ( QueryableCollection ) last.getJoinable() ); + } + else { + oj.addJoins(outerjoin); + } + last = oj; + } + return outerjoin; + } + + /** + * Count the number of instances of Joinable which are actually + * also instances of PersistentCollection which are being fetched + * by outer join + */ + protected static final int countCollectionPersisters(List associations) + throws MappingException { + int result = 0; + Iterator iter = associations.iterator(); + while ( iter.hasNext() ) { + JoinableAssociationImpl oj = (JoinableAssociationImpl) iter.next(); + if ( oj.getJoinType()==JoinType.LEFT_OUTER_JOIN && + oj.getJoinable().isCollection() && + ! oj.hasRestriction() ) { + result++; + } + } + return result; + } + + /** + * Get the order by string required for collection fetching + */ + protected static final String orderBy(List associations) + throws MappingException { + StringBuilder buf = new StringBuilder(); + JoinableAssociationImpl last = null; + for ( JoinableAssociationImpl oj : associations ) { + if ( oj.getJoinType() == JoinType.LEFT_OUTER_JOIN ) { // why does this matter? + if ( oj.getJoinable().isCollection() ) { + final QueryableCollection queryableCollection = (QueryableCollection) oj.getJoinable(); + if ( queryableCollection.hasOrdering() ) { + final String orderByString = queryableCollection.getSQLOrderByString( oj.getRHSAlias() ); + buf.append( orderByString ).append(", "); + } + } + else { + // it might still need to apply a collection ordering based on a + // many-to-many defined order-by... + if ( last != null && last.getJoinable().isCollection() ) { + final QueryableCollection queryableCollection = (QueryableCollection) last.getJoinable(); + if ( queryableCollection.isManyToMany() && last.isManyToManyWith( oj ) ) { + if ( queryableCollection.hasManyToManyOrdering() ) { + final String orderByString = queryableCollection.getManyToManyOrderByString( oj.getRHSAlias() ); + buf.append( orderByString ).append(", "); + } + } + } + } + } + last = oj; + } + if ( buf.length()>0 ) buf.setLength( buf.length()-2 ); + return buf.toString(); + } + + /** + * Render the where condition for a (batch) load by identifier / collection key + */ + protected StringBuilder whereString(String alias, String[] columnNames, int batchSize) { + if ( columnNames.length==1 ) { + // if not a composite key, use "foo in (?, ?, ?)" for batching + // if no batch, and not a composite key, use "foo = ?" + InFragment in = new InFragment().setColumn( alias, columnNames[0] ); + for ( int i=0; i= suffixes.size() ) + ? null + : suffixes.get( entityAliasCount ); + final String collectionSuffix = ( collectionSuffixes == null || collectionAliasCount >= collectionSuffixes.length ) + ? null + : collectionSuffixes[collectionAliasCount]; + final String selectFragment = joinable.selectFragment( + next == null ? null : next.getJoinable(), + next == null ? null : next.getRHSAlias(), + join.getRHSAlias(), + entitySuffix, + collectionSuffix, + join.getJoinType()==JoinType.LEFT_OUTER_JOIN + ); + if (selectFragment.trim().length() > 0) { + buf.append(", ").append(selectFragment); + } + if ( joinable.consumesEntityAlias() ) entityAliasCount++; + if ( joinable.consumesCollectionAlias() && join.getJoinType()==JoinType.LEFT_OUTER_JOIN ) collectionAliasCount++; + } + return buf.toString(); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityLoadQueryBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityLoadQueryBuilderImpl.java new file mode 100644 index 0000000000..e0d6e2246f --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityLoadQueryBuilderImpl.java @@ -0,0 +1,140 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.loader.internal; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.plan.spi.CollectionFetch; +import org.hibernate.loader.plan.spi.CompositeFetch; +import org.hibernate.loader.plan.spi.EntityFetch; +import org.hibernate.loader.plan.spi.EntityReturn; +import org.hibernate.loader.plan.spi.LoadPlan; +import org.hibernate.loader.plan.spi.LoadPlanVisitationStrategyAdapter; +import org.hibernate.loader.plan.spi.LoadPlanVisitor; +import org.hibernate.loader.spi.LoadQueryBuilder; +import org.hibernate.persister.entity.OuterJoinLoadable; + +/** + * @author Gail Badner + */ +public class EntityLoadQueryBuilderImpl implements LoadQueryBuilder { + private final SessionFactoryImplementor sessionFactory; + private final LoadQueryInfluencers loadQueryInfluencers; + private final LoadPlan loadPlan; + private final List associations; + private final List suffixes; + + public EntityLoadQueryBuilderImpl( + SessionFactoryImplementor sessionFactory, + LoadQueryInfluencers loadQueryInfluencers, + LoadPlan loadPlan) { + this.sessionFactory = sessionFactory; + this.loadQueryInfluencers = loadQueryInfluencers; + this.loadPlan = loadPlan; + LocalVisitationStrategy strategy = new LocalVisitationStrategy(); + LoadPlanVisitor.visit( loadPlan, strategy ); + this.associations = strategy.associations; + this.suffixes = strategy.suffixes; + } + + @Override + public String generateSql(int batchSize) { + return generateSql( batchSize, getOuterJoinLoadable().getKeyColumnNames() ); + } + + public String generateSql(int batchSize, String[] uniqueKey) { + final EntityLoadQueryImpl loadQuery = new EntityLoadQueryImpl( + sessionFactory, + getRootEntityReturn(), + associations, + suffixes + ); + return loadQuery.generateSql( uniqueKey, batchSize, getRootEntityReturn().getLockMode() ); + } + + private EntityReturn getRootEntityReturn() { + return (EntityReturn) loadPlan.getReturns().get( 0 ); + } + + private OuterJoinLoadable getOuterJoinLoadable() { + return (OuterJoinLoadable) getRootEntityReturn().getEntityPersister(); + } + private class LocalVisitationStrategy extends LoadPlanVisitationStrategyAdapter { + private final List associations = new ArrayList(); + private final List suffixes = new ArrayList(); + + private EntityReturn entityRootReturn; + + @Override + public void handleEntityReturn(EntityReturn rootEntityReturn) { + this.entityRootReturn = rootEntityReturn; + } + + @Override + public void startingEntityFetch(EntityFetch entityFetch) { + JoinableAssociationImpl assoc = new JoinableAssociationImpl( + entityFetch, + "", // getWithClause( entityFetch.getPropertyPath() ) + false, // hasRestriction( entityFetch.getPropertyPath() ) + sessionFactory, + loadQueryInfluencers.getEnabledFilters() + ); + associations.add( assoc ); + suffixes.add( entityFetch.getEntityAliases().getSuffix() ); + } + + @Override + public void finishingEntityFetch(EntityFetch entityFetch) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void startingCollectionFetch(CollectionFetch collectionFetch) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void finishingCollectionFetch(CollectionFetch collectionFetch) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void startingCompositeFetch(CompositeFetch fetch) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void finishingCompositeFetch(CompositeFetch fetch) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void finish(LoadPlan loadPlan) { + //suffixes.add( entityRootReturn.getEntityAliases().getSuffix() ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityLoadQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityLoadQueryImpl.java new file mode 100755 index 0000000000..a5fe1f22d4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityLoadQueryImpl.java @@ -0,0 +1,61 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.loader.internal; +import java.util.Collections; +import java.util.List; + +import org.hibernate.LockMode; +import org.hibernate.LockOptions; +import org.hibernate.MappingException; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.plan.spi.EntityReturn; + +/** + * A walker for loaders that fetch entities + * + * @see org.hibernate.loader.entity.EntityLoader + * @author Gavin King + */ +public class EntityLoadQueryImpl extends AbstractEntityLoadQueryImpl { + + public EntityLoadQueryImpl( + final SessionFactoryImplementor factory, + EntityReturn entityReturn, + List associations, + List suffixes) throws MappingException { + super( factory, entityReturn, associations, suffixes ); + } + + public String generateSql(String[] uniqueKey, int batchSize, LockMode lockMode) { + StringBuilder whereCondition = whereString( getAlias(), uniqueKey, batchSize ) + //include the discriminator and class-level where, but not filters + .append( getPersister().filterFragment( getAlias(), Collections.EMPTY_MAP ) ); + return generateSql( whereCondition.toString(), "", new LockOptions().setLockMode( lockMode ) ); + } + + public String getComment() { + return "load " + getPersister().getEntityName(); + } + +} \ No newline at end of file diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/JoinableAssociationImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/JoinableAssociationImpl.java new file mode 100644 index 0000000000..c853dba050 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/JoinableAssociationImpl.java @@ -0,0 +1,227 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.loader.internal; +import java.util.List; +import java.util.Map; + +import org.hibernate.MappingException; +import org.hibernate.cfg.NotYetImplementedException; +import org.hibernate.engine.FetchStyle; +import org.hibernate.engine.internal.JoinHelper; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.PropertyPath; +import org.hibernate.loader.plan.spi.EntityFetch; +import org.hibernate.loader.plan.spi.EntityReference; +import org.hibernate.persister.collection.QueryableCollection; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.Joinable; +import org.hibernate.persister.entity.OuterJoinLoadable; +import org.hibernate.sql.JoinFragment; +import org.hibernate.sql.JoinType; +import org.hibernate.type.AssociationType; +import org.hibernate.type.EntityType; + +/** + * Part of the Hibernate SQL rendering internals. This class represents + * a joinable association. + * + * @author Gavin King + * @author Gail Badner + */ +public final class JoinableAssociationImpl { + private final PropertyPath propertyPath; + private final AssociationType joinableType; + private final Joinable joinable; + private final String lhsAlias; // belong to other persister + private final String[] lhsColumns; // belong to other persister + private final String rhsAlias; + private final String[] rhsColumns; + private final JoinType joinType; + private final String on; + private final Map enabledFilters; + private final boolean hasRestriction; + + public JoinableAssociationImpl( + EntityFetch entityFetch, + String withClause, + boolean hasRestriction, + SessionFactoryImplementor factory, + Map enabledFilters) throws MappingException { + this.propertyPath = entityFetch.getPropertyPath(); + this.joinableType = entityFetch.getAssociationType(); + // TODO: this is not correct + final EntityPersister fetchSourcePersister = entityFetch.getOwner().retrieveFetchSourcePersister(); + final int propertyNumber = fetchSourcePersister.getEntityMetamodel().getPropertyIndex( entityFetch.getOwnerPropertyName() ); + + if ( EntityReference.class.isInstance( entityFetch.getOwner() ) ) { + this.lhsAlias = ( (EntityReference) entityFetch.getOwner() ).getSqlTableAlias(); + } + else { + throw new NotYetImplementedException( "Cannot determine LHS alias for a FetchOwner that is not an EntityReference." ); + } + final OuterJoinLoadable ownerPersister = (OuterJoinLoadable) entityFetch.getOwner().retrieveFetchSourcePersister(); + this.lhsColumns = JoinHelper.getAliasedLHSColumnNames( + entityFetch.getAssociationType(), lhsAlias, propertyNumber, ownerPersister, factory + ); + this.rhsAlias = entityFetch.getSqlTableAlias(); + + final boolean isNullable = ownerPersister.isSubclassPropertyNullable( propertyNumber ); + if ( entityFetch.getFetchStrategy().getStyle() == FetchStyle.JOIN ) { + joinType = isNullable ? JoinType.LEFT_OUTER_JOIN : JoinType.INNER_JOIN; + } + else { + joinType = JoinType.NONE; + } + this.joinable = joinableType.getAssociatedJoinable(factory); + this.rhsColumns = JoinHelper.getRHSColumnNames(joinableType, factory); + this.on = joinableType.getOnCondition( rhsAlias, factory, enabledFilters ) + + ( withClause == null || withClause.trim().length() == 0 ? "" : " and ( " + withClause + " )" ); + this.hasRestriction = hasRestriction; + this.enabledFilters = enabledFilters; // needed later for many-to-many/filter application + } + + public PropertyPath getPropertyPath() { + return propertyPath; + } + + public JoinType getJoinType() { + return joinType; + } + + public String getLhsAlias() { + return lhsAlias; + } + + public String getRHSAlias() { + return rhsAlias; + } + + private boolean isOneToOne() { + if ( joinableType.isEntityType() ) { + EntityType etype = (EntityType) joinableType; + return etype.isOneToOne() /*&& etype.isReferenceToPrimaryKey()*/; + } + else { + return false; + } + } + + public AssociationType getJoinableType() { + return joinableType; + } + + public String getRHSUniqueKeyName() { + return joinableType.getRHSUniqueKeyPropertyName(); + } + + public boolean isCollection() { + return joinableType.isCollectionType(); + } + + public Joinable getJoinable() { + return joinable; + } + + public boolean hasRestriction() { + return hasRestriction; + } + + public int getOwner(final List associations) { + if ( isOneToOne() || isCollection() ) { + return getPosition(lhsAlias, associations); + } + else { + return -1; + } + } + + /** + * Get the position of the join with the given alias in the + * list of joins + */ + private static int getPosition(String lhsAlias, List associations) { + int result = 0; + for ( int i=0; i getIdentifierResolutionContexts() { + return Collections.unmodifiableSet( + new HashSet( identifierResolutionContextMap.values() ) + ); + } + @Override public void checkVersion( ResultSet resultSet, diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/EntityLoadQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/EntityLoadQueryImpl.java deleted file mode 100644 index 77e89e5502..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/EntityLoadQueryImpl.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2013, Red Hat Inc. or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Inc. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - */ -package org.hibernate.loader.plan.internal; - -import org.hibernate.LockMode; -import org.hibernate.engine.spi.LoadQueryInfluencers; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.loader.entity.EntityJoinWalker; -import org.hibernate.loader.plan.spi.LoadQuery; -import org.hibernate.persister.entity.OuterJoinLoadable; - -/** - * @author Gail Badner - */ -public class EntityLoadQueryImpl implements LoadQuery { - final SessionFactoryImplementor sessionFactory; - final LoadQueryInfluencers loadQueryInfluencers; - final LockMode lockMode; - final OuterJoinLoadable entityPersister; - - public EntityLoadQueryImpl( - SessionFactoryImplementor sessionFactory, - LoadQueryInfluencers loadQueryInfluencers, - LockMode lockMode, - OuterJoinLoadable entityPersister) { - this.sessionFactory = sessionFactory; - this.loadQueryInfluencers = loadQueryInfluencers; - this.lockMode = lockMode; - this.entityPersister = entityPersister; - - } - - @Override - public String generateSql(int batchSize) { - final EntityJoinWalker entityJoinWalker = new EntityJoinWalker( - entityPersister, - entityPersister.getIdentifierColumnNames(), - batchSize, - lockMode, - sessionFactory, - loadQueryInfluencers - ); - return entityJoinWalker.getSQLString(); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanBuildingHelper.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanBuildingHelper.java index 3c617da984..35c8df1d63 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanBuildingHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanBuildingHelper.java @@ -64,6 +64,7 @@ public class LoadPlanBuildingHelper { FetchOwner fetchOwner, AssociationAttributeDefinition attributeDefinition, FetchStrategy fetchStrategy, + String sqlTableAlias, LoadPlanBuildingContext loadPlanBuildingContext) { return new EntityFetch( @@ -73,7 +74,7 @@ public class LoadPlanBuildingHelper { fetchOwner, attributeDefinition.getName(), fetchStrategy, - null, // sql table alias + sqlTableAlias, loadPlanBuildingContext.resolveEntityColumnAliases( attributeDefinition ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanImpl.java index 959f9223cf..47daeb3c13 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanImpl.java @@ -26,9 +26,7 @@ package org.hibernate.loader.plan.internal; import java.util.Collections; import java.util.List; -import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.loader.plan.spi.LoadPlan; -import org.hibernate.loader.plan.spi.LoadQuery; import org.hibernate.loader.plan.spi.Return; /** @@ -39,16 +37,14 @@ import org.hibernate.loader.plan.spi.Return; public class LoadPlanImpl implements LoadPlan { private final boolean hasScalars; private final List returns; - private final LoadQuery loadQuery; - public LoadPlanImpl(LoadQuery loadQuery, boolean hasScalars, List returns) { - this.loadQuery = loadQuery; + public LoadPlanImpl(boolean hasScalars, List returns) { this.hasScalars = hasScalars; this.returns = returns; } - public LoadPlanImpl(LoadQuery loadQuery, boolean hasScalars, Return rootReturn) { - this( loadQuery, hasScalars, Collections.singletonList( rootReturn ) ); + public LoadPlanImpl(boolean hasScalars, Return rootReturn) { + this( hasScalars, Collections.singletonList( rootReturn ) ); } @Override @@ -60,9 +56,4 @@ public class LoadPlanImpl implements LoadPlan { public List getReturns() { return returns; } - - @Override - public LoadQuery getLoadQuery() { - return loadQuery; - } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/SingleRootReturnLoadPlanBuilderStrategy.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/SingleRootReturnLoadPlanBuilderStrategy.java index 181be31b77..fd710aa7f6 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/SingleRootReturnLoadPlanBuilderStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/SingleRootReturnLoadPlanBuilderStrategy.java @@ -39,11 +39,9 @@ import org.hibernate.loader.plan.spi.CollectionReturn; import org.hibernate.loader.plan.spi.EntityReturn; import org.hibernate.loader.plan.spi.LoadPlan; import org.hibernate.loader.plan.spi.LoadPlanBuilderStrategy; -import org.hibernate.loader.plan.spi.LoadQuery; import org.hibernate.loader.plan.spi.Return; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.CollectionDefinition; import org.hibernate.persister.walking.spi.EntityDefinition; @@ -103,23 +101,7 @@ public class SingleRootReturnLoadPlanBuilderStrategy @Override public LoadPlan buildLoadPlan() { - return new LoadPlanImpl( createLoadQuery(), false, rootReturn ); - } - - private LoadQuery createLoadQuery() { - if ( EntityReturn.class.isInstance( rootReturn ) ) { - final EntityReturn entityReturn = (EntityReturn) rootReturn; - return new EntityLoadQueryImpl( - sessionFactory(), - loadQueryInfluencers, - entityReturn.getLockMode(), - (OuterJoinLoadable) entityReturn.getEntityPersister() - ); - } - else { - // TODO: create a LoadQuery for other types of returns. - return null; - } + return new LoadPlanImpl( false, rootReturn ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwner.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwner.java index d0dbc8197f..ce1d1aef82 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwner.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwner.java @@ -26,9 +26,12 @@ package org.hibernate.loader.plan.spi; import java.util.ArrayList; import java.util.List; +import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; import org.hibernate.LockMode; +import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.spi.ResultSetProcessingContext; /** * @author Steve Ebersole diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractLoadPlanBuilderStrategy.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractLoadPlanBuilderStrategy.java index 8c61e92a60..405b318dcc 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractLoadPlanBuilderStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractLoadPlanBuilderStrategy.java @@ -401,7 +401,12 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder associationFetch = fetchOwner.buildCollectionFetch( attributeDefinition, fetchStrategy, this ); } else { - associationFetch = fetchOwner.buildEntityFetch( attributeDefinition, fetchStrategy, this ); + associationFetch = fetchOwner.buildEntityFetch( + attributeDefinition, + fetchStrategy, + generateEntityFetchSqlTableAlias( attributeDefinition.toEntityDefinition().getEntityPersister().getEntityName() ), + this + ); } if ( FetchOwner.class.isInstance( associationFetch ) ) { @@ -477,6 +482,10 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder return sessionFactory(); } + protected String generateEntityFetchSqlTableAlias(String entityName) { + return StringHelper.generateAlias( StringHelper.unqualifyEntityName( entityName ), currentDepth() ); + } + @Override public EntityAliases resolveEntityColumnAliases(AssociationAttributeDefinition attributeDefinition) { return generateEntityColumnAliases( attributeDefinition.toEntityDefinition().getEntityPersister() ); @@ -539,6 +548,11 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder return entityReference.getAlias(); } + @Override + public String getSqlTableAlias() { + return entityReference.getSqlTableAlias(); + } + @Override public LockMode getLockMode() { return entityReference.getLockMode(); @@ -566,6 +580,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder public EntityFetch buildEntityFetch( AssociationAttributeDefinition attributeDefinition, FetchStrategy fetchStrategy, + String sqlTableAlias, LoadPlanBuildingContext loadPlanBuildingContext) { // we have a key-many-to-one // @@ -575,6 +590,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder this, attributeDefinition, fetchStrategy, + sqlTableAlias, loadPlanBuildingContext ); fetchToHydratedStateExtractorMap.put( fetch, attributeDefinition.getHydratedCompoundValueExtractor() ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractSingularAttributeFetch.java similarity index 94% rename from hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetch.java rename to hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractSingularAttributeFetch.java index bb9d9d0c18..d3a376dd09 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractSingularAttributeFetch.java @@ -33,14 +33,14 @@ import org.hibernate.loader.PropertyPath; /** * @author Steve Ebersole */ -public abstract class AbstractFetch extends AbstractFetchOwner implements Fetch { +public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner implements Fetch { private final FetchOwner owner; private final String ownerProperty; private final FetchStrategy fetchStrategy; private final PropertyPath propertyPath; - public AbstractFetch( + public AbstractSingularAttributeFetch( SessionFactoryImplementor factory, String alias, LockMode lockMode, diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java index ae79e5db80..89d82d4370 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java @@ -31,13 +31,12 @@ import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.CollectionAliases; import org.hibernate.loader.EntityAliases; -import org.hibernate.loader.PropertyPath; import org.hibernate.loader.spi.ResultSetProcessingContext; /** * @author Steve Ebersole */ -public class CollectionFetch extends AbstractCollectionReference implements CollectionReference, Fetch { +public class CollectionFetch extends AbstractCollectionReference implements Fetch { private final FetchOwner fetchOwner; private final FetchStrategy fetchStrategy; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionReturn.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionReturn.java index 5bb106c0af..13b27be1b1 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionReturn.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionReturn.java @@ -36,7 +36,7 @@ import org.hibernate.loader.spi.ResultSetProcessingContext; /** * @author Steve Ebersole */ -public class CollectionReturn extends AbstractCollectionReference implements Return, CollectionReference { +public class CollectionReturn extends AbstractCollectionReference implements Return { private final String ownerEntityName; private final String ownerProperty; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeElementGraph.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeElementGraph.java index c1c450793b..306b8eb83d 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeElementGraph.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeElementGraph.java @@ -72,11 +72,13 @@ public class CompositeElementGraph extends AbstractPlanNode implements FetchOwne public EntityFetch buildEntityFetch( AssociationAttributeDefinition attributeDefinition, FetchStrategy fetchStrategy, + String sqlTableAlias, LoadPlanBuildingContext loadPlanBuildingContext) { return LoadPlanBuildingHelper.buildStandardEntityFetch( this, attributeDefinition, fetchStrategy, + sqlTableAlias, loadPlanBuildingContext ); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java index 77eb3ebd2f..01a6140c81 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java @@ -39,7 +39,7 @@ import org.hibernate.persister.walking.spi.CompositionDefinition; /** * @author Steve Ebersole */ -public class CompositeFetch extends AbstractFetch implements Fetch { +public class CompositeFetch extends AbstractSingularAttributeFetch { public static final FetchStrategy FETCH_PLAN = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN ); public CompositeFetch( @@ -67,7 +67,7 @@ public class CompositeFetch extends AbstractFetch implements Fetch { public EntityFetch buildEntityFetch( AssociationAttributeDefinition attributeDefinition, FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { + String sqlTableAlias, LoadPlanBuildingContext loadPlanBuildingContext) { return null; //To change body of implemented methods use File | Settings | File Templates. } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeIndexGraph.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeIndexGraph.java index 8b482ac2e1..fce9033b72 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeIndexGraph.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeIndexGraph.java @@ -72,11 +72,13 @@ public class CompositeIndexGraph extends AbstractPlanNode implements FetchOwner public EntityFetch buildEntityFetch( AssociationAttributeDefinition attributeDefinition, FetchStrategy fetchStrategy, + String sqlTableAlias, LoadPlanBuildingContext loadPlanBuildingContext) { return LoadPlanBuildingHelper.buildStandardEntityFetch( this, attributeDefinition, fetchStrategy, + sqlTableAlias, loadPlanBuildingContext ); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityElementGraph.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityElementGraph.java index dbb2dc1129..7f55b638bf 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityElementGraph.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityElementGraph.java @@ -47,6 +47,11 @@ public class EntityElementGraph extends AbstractPlanNode implements FetchOwner, return null; } + @Override + public String getSqlTableAlias() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + @Override public LockMode getLockMode() { return null; @@ -111,11 +116,13 @@ public class EntityElementGraph extends AbstractPlanNode implements FetchOwner, public EntityFetch buildEntityFetch( AssociationAttributeDefinition attributeDefinition, FetchStrategy fetchStrategy, + String sqlTableAlias, LoadPlanBuildingContext loadPlanBuildingContext) { return LoadPlanBuildingHelper.buildStandardEntityFetch( this, attributeDefinition, fetchStrategy, + sqlTableAlias, loadPlanBuildingContext ); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java index 7b4ebd1436..54f7a61a7e 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java @@ -38,12 +38,13 @@ import org.hibernate.loader.spi.ResultSetProcessingContext; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.type.AssociationType; import org.hibernate.type.EntityType; /** * @author Steve Ebersole */ -public class EntityFetch extends AbstractFetch implements EntityReference, FetchOwner { +public class EntityFetch extends AbstractSingularAttributeFetch implements EntityReference { private final String sqlTableAlias; private final EntityAliases entityAliases; @@ -69,6 +70,10 @@ public class EntityFetch extends AbstractFetch implements EntityReference, Fetch this.persister = sessionFactory.getEntityPersister( associationType.getAssociatedEntityName() ); } + public EntityType getAssociationType() { + return associationType; + } + @Override public EntityPersister getEntityPersister() { return persister; @@ -79,6 +84,11 @@ public class EntityFetch extends AbstractFetch implements EntityReference, Fetch return identifierDescription; } + @Override + public String getSqlTableAlias() { + return sqlTableAlias; + } + @Override public EntityAliases getEntityAliases() { return entityAliases; @@ -107,11 +117,13 @@ public class EntityFetch extends AbstractFetch implements EntityReference, Fetch public EntityFetch buildEntityFetch( AssociationAttributeDefinition attributeDefinition, FetchStrategy fetchStrategy, + String sqlTableAlias, LoadPlanBuildingContext loadPlanBuildingContext) { return LoadPlanBuildingHelper.buildStandardEntityFetch( this, attributeDefinition, fetchStrategy, + sqlTableAlias, loadPlanBuildingContext ); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityIndexGraph.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityIndexGraph.java index 234efab221..806567396c 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityIndexGraph.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityIndexGraph.java @@ -69,6 +69,11 @@ public class EntityIndexGraph extends AbstractPlanNode implements FetchOwner, En return null; } + @Override + public String getSqlTableAlias() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + @Override public LockMode getLockMode() { return null; @@ -133,11 +138,13 @@ public class EntityIndexGraph extends AbstractPlanNode implements FetchOwner, En public EntityFetch buildEntityFetch( AssociationAttributeDefinition attributeDefinition, FetchStrategy fetchStrategy, + String sqlTableAlias, LoadPlanBuildingContext loadPlanBuildingContext) { return LoadPlanBuildingHelper.buildStandardEntityFetch( this, attributeDefinition, fetchStrategy, + sqlTableAlias, loadPlanBuildingContext ); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReference.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReference.java index a0ee50051e..da0f1067a0 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReference.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReference.java @@ -23,8 +23,11 @@ */ package org.hibernate.loader.plan.spi; +import org.hibernate.AssertionFailure; import org.hibernate.LockMode; +import org.hibernate.engine.spi.EntityKey; import org.hibernate.loader.EntityAliases; +import org.hibernate.loader.spi.ResultSetProcessingContext; import org.hibernate.persister.entity.EntityPersister; /** @@ -32,7 +35,7 @@ import org.hibernate.persister.entity.EntityPersister; * * @author Steve Ebersole */ -public interface EntityReference extends IdentifierDescriptionInjectable { +public interface EntityReference extends IdentifierDescriptionInjectable, ResultSetProcessingContext.EntityKeyResolutionContext { /** * Retrieve the alias associated with the persister (entity/collection). * @@ -40,6 +43,13 @@ public interface EntityReference extends IdentifierDescriptionInjectable { */ public String getAlias(); + /** + * Retrieve the SQL table alias. + * + * @return The SQL table alias + */ + public String getSqlTableAlias(); + /** * Retrieve the lock mode associated with this return. * diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java index 9666aa0158..682696a64c 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java @@ -44,9 +44,7 @@ import static org.hibernate.loader.spi.ResultSetProcessingContext.IdentifierReso /** * @author Steve Ebersole */ -public class EntityReturn - extends AbstractFetchOwner - implements Return, FetchOwner, EntityReference, ResultSetProcessingContext.EntityKeyResolutionContext { +public class EntityReturn extends AbstractFetchOwner implements Return, EntityReference { private final EntityAliases entityAliases; private final String sqlTableAlias; @@ -76,6 +74,11 @@ public class EntityReturn return super.getAlias(); } + @Override + public String getSqlTableAlias() { + return sqlTableAlias; + } + @Override public LockMode getLockMode() { return super.getLockMode(); @@ -127,11 +130,13 @@ public class EntityReturn public EntityFetch buildEntityFetch( AssociationAttributeDefinition attributeDefinition, FetchStrategy fetchStrategy, + String sqlTableAlias, LoadPlanBuildingContext loadPlanBuildingContext) { return LoadPlanBuildingHelper.buildStandardEntityFetch( this, attributeDefinition, fetchStrategy, + sqlTableAlias, loadPlanBuildingContext ); } @@ -176,13 +181,19 @@ public class EntityReturn @Override public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { - final IdentifierResolutionContext identifierResolutionContext = context.getIdentifierResolutionContext( this ); - EntityKey entityKey = identifierResolutionContext.getEntityKey(); - if ( entityKey == null ) { - throw new AssertionFailure( "Could not locate resolved EntityKey"); + Object objectForThisEntityReturn = null; + for ( IdentifierResolutionContext identifierResolutionContext : context.getIdentifierResolutionContexts() ) { + final EntityReference entityReference = identifierResolutionContext.getEntityReference(); + final EntityKey entityKey = identifierResolutionContext.getEntityKey(); + if ( entityKey == null ) { + throw new AssertionFailure( "Could not locate resolved EntityKey"); + } + final Object object = context.resolveEntityKey( entityKey, entityReference ); + if ( this == entityReference ) { + objectForThisEntityReturn = object; + } } - - return context.resolveEntityKey( entityKey, this ); + return objectForThisEntityReturn; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwner.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwner.java index a01e88ab2e..94958f221d 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwner.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwner.java @@ -84,6 +84,7 @@ public interface FetchOwner { public EntityFetch buildEntityFetch( AssociationAttributeDefinition attributeDefinition, FetchStrategy fetchStrategy, + String sqlTableAlias, LoadPlanBuildingContext loadPlanBuildingContext); public CompositeFetch buildCompositeFetch( diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlan.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlan.java index 2e999284c4..0173b39714 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlan.java @@ -25,8 +25,6 @@ package org.hibernate.loader.plan.spi; import java.util.List; -import org.hibernate.engine.spi.LoadQueryInfluencers; - /** * Describes a plan for performing a load of results. * @@ -57,8 +55,6 @@ public interface LoadPlan { public List getReturns(); - public LoadQuery getLoadQuery(); - // todo : would also like to see "call back" style access for handling "subsequent actions" such as: // 1) follow-on locking // 2) join fetch conversions to subselect fetches diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadQuery.java b/hibernate-core/src/main/java/org/hibernate/loader/spi/LoadQueryBuilder.java similarity index 94% rename from hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadQuery.java rename to hibernate-core/src/main/java/org/hibernate/loader/spi/LoadQueryBuilder.java index aa4c82038a..773f145ddb 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/spi/LoadQueryBuilder.java @@ -21,12 +21,12 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.loader.plan.spi; +package org.hibernate.loader.spi; /** * @author Gail Badner */ -public interface LoadQuery { +public interface LoadQueryBuilder { String generateSql(int batchSize); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/spi/ResultSetProcessingContext.java b/hibernate-core/src/main/java/org/hibernate/loader/spi/ResultSetProcessingContext.java index e1f0321cf5..2a6dce6f43 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/spi/ResultSetProcessingContext.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/spi/ResultSetProcessingContext.java @@ -25,6 +25,7 @@ package org.hibernate.loader.spi; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Set; import org.hibernate.LockMode; import org.hibernate.engine.spi.EntityKey; @@ -59,6 +60,8 @@ public interface ResultSetProcessingContext { public IdentifierResolutionContext getIdentifierResolutionContext(EntityReference entityReference); + public Set getIdentifierResolutionContexts(); + public void registerHydratedEntity(EntityPersister persister, EntityKey entityKey, Object entityInstance); public static interface EntityKeyResolutionContext { diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 5f4c423a2b..299b2626e3 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -96,6 +96,9 @@ import org.hibernate.loader.entity.BatchingEntityLoaderBuilder; import org.hibernate.loader.entity.CascadeEntityLoader; import org.hibernate.loader.entity.EntityLoader; import org.hibernate.loader.entity.UniqueEntityLoader; +import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy; +import org.hibernate.loader.plan.spi.LoadPlan; +import org.hibernate.loader.plan.spi.LoadPlanBuilder; import org.hibernate.mapping.Column; import org.hibernate.mapping.Component; import org.hibernate.mapping.PersistentClass; @@ -127,6 +130,10 @@ import org.hibernate.sql.SelectFragment; import org.hibernate.sql.SimpleSelect; import org.hibernate.sql.Template; import org.hibernate.sql.Update; +import org.hibernate.sql.ordering.antlr.ColumnMapper; +import org.hibernate.sql.ordering.antlr.ColumnReference; +import org.hibernate.sql.ordering.antlr.FormulaReference; +import org.hibernate.sql.ordering.antlr.SqlValueReference; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.tuple.entity.EntityTuplizer; import org.hibernate.type.AssociationType; diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java index 46d3fd8bee..d3ca57955e 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java @@ -45,6 +45,7 @@ import org.hibernate.id.IdentifierGenerator; import org.hibernate.internal.FilterAliasGenerator; import org.hibernate.metadata.ClassMetadata; import org.hibernate.persister.walking.spi.EntityDefinition; +import org.hibernate.sql.ordering.antlr.ColumnMapper; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.tuple.entity.EntityTuplizer; import org.hibernate.type.Type; diff --git a/hibernate-core/src/test/java/org/hibernate/loader/AssociationResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/AssociationResultSetProcessorTest.java new file mode 100644 index 0000000000..b41cf73572 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/loader/AssociationResultSetProcessorTest.java @@ -0,0 +1,312 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.loader; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; + +import org.junit.Test; + +import org.hibernate.Hibernate; +import org.hibernate.Session; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.engine.spi.QueryParameters; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.jdbc.Work; +import org.hibernate.loader.internal.EntityLoadQueryBuilderImpl; +import org.hibernate.loader.internal.ResultSetProcessorImpl; +import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy; +import org.hibernate.loader.plan.spi.LoadPlan; +import org.hibernate.loader.plan.spi.LoadPlanBuilder; +import org.hibernate.loader.spi.NamedParameterContext; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.junit4.ExtraAssertions; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * @author Steve Ebersole + */ +public class AssociationResultSetProcessorTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Message.class, Poster.class, ReportedMessage.class }; + } + + @Test + public void testManyToOneEntityProcessing() throws Exception { + final EntityPersister entityPersister = sessionFactory().getEntityPersister( Message.class.getName() ); + + // create some test data + Session session = openSession(); + session.beginTransaction(); + Message message = new Message( 1, "the message" ); + Poster poster = new Poster( 2, "the poster" ); + session.save( message ); + session.save( poster ); + message.poster = poster; + poster.messages.add( message ); + session.getTransaction().commit(); + session.close(); + + { + final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy( + sessionFactory(), + LoadQueryInfluencers.NONE, + "abc", + 0 + ); + final LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister ); + final EntityLoadQueryBuilderImpl queryBuilder = new EntityLoadQueryBuilderImpl( + sessionFactory(), + LoadQueryInfluencers.NONE, + plan + ); + final String sql = queryBuilder.generateSql( 1 ); + + final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan ); + final List results = new ArrayList(); + + final Session workSession = openSession(); + workSession.beginTransaction(); + workSession.doWork( + new Work() { + @Override + public void execute(Connection connection) throws SQLException { + PreparedStatement ps = connection.prepareStatement( sql ); + ps.setInt( 1, 1 ); + ResultSet resultSet = ps.executeQuery(); + results.addAll( + resultSetProcessor.extractResults( + resultSet, + (SessionImplementor) workSession, + new QueryParameters(), + new NamedParameterContext() { + @Override + public int[] getNamedParameterLocations(String name) { + return new int[0]; + } + }, + true, + false, + null, + null + ) + ); + resultSet.close(); + ps.close(); + } + } + ); + assertEquals( 1, results.size() ); + Object result = results.get( 0 ); + assertNotNull( result ); + + Message workMessage = ExtraAssertions.assertTyping( Message.class, result ); + assertEquals( 1, workMessage.mid.intValue() ); + assertEquals( "the message", workMessage.msgTxt ); + assertTrue( Hibernate.isInitialized( workMessage.poster ) ); + Poster workPoster = workMessage.poster; + assertEquals( 2, workPoster.pid.intValue() ); + assertEquals( "the poster", workPoster.name ); + assertFalse( Hibernate.isInitialized( workPoster.messages ) ); + + workSession.getTransaction().commit(); + workSession.close(); + } + + // clean up test data + session = openSession(); + session.beginTransaction(); + session.createQuery( "delete Message" ).executeUpdate(); + session.createQuery( "delete Poster" ).executeUpdate(); + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testNestedManyToOneEntityProcessing() throws Exception { + final EntityPersister entityPersister = sessionFactory().getEntityPersister( ReportedMessage.class.getName() ); + + // create some test data + Session session = openSession(); + session.beginTransaction(); + Message message = new Message( 1, "the message" ); + Poster poster = new Poster( 2, "the poster" ); + session.save( message ); + session.save( poster ); + message.poster = poster; + poster.messages.add( message ); + ReportedMessage reportedMessage = new ReportedMessage( 0, "inappropriate", message ); + session.save( reportedMessage ); + session.getTransaction().commit(); + session.close(); + + { + final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy( + sessionFactory(), + LoadQueryInfluencers.NONE, + "abc", + 0 + ); + final LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister ); + final EntityLoadQueryBuilderImpl queryBuilder = new EntityLoadQueryBuilderImpl( + sessionFactory(), + LoadQueryInfluencers.NONE, + plan + ); + final String sql = queryBuilder.generateSql( 1 ); + + final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan ); + final List results = new ArrayList(); + + final Session workSession = openSession(); + workSession.beginTransaction(); + workSession.doWork( + new Work() { + @Override + public void execute(Connection connection) throws SQLException { + PreparedStatement ps = connection.prepareStatement( sql ); + ps.setInt( 1, 0 ); + ResultSet resultSet = ps.executeQuery(); + results.addAll( + resultSetProcessor.extractResults( + resultSet, + (SessionImplementor) workSession, + new QueryParameters(), + new NamedParameterContext() { + @Override + public int[] getNamedParameterLocations(String name) { + return new int[0]; + } + }, + true, + false, + null, + null + ) + ); + resultSet.close(); + ps.close(); + } + } + ); + assertEquals( 1, results.size() ); + Object result = results.get( 0 ); + assertNotNull( result ); + + ReportedMessage workReportedMessage = ExtraAssertions.assertTyping( ReportedMessage.class, result ); + assertEquals( 0, workReportedMessage.id.intValue() ); + assertEquals( "inappropriate", workReportedMessage.reason ); + Message workMessage = workReportedMessage.message; + assertNotNull( workMessage ); + assertTrue( Hibernate.isInitialized( workMessage ) ); + assertEquals( 1, workMessage.mid.intValue() ); + assertEquals( "the message", workMessage.msgTxt ); + assertTrue( Hibernate.isInitialized( workMessage.poster ) ); + Poster workPoster = workMessage.poster; + assertEquals( 2, workPoster.pid.intValue() ); + assertEquals( "the poster", workPoster.name ); + assertFalse( Hibernate.isInitialized( workPoster.messages ) ); + + workSession.getTransaction().commit(); + workSession.close(); + } + + // clean up test data + session = openSession(); + session.beginTransaction(); + session.createQuery( "delete ReportedMessage" ).executeUpdate(); + session.createQuery( "delete Message" ).executeUpdate(); + session.createQuery( "delete Poster" ).executeUpdate(); + session.getTransaction().commit(); + session.close(); + } + + @Entity( name = "ReportedMessage" ) + public static class ReportedMessage { + @Id + private Integer id; + private String reason; + @ManyToOne + @JoinColumn + private Message message; + + public ReportedMessage() {} + + public ReportedMessage(Integer id, String reason, Message message) { + this.id = id; + this.reason = reason; + this.message = message; + } + } + + @Entity( name = "Message" ) + public static class Message { + @Id + private Integer mid; + private String msgTxt; + @ManyToOne( cascade = CascadeType.MERGE ) + @JoinColumn + private Poster poster; + + public Message() {} + + public Message(Integer mid, String msgTxt) { + this.mid = mid; + this.msgTxt = msgTxt; + } + } + + @Entity( name = "Poster" ) + public static class Poster { + @Id + private Integer pid; + private String name; + @OneToMany(mappedBy = "poster") + private List messages = new ArrayList(); + + public Poster() {} + + public Poster(Integer pid, String name) { + this.pid = pid; + this.name = name; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/loader/SimpleResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/SimpleResultSetProcessorTest.java index 1674f43f54..b420e0ec29 100644 --- a/hibernate-core/src/test/java/org/hibernate/loader/SimpleResultSetProcessorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/loader/SimpleResultSetProcessorTest.java @@ -31,26 +31,20 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import org.hibernate.LockMode; import org.hibernate.Session; -import org.hibernate.engine.jdbc.spi.StatementPreparer; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.jdbc.Work; -import org.hibernate.loader.entity.BatchingEntityLoaderBuilder; -import org.hibernate.loader.entity.EntityLoader; +import org.hibernate.loader.internal.EntityLoadQueryBuilderImpl; import org.hibernate.loader.internal.ResultSetProcessorImpl; -import org.hibernate.loader.plan.internal.EntityLoadQueryImpl; import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy; import org.hibernate.loader.plan.spi.LoadPlan; import org.hibernate.loader.plan.spi.LoadPlanBuilder; import org.hibernate.loader.spi.NamedParameterContext; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.persister.entity.OuterJoinLoadable; import org.junit.Test; @@ -82,14 +76,6 @@ public class SimpleResultSetProcessorTest extends BaseCoreFunctionalTestCase { session.close(); { - final EntityLoadQueryImpl queryBuilder = new EntityLoadQueryImpl( - sessionFactory(), - LoadQueryInfluencers.NONE, - LockMode.NONE, - (OuterJoinLoadable) entityPersister - ); - final String sql = queryBuilder.generateSql( 1 ); - final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy( sessionFactory(), LoadQueryInfluencers.NONE, @@ -97,6 +83,13 @@ public class SimpleResultSetProcessorTest extends BaseCoreFunctionalTestCase { 0 ); final LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister ); + final EntityLoadQueryBuilderImpl queryBuilder = new EntityLoadQueryBuilderImpl( + sessionFactory(), + LoadQueryInfluencers.NONE, + plan + ); + final String sql = queryBuilder.generateSql( 1 ); + final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan ); final List results = new ArrayList(); diff --git a/hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanBuilderTest.java b/hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanBuilderTest.java index f26afd3dea..43d51cb3f4 100644 --- a/hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanBuilderTest.java +++ b/hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanBuilderTest.java @@ -33,8 +33,10 @@ import java.util.List; import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.loader.internal.EntityLoadQueryBuilderImpl; import org.hibernate.loader.plan.internal.CascadeLoadPlanBuilderStrategy; import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy; +import org.hibernate.loader.spi.LoadQueryBuilder; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; @@ -78,9 +80,8 @@ public class LoadPlanBuilderTest extends BaseCoreFunctionalTestCase { assertNotNull( entityFetch.getFetches() ); assertEquals( 0, entityFetch.getFetches().length ); - String loadSql = plan.getLoadQuery().generateSql( 1 ); - // TODO: assert that aliases used in loadSql match up with those in entityReturn and entityFetch - // (they currently do not match up) + LoadQueryBuilder loadQueryBuilder = new EntityLoadQueryBuilderImpl( sessionFactory(), LoadQueryInfluencers.NONE, plan ); + String sql = loadQueryBuilder.generateSql( 1 ); } @Test