diff --git a/hibernate-core/src/main/java/org/hibernate/HibernateError.java b/hibernate-core/src/main/java/org/hibernate/HibernateError.java new file mode 100644 index 0000000000..4376031c02 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/HibernateError.java @@ -0,0 +1,43 @@ +/* + * 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; + +/** + * Marks a group of exceptions that generally indicate an internal Hibernate error or bug. + * + * @author Steve Ebersole + */ +public abstract class HibernateError extends HibernateException { + public HibernateError(String message) { + super( message ); + } + + public HibernateError(Throwable root) { + super( root ); + } + + public HibernateError(String message, Throwable root) { + super( message, root ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/classic/QueryTranslatorImpl.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/classic/QueryTranslatorImpl.java index deca43ace7..401b2fe4c6 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/classic/QueryTranslatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/classic/QueryTranslatorImpl.java @@ -63,6 +63,7 @@ import org.hibernate.internal.util.ReflectHelper; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.loader.BasicLoader; +import org.hibernate.loader.spi.AfterLoadAction; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.persister.entity.Loadable; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/Loader.java b/hibernate-core/src/main/java/org/hibernate/loader/Loader.java index 2c80db36c2..2ad4cf8479 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/Loader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/Loader.java @@ -79,6 +79,7 @@ import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.FetchingScrollableResultsImpl; import org.hibernate.internal.ScrollableResultsImpl; import org.hibernate.internal.util.StringHelper; +import org.hibernate.loader.spi.AfterLoadAction; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Loadable; @@ -245,10 +246,6 @@ public abstract class Loader { : sql; } - protected static interface AfterLoadAction { - public void afterLoad(SessionImplementor session, Object entity, Loadable persister); - } - protected boolean shouldUseFollowOnLocking( QueryParameters parameters, Dialect dialect, @@ -509,7 +506,7 @@ public abstract class Loader { } // We call getKeyFromResultSet() here so that we can know the - // key value upon which to doAfterTransactionCompletion the breaking logic. However, + // key value upon which to perform the breaking logic. However, // it is also then called from getRowFromResultSet() which is certainly // not the most efficient. But the call here is needed, and there // currently is no other way without refactoring of the doQuery()/getRowFromResultSet() @@ -527,7 +524,7 @@ public abstract class Loader { catch ( SQLException sqle ) { throw factory.getSQLExceptionHelper().convert( sqle, - "could not doAfterTransactionCompletion sequential read of results (forward)", + "could not perform sequential read of results (forward)", getSQLString() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/collection/DynamicBatchingCollectionInitializerBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/collection/DynamicBatchingCollectionInitializerBuilder.java index 62e82e78f6..8f470ca1e1 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/collection/DynamicBatchingCollectionInitializerBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/collection/DynamicBatchingCollectionInitializerBuilder.java @@ -43,6 +43,7 @@ import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.loader.JoinWalker; import org.hibernate.loader.Loader; +import org.hibernate.loader.spi.AfterLoadAction; import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.pretty.MessageHelper; import org.hibernate.type.Type; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java index e1caffb218..0b7093bc0e 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java @@ -45,6 +45,7 @@ import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.internal.CriteriaImpl; import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.loader.OuterJoinLoader; +import org.hibernate.loader.spi.AfterLoadAction; import org.hibernate.persister.entity.Loadable; import org.hibernate.persister.entity.Lockable; import org.hibernate.persister.entity.OuterJoinLoadable; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java index 239451eea5..b626e375ec 100755 --- a/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java @@ -27,7 +27,6 @@ import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -41,10 +40,7 @@ import org.hibernate.QueryException; import org.hibernate.ScrollableResults; import org.hibernate.Session; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.pagination.LimitHandler; -import org.hibernate.dialect.pagination.LimitHelper; import org.hibernate.engine.spi.QueryParameters; -import org.hibernate.engine.spi.RowSelection; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.hql.internal.HolderInstantiator; @@ -53,10 +49,10 @@ import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.loader.CollectionAliases; import org.hibernate.loader.EntityAliases; import org.hibernate.loader.Loader; +import org.hibernate.loader.spi.AfterLoadAction; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.persister.entity.Loadable; -import org.hibernate.persister.entity.Lockable; import org.hibernate.persister.entity.Queryable; import org.hibernate.transform.ResultTransformer; import org.hibernate.type.CollectionType; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/entity/DynamicBatchingEntityLoaderBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/entity/DynamicBatchingEntityLoaderBuilder.java index af1fcc26ea..c1be46e16c 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/entity/DynamicBatchingEntityLoaderBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/entity/DynamicBatchingEntityLoaderBuilder.java @@ -43,6 +43,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.ArrayHelper; +import org.hibernate.loader.spi.AfterLoadAction; import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.pretty.MessageHelper; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java index c50eed3458..3a45e39d53 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java @@ -52,6 +52,7 @@ import org.hibernate.hql.internal.ast.tree.SelectClause; import org.hibernate.internal.IteratorImpl; import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.loader.BasicLoader; +import org.hibernate.loader.spi.AfterLoadAction; import org.hibernate.param.ParameterSpecification; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.QueryableCollection; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/OnDemandResultSetProcessorImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/OnDemandResultSetProcessorImpl.java new file mode 100644 index 0000000000..8fc816c9e6 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/OnDemandResultSetProcessorImpl.java @@ -0,0 +1,58 @@ +/* + * 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.sql.ResultSet; + +import org.hibernate.engine.spi.QueryParameters; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.loader.spi.OnDemandResultSetProcessor; + +/** + * @author Steve Ebersole + */ +public class OnDemandResultSetProcessorImpl implements OnDemandResultSetProcessor { + @Override + public Object extractSingleRow( + ResultSet resultSet, + SessionImplementor session, + QueryParameters queryParameters) { + return null; + } + + @Override + public Object extractSequentialRowsForward( + ResultSet resultSet, SessionImplementor session, QueryParameters queryParameters) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public Object extractSequentialRowsReverse( + ResultSet resultSet, + SessionImplementor session, + QueryParameters queryParameters, + boolean isLogicallyAfterLast) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/ResultSetProcessingContextImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/ResultSetProcessingContextImpl.java new file mode 100644 index 0000000000..2e390c980b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/ResultSetProcessingContextImpl.java @@ -0,0 +1,557 @@ +/* + * 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.io.Serializable; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.jboss.logging.Logger; + +import org.hibernate.HibernateException; +import org.hibernate.LockMode; +import org.hibernate.StaleObjectStateException; +import org.hibernate.WrongClassException; +import org.hibernate.engine.internal.TwoPhaseLoad; +import org.hibernate.engine.spi.EntityKey; +import org.hibernate.engine.spi.EntityUniqueKey; +import org.hibernate.engine.spi.QueryParameters; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SubselectFetch; +import org.hibernate.event.spi.EventSource; +import org.hibernate.event.spi.PostLoadEvent; +import org.hibernate.event.spi.PreLoadEvent; +import org.hibernate.loader.EntityAliases; +import org.hibernate.loader.plan.spi.CollectionFetch; +import org.hibernate.loader.plan.spi.CollectionReturn; +import org.hibernate.loader.plan.spi.EntityReference; +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.AfterLoadAction; +import org.hibernate.loader.spi.NamedParameterContext; +import org.hibernate.loader.spi.ResultSetProcessingContext; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.Loadable; +import org.hibernate.persister.entity.UniqueKeyLoadable; +import org.hibernate.pretty.MessageHelper; +import org.hibernate.type.EntityType; +import org.hibernate.type.Type; +import org.hibernate.type.VersionType; + +/** + * @author Steve Ebersole + */ +public class ResultSetProcessingContextImpl implements ResultSetProcessingContext { + private static final Logger LOG = Logger.getLogger( ResultSetProcessingContextImpl.class ); + + private final ResultSet resultSet; + private final SessionImplementor session; + private final LoadPlan loadPlan; + private final boolean readOnly; + private final QueryParameters queryParameters; + private final NamedParameterContext namedParameterContext; + private final boolean hadSubselectFetches; + + private final EntityKey dictatedRootEntityKey; + + private List currentRowHydratedEntityRegistrationList; + + private Map> subselectLoadableEntityKeyMap; + private List hydratedEntityRegistrationList; + + public ResultSetProcessingContextImpl( + ResultSet resultSet, + SessionImplementor session, + LoadPlan loadPlan, + boolean readOnly, + boolean useOptionalEntityKey, + QueryParameters queryParameters, + NamedParameterContext namedParameterContext, + boolean hadSubselectFetches) { + this.resultSet = resultSet; + this.session = session; + this.loadPlan = loadPlan; + this.readOnly = readOnly; + this.queryParameters = queryParameters; + this.namedParameterContext = namedParameterContext; + this.hadSubselectFetches = hadSubselectFetches; + + if ( useOptionalEntityKey ) { + this.dictatedRootEntityKey = ResultSetProcessorHelper.getOptionalObjectKey( queryParameters, session ); + if ( this.dictatedRootEntityKey == null ) { + throw new HibernateException( "Unable to resolve optional entity-key" ); + } + } + else { + this.dictatedRootEntityKey = null; + } + } + + @Override + public SessionImplementor getSession() { + return session; + } + + @Override + public QueryParameters getQueryParameters() { + return queryParameters; + } + + @Override + public EntityKey getDictatedRootEntityKey() { + return dictatedRootEntityKey; + } + + private Map identifierResolutionContextMap; + + @Override + public IdentifierResolutionContext getIdentifierResolutionContext(final EntityReference entityReference) { + if ( identifierResolutionContextMap == null ) { + identifierResolutionContextMap = new HashMap(); + } + IdentifierResolutionContext context = identifierResolutionContextMap.get( entityReference ); + if ( context == null ) { + context = new IdentifierResolutionContext() { + private Serializable hydratedForm; + private EntityKey entityKey; + + @Override + public EntityReference getEntityReference() { + return entityReference; + } + + @Override + public void registerHydratedForm(Serializable hydratedForm) { + if ( this.hydratedForm != null ) { + // this could be bad... + } + this.hydratedForm = hydratedForm; + } + + @Override + public Serializable getHydratedForm() { + return hydratedForm; + } + + @Override + public void registerEntityKey(EntityKey entityKey) { + if ( this.entityKey != null ) { + // again, could be trouble... + } + this.entityKey = entityKey; + } + + @Override + public EntityKey getEntityKey() { + return entityKey; + } + }; + identifierResolutionContextMap.put( entityReference, context ); + } + + return context; + } + + @Override + public void checkVersion( + ResultSet resultSet, + EntityPersister persister, + EntityAliases entityAliases, + EntityKey entityKey, + Object entityInstance) throws SQLException { + final Object version = session.getPersistenceContext().getEntry( entityInstance ).getVersion(); + + if ( version != null ) { + //null version means the object is in the process of being loaded somewhere else in the ResultSet + VersionType versionType = persister.getVersionType(); + Object currentVersion = versionType.nullSafeGet( + resultSet, + entityAliases.getSuffixedVersionAliases(), + session, + null + ); + if ( !versionType.isEqual(version, currentVersion) ) { + if ( session.getFactory().getStatistics().isStatisticsEnabled() ) { + session.getFactory().getStatisticsImplementor() + .optimisticFailure( persister.getEntityName() ); + } + throw new StaleObjectStateException( persister.getEntityName(), entityKey.getIdentifier() ); + } + } + } + + @Override + public String getConcreteEntityTypeName( + final ResultSet rs, + final EntityPersister persister, + final EntityAliases entityAliases, + final EntityKey entityKey) throws SQLException { + + final Loadable loadable = (Loadable) persister; + if ( ! loadable.hasSubclasses() ) { + return persister.getEntityName(); + } + + final Object discriminatorValue = loadable.getDiscriminatorType().nullSafeGet( + rs, + entityAliases.getSuffixedDiscriminatorAlias(), + session, + null + ); + + final String result = loadable.getSubclassForDiscriminatorValue( discriminatorValue ); + + if ( result == null ) { + // whoops! we got an instance of another class hierarchy branch + throw new WrongClassException( + "Discriminator: " + discriminatorValue, + entityKey.getIdentifier(), + persister.getEntityName() + ); + } + + return result; + } + + @Override + public void loadFromResultSet( + ResultSet resultSet, + Object entityInstance, + String concreteEntityTypeName, + EntityKey entityKey, + EntityAliases entityAliases, + LockMode acquiredLockMode, + EntityPersister rootPersister, + boolean eagerFetch, + EntityType associationType) throws SQLException { + + final Serializable id = entityKey.getIdentifier(); + + // Get the persister for the _subclass_ + final Loadable persister = (Loadable) getSession().getFactory().getEntityPersister( concreteEntityTypeName ); + + if ( LOG.isTraceEnabled() ) { + LOG.tracev( + "Initializing object from ResultSet: {0}", + MessageHelper.infoString( + persister, + id, + getSession().getFactory() + ) + ); + } + + // add temp entry so that the next step is circular-reference + // safe - only needed because some types don't take proper + // advantage of two-phase-load (esp. components) + TwoPhaseLoad.addUninitializedEntity( + entityKey, + entityInstance, + persister, + acquiredLockMode, + !eagerFetch, + session + ); + + // This is not very nice (and quite slow): + final String[][] cols = persister == rootPersister ? + entityAliases.getSuffixedPropertyAliases() : + entityAliases.getSuffixedPropertyAliases(persister); + + final Object[] values = persister.hydrate( + resultSet, + id, + entityInstance, + (Loadable) rootPersister, + cols, + eagerFetch, + session + ); + + final Object rowId = persister.hasRowId() ? resultSet.getObject( entityAliases.getRowIdAlias() ) : null; + + if ( associationType != null ) { + String ukName = associationType.getRHSUniqueKeyPropertyName(); + if ( ukName != null ) { + final int index = ( (UniqueKeyLoadable) persister ).getPropertyIndex( ukName ); + final Type type = persister.getPropertyTypes()[index]; + + // polymorphism not really handled completely correctly, + // perhaps...well, actually its ok, assuming that the + // entity name used in the lookup is the same as the + // the one used here, which it will be + + EntityUniqueKey euk = new EntityUniqueKey( + rootPersister.getEntityName(), //polymorphism comment above + ukName, + type.semiResolve( values[index], session, entityInstance ), + type, + persister.getEntityMode(), + session.getFactory() + ); + session.getPersistenceContext().addEntity( euk, entityInstance ); + } + } + + TwoPhaseLoad.postHydrate( + persister, + id, + values, + rowId, + entityInstance, + acquiredLockMode, + !eagerFetch, + session + ); + + } + + @Override + public void registerHydratedEntity(EntityPersister persister, EntityKey entityKey, Object entityInstance) { + if ( currentRowHydratedEntityRegistrationList == null ) { + currentRowHydratedEntityRegistrationList = new ArrayList(); + } + currentRowHydratedEntityRegistrationList.add( new HydratedEntityRegistration( persister, entityKey, entityInstance ) ); + } + + /** + * Package-protected + */ + void finishUpRow() { + if ( currentRowHydratedEntityRegistrationList == null ) { + return; + } + + + // managing the running list of registrations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + if ( hydratedEntityRegistrationList == null ) { + hydratedEntityRegistrationList = new ArrayList(); + } + hydratedEntityRegistrationList.addAll( currentRowHydratedEntityRegistrationList ); + + + // managing the map forms needed for subselect fetch generation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + if ( ! hadSubselectFetches ) { + return; + } + if ( subselectLoadableEntityKeyMap == null ) { + subselectLoadableEntityKeyMap = new HashMap>(); + } + for ( HydratedEntityRegistration registration : currentRowHydratedEntityRegistrationList ) { + Set entityKeys = subselectLoadableEntityKeyMap.get( registration.persister ); + if ( entityKeys == null ) { + entityKeys = new HashSet(); + subselectLoadableEntityKeyMap.put( registration.persister, entityKeys ); + } + entityKeys.add( registration.key ); + } + + // release the currentRowHydratedEntityRegistrationList entries + currentRowHydratedEntityRegistrationList.clear(); + } + + /** + * Package-protected + * + * @param afterLoadActionList List of after-load actions to perform + */ + void finishUp(List afterLoadActionList) { + initializeEntitiesAndCollections( afterLoadActionList ); + createSubselects(); + + if ( hydratedEntityRegistrationList != null ) { + hydratedEntityRegistrationList.clear(); + hydratedEntityRegistrationList = null; + } + + if ( subselectLoadableEntityKeyMap != null ) { + subselectLoadableEntityKeyMap.clear(); + subselectLoadableEntityKeyMap = null; + } + } + + private void initializeEntitiesAndCollections(List afterLoadActionList) { + // for arrays, we should end the collection load before resolving the entities, since the + // actual array instances are not instantiated during loading + finishLoadingArrays(); + + + // IMPORTANT: reuse the same event instances for performance! + final PreLoadEvent preLoadEvent; + final PostLoadEvent postLoadEvent; + if ( session.isEventSource() ) { + preLoadEvent = new PreLoadEvent( (EventSource) session ); + postLoadEvent = new PostLoadEvent( (EventSource) session ); + } + else { + preLoadEvent = null; + postLoadEvent = null; + } + + // now finish loading the entities (2-phase load) + performTwoPhaseLoad( preLoadEvent, postLoadEvent ); + + // now we can finalize loading collections + finishLoadingCollections(); + + // finally, perform post-load operations + postLoad( postLoadEvent, afterLoadActionList ); + } + + private void finishLoadingArrays() { + LoadPlanVisitor.visit( + loadPlan, + new LoadPlanVisitationStrategyAdapter() { + @Override + public void handleCollectionReturn(CollectionReturn rootCollectionReturn) { + endLoadingArray( rootCollectionReturn.getCollectionPersister() ); + } + + @Override + public void startingCollectionFetch(CollectionFetch collectionFetch) { + endLoadingArray( collectionFetch.getCollectionPersister() ); + } + + private void endLoadingArray(CollectionPersister persister) { + if ( persister.isArray() ) { + session.getPersistenceContext() + .getLoadContexts() + .getCollectionLoadContext( resultSet ) + .endLoadingCollections( persister ); + } + } + } + ); + } + + private void performTwoPhaseLoad(PreLoadEvent preLoadEvent, PostLoadEvent postLoadEvent) { + final int numberOfHydratedObjects = hydratedEntityRegistrationList == null + ? 0 + : hydratedEntityRegistrationList.size(); + LOG.tracev( "Total objects hydrated: {0}", numberOfHydratedObjects ); + + if ( hydratedEntityRegistrationList == null ) { + return; + } + + for ( HydratedEntityRegistration registration : hydratedEntityRegistrationList ) { + TwoPhaseLoad.initializeEntity( registration.instance, readOnly, session, preLoadEvent, postLoadEvent ); + } + } + + private void finishLoadingCollections() { + LoadPlanVisitor.visit( + loadPlan, + new LoadPlanVisitationStrategyAdapter() { + @Override + public void handleCollectionReturn(CollectionReturn rootCollectionReturn) { + endLoadingArray( rootCollectionReturn.getCollectionPersister() ); + } + + @Override + public void startingCollectionFetch(CollectionFetch collectionFetch) { + endLoadingArray( collectionFetch.getCollectionPersister() ); + } + + private void endLoadingArray(CollectionPersister persister) { + if ( ! persister.isArray() ) { + session.getPersistenceContext() + .getLoadContexts() + .getCollectionLoadContext( resultSet ) + .endLoadingCollections( persister ); + } + } + } + ); + } + + private void postLoad(PostLoadEvent postLoadEvent, List afterLoadActionList) { + // Until this entire method is refactored w/ polymorphism, postLoad was + // split off from initializeEntity. It *must* occur after + // endCollectionLoad to ensure the collection is in the + // persistence context. + if ( hydratedEntityRegistrationList == null ) { + return; + } + + for ( HydratedEntityRegistration registration : hydratedEntityRegistrationList ) { + TwoPhaseLoad.postLoad( registration.instance, session, postLoadEvent ); + if ( afterLoadActionList != null ) { + for ( AfterLoadAction afterLoadAction : afterLoadActionList ) { + afterLoadAction.afterLoad( session, registration.instance, (Loadable) registration.persister ); + } + } + } + } + + private void createSubselects() { + if ( subselectLoadableEntityKeyMap.size() <= 1 ) { + // if we only returned one entity, query by key is more efficient; so do nothing here + return; + } + + final Map namedParameterLocMap = + ResultSetProcessorHelper.buildNamedParameterLocMap( queryParameters, namedParameterContext ); + + for ( Map.Entry> entry : subselectLoadableEntityKeyMap.entrySet() ) { + if ( ! entry.getKey().hasSubselectLoadableCollections() ) { + continue; + } + + SubselectFetch subselectFetch = new SubselectFetch( + //getSQLString(), + null, // aliases[i], + (Loadable) entry.getKey(), + queryParameters, + entry.getValue(), + namedParameterLocMap + ); + + for ( EntityKey key : entry.getValue() ) { + session.getPersistenceContext().getBatchFetchQueue().addSubselect( key, subselectFetch ); + } + + } + } + + private static class HydratedEntityRegistration { + private final EntityPersister persister; + private final EntityKey key; + private final Object instance; + + private HydratedEntityRegistration(EntityPersister persister, EntityKey key, Object instance) { + this.persister = persister; + this.key = key; + this.instance = instance; + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/ResultSetProcessorHelper.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/ResultSetProcessorHelper.java new file mode 100644 index 0000000000..8401218694 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/ResultSetProcessorHelper.java @@ -0,0 +1,77 @@ +/* + * 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.io.Serializable; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +import org.hibernate.engine.spi.EntityKey; +import org.hibernate.engine.spi.QueryParameters; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.loader.EntityAliases; +import org.hibernate.loader.plan.spi.EntityReference; +import org.hibernate.loader.plan.spi.Fetch; +import org.hibernate.loader.spi.NamedParameterContext; +import org.hibernate.loader.spi.ResultSetProcessingContext; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.type.CompositeType; +import org.hibernate.type.Type; + +/** + * @author Steve Ebersole + */ +public class ResultSetProcessorHelper { + public static EntityKey getOptionalObjectKey(QueryParameters queryParameters, SessionImplementor session) { + final Object optionalObject = queryParameters.getOptionalObject(); + final Serializable optionalId = queryParameters.getOptionalId(); + final String optionalEntityName = queryParameters.getOptionalEntityName(); + + if ( optionalObject != null && optionalEntityName != null ) { + return session.generateEntityKey( optionalId, session.getEntityPersister( optionalEntityName, optionalObject ) ); + } + else { + return null; + } + } + + public static Map buildNamedParameterLocMap( + QueryParameters queryParameters, + NamedParameterContext namedParameterContext) { + if ( queryParameters.getNamedParameters() == null || queryParameters.getNamedParameters().isEmpty() ) { + return null; + } + + final Map namedParameterLocMap = new HashMap(); + for ( String name : queryParameters.getNamedParameters().keySet() ) { + namedParameterLocMap.put( + name, + namedParameterContext.getNamedParameterLocations( name ) + ); + } + return namedParameterLocMap; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/ResultSetProcessorImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/ResultSetProcessorImpl.java new file mode 100644 index 0000000000..d31278e5f5 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/ResultSetProcessorImpl.java @@ -0,0 +1,207 @@ +/* + * 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.io.Serializable; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import org.jboss.logging.Logger; + +import org.hibernate.dialect.pagination.LimitHelper; +import org.hibernate.engine.FetchStyle; +import org.hibernate.engine.spi.QueryParameters; +import org.hibernate.engine.spi.RowSelection; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.loader.plan.spi.CollectionFetch; +import org.hibernate.loader.plan.spi.CollectionReturn; +import org.hibernate.loader.plan.spi.EntityFetch; +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.plan.spi.Return; +import org.hibernate.loader.spi.AfterLoadAction; +import org.hibernate.loader.spi.NamedParameterContext; +import org.hibernate.loader.spi.OnDemandResultSetProcessor; +import org.hibernate.loader.spi.ResultSetProcessor; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.pretty.MessageHelper; +import org.hibernate.transform.ResultTransformer; + +/** + * @author Steve Ebersole + */ +public class ResultSetProcessorImpl implements ResultSetProcessor { + private static final Logger LOG = Logger.getLogger( ResultSetProcessorImpl.class ); + + private final LoadPlan loadPlan; + + private final boolean hadSubselectFetches; + + public ResultSetProcessorImpl(LoadPlan loadPlan) { + this.loadPlan = loadPlan; + + LocalVisitationStrategy strategy = new LocalVisitationStrategy(); + LoadPlanVisitor.visit( loadPlan, strategy ); + this.hadSubselectFetches = strategy.hadSubselectFetches; + } + + @Override + public OnDemandResultSetProcessor toOnDemandForm() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List extractResults( + ResultSet resultSet, + final SessionImplementor session, + QueryParameters queryParameters, + NamedParameterContext namedParameterContext, + boolean returnProxies, + boolean readOnly, + ResultTransformer forcedResultTransformer, + List afterLoadActionList) throws SQLException { + + handlePotentiallyEmptyCollectionRootReturns( queryParameters.getCollectionKeys(), resultSet, session ); + + final int maxRows; + final RowSelection selection = queryParameters.getRowSelection(); + if ( LimitHelper.hasMaxRows( selection ) ) { + maxRows = selection.getMaxRows(); + LOG.tracef( "Limiting ResultSet processing to just %s rows", maxRows ); + } + else { + maxRows = Integer.MAX_VALUE; + } + + final ResultSetProcessingContextImpl context = new ResultSetProcessingContextImpl( + resultSet, + session, + loadPlan, + readOnly, + true, // use optional entity key? for now, always say yes + queryParameters, + namedParameterContext, + hadSubselectFetches + ); + + final List loadResults = new ArrayList(); + + final int rootReturnCount = loadPlan.getReturns().size(); + + LOG.trace( "Processing result set" ); + int count; + for ( count = 0; count < maxRows && resultSet.next(); count++ ) { + LOG.debugf( "Starting ResultSet row #%s", count ); + + Object logicalRow; + if ( rootReturnCount == 1 ) { + loadPlan.getReturns().get( 0 ).hydrate( resultSet, context ); + loadPlan.getReturns().get( 0 ).resolve( resultSet, context ); + + logicalRow = loadPlan.getReturns().get( 0 ).read( resultSet, context ); + } + else { + for ( Return rootReturn : loadPlan.getReturns() ) { + rootReturn.hydrate( resultSet, context ); + } + for ( Return rootReturn : loadPlan.getReturns() ) { + rootReturn.resolve( resultSet, context ); + } + + logicalRow = new Object[ rootReturnCount ]; + int pos = 0; + for ( Return rootReturn : loadPlan.getReturns() ) { + ( (Object[]) logicalRow )[pos] = rootReturn.read( resultSet, context ); + pos++; + } + } + + // todo : apply transformers here? + + loadResults.add( logicalRow ); + + context.finishUpRow(); + } + + LOG.tracev( "Done processing result set ({0} rows)", count ); + + context.finishUp( afterLoadActionList ); + + session.getPersistenceContext().initializeNonLazyCollections(); + + return loadResults; + } + + + private void handlePotentiallyEmptyCollectionRootReturns( + Serializable[] collectionKeys, + ResultSet resultSet, + SessionImplementor session) { + if ( collectionKeys == null ) { + // this is not a collection initializer (and empty collections will be detected by looking for + // the owner's identifier in the result set) + return; + } + + // this is a collection initializer, so we must create a collection + // for each of the passed-in keys, to account for the possibility + // that the collection is empty and has no rows in the result set + // + // todo : move this inside CollectionReturn ? + CollectionPersister persister = ( (CollectionReturn) loadPlan.getReturns().get( 0 ) ).getCollectionPersister(); + for ( Serializable key : collectionKeys ) { + if ( LOG.isDebugEnabled() ) { + LOG.debugf( + "Preparing collection intializer : %s", + MessageHelper.collectionInfoString( persister, key, session.getFactory() ) + ); + session.getPersistenceContext() + .getLoadContexts() + .getCollectionLoadContext( resultSet ) + .getLoadingCollection( persister, key ); + } + } + } + + + private class LocalVisitationStrategy extends LoadPlanVisitationStrategyAdapter { + private boolean hadSubselectFetches = false; + + @Override + public void startingEntityFetch(EntityFetch entityFetch) { +// only collections are currently supported for subselect fetching. +// hadSubselectFetches = hadSubselectFetches +// | entityFetch.getFetchStrategy().getStyle() == FetchStyle.SUBSELECT; + } + + @Override + public void startingCollectionFetch(CollectionFetch collectionFetch) { + hadSubselectFetches = hadSubselectFetches + | collectionFetch.getFetchStrategy().getStyle() == FetchStyle.SUBSELECT; + } + } +} 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 new file mode 100644 index 0000000000..3c617da984 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanBuildingHelper.java @@ -0,0 +1,92 @@ +/* + * 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.FetchStrategy; +import org.hibernate.loader.CollectionAliases; +import org.hibernate.loader.EntityAliases; +import org.hibernate.loader.plan.spi.AbstractFetchOwner; +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.FetchOwner; +import org.hibernate.loader.plan.spi.LoadPlanBuildingContext; +import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; +import org.hibernate.persister.walking.spi.CompositionDefinition; + +/** + * @author Steve Ebersole + */ +public class LoadPlanBuildingHelper { + public static CollectionFetch buildStandardCollectionFetch( + FetchOwner fetchOwner, + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + final CollectionAliases collectionAliases = loadPlanBuildingContext.resolveCollectionColumnAliases( attributeDefinition ); + final EntityAliases elementEntityAliases = loadPlanBuildingContext.resolveEntityColumnAliases( attributeDefinition ); + + return new CollectionFetch( + loadPlanBuildingContext.getSessionFactory(), + loadPlanBuildingContext.resolveFetchSourceAlias( attributeDefinition ), + LockMode.NONE, // todo : for now + fetchOwner, + fetchStrategy, + attributeDefinition.getName(), + collectionAliases, + elementEntityAliases + ); + } + + public static EntityFetch buildStandardEntityFetch( + FetchOwner fetchOwner, + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + + return new EntityFetch( + loadPlanBuildingContext.getSessionFactory(), + loadPlanBuildingContext.resolveFetchSourceAlias( attributeDefinition ), + LockMode.NONE, // todo : for now + fetchOwner, + attributeDefinition.getName(), + fetchStrategy, + null, // sql table alias + loadPlanBuildingContext.resolveEntityColumnAliases( attributeDefinition ) + ); + } + + public static CompositeFetch buildStandardCompositeFetch( + FetchOwner fetchOwner, + CompositionDefinition attributeDefinition, + LoadPlanBuildingContext loadPlanBuildingContext) { + return new CompositeFetch( + loadPlanBuildingContext.getSessionFactory(), + loadPlanBuildingContext.resolveFetchSourceAlias( attributeDefinition ), + (AbstractFetchOwner) fetchOwner, + attributeDefinition.getName() + ); + } +} 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 f329b30e26..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 @@ -32,26 +32,18 @@ import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.util.StringHelper; import org.hibernate.loader.CollectionAliases; -import org.hibernate.loader.DefaultEntityAliases; import org.hibernate.loader.EntityAliases; -import org.hibernate.loader.GeneratedCollectionAliases; import org.hibernate.loader.PropertyPath; -import org.hibernate.loader.plan.spi.AbstractFetchOwner; import org.hibernate.loader.plan.spi.AbstractLoadPlanBuilderStrategy; -import org.hibernate.loader.plan.spi.CollectionFetch; import org.hibernate.loader.plan.spi.CollectionReturn; -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.FetchOwner; import org.hibernate.loader.plan.spi.LoadPlan; import org.hibernate.loader.plan.spi.LoadPlanBuilderStrategy; import org.hibernate.loader.plan.spi.Return; import org.hibernate.persister.collection.CollectionPersister; -import org.hibernate.persister.entity.Loadable; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.CollectionDefinition; -import org.hibernate.persister.walking.spi.CompositeDefinition; import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.type.EntityType; import org.hibernate.type.Type; @@ -74,7 +66,6 @@ public class SingleRootReturnLoadPlanBuilderStrategy private final LoadQueryInfluencers loadQueryInfluencers; private final String rootAlias; - private int currentSuffixBase; private Return rootReturn; @@ -85,10 +76,9 @@ public class SingleRootReturnLoadPlanBuilderStrategy LoadQueryInfluencers loadQueryInfluencers, String rootAlias, int suffixSeed) { - super( sessionFactory ); + super( sessionFactory, suffixSeed ); this.loadQueryInfluencers = loadQueryInfluencers; this.rootAlias = rootAlias; - this.currentSuffixBase = suffixSeed; } @Override @@ -153,10 +143,7 @@ public class SingleRootReturnLoadPlanBuilderStrategy LockMode.NONE, // todo : for now entityName, StringHelper.generateAlias( StringHelper.unqualifyEntityName( entityName ), currentDepth() ), - new DefaultEntityAliases( - (Loadable) entityDefinition.getEntityPersister(), - Integer.toString( currentSuffixBase++ ) + '_' - ) + generateEntityColumnAliases( entityDefinition.getEntityPersister() ) ); } @@ -165,17 +152,16 @@ public class SingleRootReturnLoadPlanBuilderStrategy final CollectionPersister persister = collectionDefinition.getCollectionPersister(); final String collectionRole = persister.getRole(); - final CollectionAliases collectionAliases = new GeneratedCollectionAliases( - collectionDefinition.getCollectionPersister(), - Integer.toString( currentSuffixBase++ ) + '_' + final CollectionAliases collectionAliases = generateCollectionColumnAliases( + collectionDefinition.getCollectionPersister() ); + final Type elementType = collectionDefinition.getCollectionPersister().getElementType(); final EntityAliases elementAliases; if ( elementType.isEntityType() ) { final EntityType entityElementType = (EntityType) elementType; - elementAliases = new DefaultEntityAliases( - (Loadable) entityElementType.getAssociatedJoinable( sessionFactory() ), - Integer.toString( currentSuffixBase++ ) + '_' + elementAliases = generateEntityColumnAliases( + (EntityPersister) entityElementType.getAssociatedJoinable( sessionFactory() ) ); } else { @@ -193,76 +179,16 @@ public class SingleRootReturnLoadPlanBuilderStrategy ); } - @Override - protected CollectionFetch buildCollectionFetch( - FetchOwner fetchOwner, - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy) { - final CollectionDefinition collectionDefinition = attributeDefinition.toCollectionDefinition(); - final CollectionAliases collectionAliases = new GeneratedCollectionAliases( - collectionDefinition.getCollectionPersister(), - Integer.toString( currentSuffixBase++ ) + '_' - ); - final Type elementType = collectionDefinition.getCollectionPersister().getElementType(); - final EntityAliases elementAliases; - if ( elementType.isEntityType() ) { - final EntityType entityElementType = (EntityType) elementType; - elementAliases = new DefaultEntityAliases( - (Loadable) entityElementType.getAssociatedJoinable( sessionFactory() ), - Integer.toString( currentSuffixBase++ ) + '_' - ); - } - else { - elementAliases = null; - } - return new CollectionFetch( - sessionFactory(), - createImplicitAlias(), - LockMode.NONE, // todo : for now - (AbstractFetchOwner) fetchOwner, - fetchStrategy, - attributeDefinition.getName(), - collectionAliases, - elementAliases - ); + // LoadPlanBuildingContext impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Override + public String resolveRootSourceAlias(EntityDefinition definition) { + return rootAlias; } @Override - protected EntityFetch buildEntityFetch( - FetchOwner fetchOwner, - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy) { - final EntityDefinition entityDefinition = attributeDefinition.toEntityDefinition(); - - return new EntityFetch( - sessionFactory(), - createImplicitAlias(), - LockMode.NONE, // todo : for now - (AbstractFetchOwner) fetchOwner, - attributeDefinition.getName(), - fetchStrategy, - StringHelper.generateAlias( entityDefinition.getEntityPersister().getEntityName(), currentDepth() ), - new DefaultEntityAliases( - (Loadable) entityDefinition.getEntityPersister(), - Integer.toString( currentSuffixBase++ ) + '_' - ) - ); - } - - @Override - protected CompositeFetch buildCompositeFetch(FetchOwner fetchOwner, CompositeDefinition attributeDefinition) { - return new CompositeFetch( - sessionFactory(), - createImplicitAlias(), - (AbstractFetchOwner) fetchOwner, - attributeDefinition.getName() - ); - } - - private int implicitAliasUniqueness = 0; - - private String createImplicitAlias() { - return "ia" + implicitAliasUniqueness++; + public String resolveRootSourceAlias(CollectionDefinition definition) { + return rootAlias; } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractCollectionReference.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractCollectionReference.java new file mode 100644 index 0000000000..e623f0a898 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractCollectionReference.java @@ -0,0 +1,143 @@ +/* + * 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.spi; + +import org.hibernate.LockMode; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.CollectionAliases; +import org.hibernate.loader.EntityAliases; +import org.hibernate.loader.PropertyPath; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.type.Type; + +/** + * @author Steve Ebersole + */ +public abstract class AbstractCollectionReference extends AbstractPlanNode implements CollectionReference { + private final String alias; + private final LockMode lockMode; + private final CollectionPersister collectionPersister; + private final PropertyPath propertyPath; + + private final CollectionAliases collectionAliases; + private final EntityAliases elementEntityAliases; + + private final FetchOwner indexGraph; + private final FetchOwner elementGraph; + + protected AbstractCollectionReference( + SessionFactoryImplementor sessionFactory, + String alias, + LockMode lockMode, + CollectionPersister collectionPersister, + PropertyPath propertyPath, + CollectionAliases collectionAliases, + EntityAliases elementEntityAliases) { + super( sessionFactory ); + this.alias = alias; + this.lockMode = lockMode; + this.collectionPersister = collectionPersister; + this.propertyPath = propertyPath; + + this.collectionAliases = collectionAliases; + this.elementEntityAliases = elementEntityAliases; + + this.indexGraph = buildIndexGraph( getCollectionPersister() ); + this.elementGraph = buildElementGraph( getCollectionPersister() ); + } + + private FetchOwner buildIndexGraph(CollectionPersister persister) { + if ( persister.hasIndex() ) { + final Type type = persister.getIndexType(); + if ( type.isAssociationType() ) { + if ( type.isEntityType() ) { + return new EntityIndexGraph( sessionFactory(), this, propertyPath() ); + } + } + else if ( type.isComponentType() ) { + return new CompositeIndexGraph( sessionFactory(), this, propertyPath() ); + } + } + + return null; + } + + private FetchOwner buildElementGraph(CollectionPersister persister) { + final Type type = persister.getElementType(); + if ( type.isAssociationType() ) { + if ( type.isEntityType() ) { + return new EntityElementGraph( sessionFactory(), this, propertyPath() ); + } + } + else if ( type.isComponentType() ) { + return new CompositeElementGraph( sessionFactory(), this, propertyPath() ); + } + + return null; + } + + public PropertyPath propertyPath() { + return propertyPath; + } + + @Override + public String getAlias() { + return alias; + } + + @Override + public LockMode getLockMode() { + return lockMode; + } + + @Override + public CollectionAliases getCollectionAliases() { + return collectionAliases; + } + + @Override + public EntityAliases getElementEntityAliases() { + return elementEntityAliases; + } + + @Override + public CollectionPersister getCollectionPersister() { + return collectionPersister; + } + + @Override + public FetchOwner getIndexGraph() { + return indexGraph; + } + + @Override + public FetchOwner getElementGraph() { + return elementGraph; + } + + @Override + public boolean hasEntityElements() { + return getCollectionPersister().isOneToMany() || getCollectionPersister().isManyToMany(); + } +} 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/AbstractFetch.java index e7d2751247..bb9d9d0c18 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/AbstractFetch.java @@ -44,7 +44,7 @@ public abstract class AbstractFetch extends AbstractFetchOwner implements Fetch SessionFactoryImplementor factory, String alias, LockMode lockMode, - AbstractFetchOwner owner, + FetchOwner owner, String ownerProperty, FetchStrategy fetchStrategy) { super( factory, alias, lockMode ); @@ -85,4 +85,9 @@ public abstract class AbstractFetch extends AbstractFetchOwner implements Fetch public PropertyPath getPropertyPath() { return propertyPath; } + + @Override + public String toString() { + return "Fetch(" + propertyPath.getFullPath() + ")"; + } } 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 87063b9278..d0dbc8197f 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 @@ -56,7 +56,8 @@ public abstract class AbstractFetchOwner extends AbstractPlanNode implements Fet return lockMode; } - void addFetch(Fetch fetch) { + @Override + public void addFetch(Fetch fetch) { if ( fetch.getOwner() != this ) { throw new IllegalArgumentException( "Fetch and owner did not match" ); } 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 f9324daa96..2ec4457bfe 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 @@ -23,29 +23,62 @@ */ package org.hibernate.loader.plan.spi; +import java.io.Serializable; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jboss.logging.Logger; import org.hibernate.HibernateException; +import org.hibernate.LockMode; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchTiming; +import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.internal.util.StringHelper; +import org.hibernate.loader.CollectionAliases; +import org.hibernate.loader.DefaultEntityAliases; +import org.hibernate.loader.EntityAliases; +import org.hibernate.loader.GeneratedCollectionAliases; +import org.hibernate.loader.PropertyPath; +import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; +import org.hibernate.loader.spi.ResultSetProcessingContext; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.Loadable; +import org.hibernate.persister.spi.HydratedCompoundValueHandler; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.CollectionDefinition; -import org.hibernate.persister.walking.spi.CompositeDefinition; +import org.hibernate.persister.walking.spi.CollectionElementDefinition; +import org.hibernate.persister.walking.spi.CollectionIndexDefinition; +import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.persister.walking.spi.EntityDefinition; +import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; +import org.hibernate.persister.walking.spi.WalkingException; import org.hibernate.type.Type; +import static org.hibernate.loader.spi.ResultSetProcessingContext.IdentifierResolutionContext; + /** * @author Steve Ebersole */ -public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilderStrategy { +public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilderStrategy, LoadPlanBuildingContext { + private static final Logger log = Logger.getLogger( AbstractLoadPlanBuilderStrategy.class ); + private final SessionFactoryImplementor sessionFactory; private ArrayDeque fetchOwnerStack = new ArrayDeque(); + private ArrayDeque collectionReferenceStack = new ArrayDeque(); - protected AbstractLoadPlanBuilderStrategy(SessionFactoryImplementor sessionFactory) { + protected AbstractLoadPlanBuilderStrategy(SessionFactoryImplementor sessionFactory, int suffixSeed) { this.sessionFactory = sessionFactory; + this.currentSuffixBase = suffixSeed; } public SessionFactoryImplementor sessionFactory() { @@ -53,21 +86,39 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder } protected FetchOwner currentFetchOwner() { - return fetchOwnerStack.peekLast(); + return fetchOwnerStack.peekFirst(); } @Override public void start() { - // nothing to do + if ( ! fetchOwnerStack.isEmpty() ) { + throw new WalkingException( + "Fetch owner stack was not empty on start; " + + "be sure to not use LoadPlanBuilderStrategy instances concurrently" + ); + } + if ( ! collectionReferenceStack.isEmpty() ) { + throw new WalkingException( + "Collection reference stack was not empty on start; " + + "be sure to not use LoadPlanBuilderStrategy instances concurrently" + ); + } } @Override public void finish() { - // nothing to do + fetchOwnerStack.clear(); + collectionReferenceStack.clear(); } @Override public void startingEntity(EntityDefinition entityDefinition) { + log.tracef( + "%s Starting entity : %s", + StringHelper.repeat( ">>", fetchOwnerStack.size() ), + entityDefinition.getEntityPersister().getEntityName() + ); + if ( fetchOwnerStack.isEmpty() ) { // this is a root... if ( ! supportsRootEntityReturns() ) { @@ -75,7 +126,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder } final EntityReturn entityReturn = buildRootEntityReturn( entityDefinition ); addRootReturn( entityReturn ); - fetchOwnerStack.push( entityReturn ); + pushToStack( entityReturn ); } // otherwise this call should represent a fetch which should have been handled in #startingAttribute } @@ -88,11 +139,112 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder @Override public void finishingEntity(EntityDefinition entityDefinition) { - // nothing to do + // pop the current fetch owner, and make sure what we just popped represents this entity + final FetchOwner poppedFetchOwner = popFromStack(); + + if ( ! EntityReference.class.isInstance( poppedFetchOwner ) ) { + throw new WalkingException( "Mismatched FetchOwner from stack on pop" ); + } + + final EntityReference entityReference = (EntityReference) poppedFetchOwner; + // NOTE : this is not the most exhaustive of checks because of hierarchical associations (employee/manager) + if ( ! entityReference.getEntityPersister().equals( entityDefinition.getEntityPersister() ) ) { + throw new WalkingException( "Mismatched FetchOwner from stack on pop" ); + } + + log.tracef( + "%s Finished entity : %s", + StringHelper.repeat( "<<", fetchOwnerStack.size() ), + entityDefinition.getEntityPersister().getEntityName() + ); + } + + @Override + public void startingEntityIdentifier(EntityIdentifierDefinition entityIdentifierDefinition) { + log.tracef( + "%s Starting entity identifier : %s", + StringHelper.repeat( ">>", fetchOwnerStack.size() ), + entityIdentifierDefinition.getEntityDefinition().getEntityPersister().getEntityName() + ); + + final EntityReference entityReference = (EntityReference) currentFetchOwner(); + + // perform some stack validation + if ( ! entityReference.getEntityPersister().equals( entityIdentifierDefinition.getEntityDefinition().getEntityPersister() ) ) { + throw new WalkingException( + String.format( + "Encountered unexpected fetch owner [%s] in stack while processing entity identifier for [%s]", + entityReference.getEntityPersister().getEntityName(), + entityIdentifierDefinition.getEntityDefinition().getEntityPersister().getEntityName() + ) + ); + } + + final FetchOwner identifierAttributeCollector; + if ( entityIdentifierDefinition.isEncapsulated() ) { + identifierAttributeCollector = new EncapsulatedIdentifierAttributeCollector( entityReference ); + } + else { + identifierAttributeCollector = new NonEncapsulatedIdentifierAttributeCollector( entityReference ); + } + pushToStack( identifierAttributeCollector ); + } + + @Override + public void finishingEntityIdentifier(EntityIdentifierDefinition entityIdentifierDefinition) { + // perform some stack validation on exit, first on the current stack element we want to pop + { + final FetchOwner poppedFetchOwner = popFromStack(); + + if ( ! AbstractIdentifierAttributeCollector.class.isInstance( poppedFetchOwner ) ) { + throw new WalkingException( "Unexpected state in FetchOwner stack" ); + } + + final EntityReference entityReference = (EntityReference) poppedFetchOwner; + if ( ! entityReference.getEntityPersister().equals( entityIdentifierDefinition.getEntityDefinition().getEntityPersister() ) ) { + throw new WalkingException( + String.format( + "Encountered unexpected fetch owner [%s] in stack while processing entity identifier for [%s]", + entityReference.getEntityPersister().getEntityName(), + entityIdentifierDefinition.getEntityDefinition().getEntityPersister().getEntityName() + ) + ); + } + } + + // and then on the element before it + { + final FetchOwner currentFetchOwner = currentFetchOwner(); + if ( ! EntityReference.class.isInstance( currentFetchOwner ) ) { + throw new WalkingException( "Unexpected state in FetchOwner stack" ); + } + final EntityReference entityReference = (EntityReference) currentFetchOwner; + if ( ! entityReference.getEntityPersister().equals( entityIdentifierDefinition.getEntityDefinition().getEntityPersister() ) ) { + throw new WalkingException( + String.format( + "Encountered unexpected fetch owner [%s] in stack while processing entity identifier for [%s]", + entityReference.getEntityPersister().getEntityName(), + entityIdentifierDefinition.getEntityDefinition().getEntityPersister().getEntityName() + ) + ); + } + } + + log.tracef( + "%s Finished entity identifier : %s", + StringHelper.repeat( "<<", fetchOwnerStack.size() ), + entityIdentifierDefinition.getEntityDefinition().getEntityPersister().getEntityName() + ); } @Override public void startingCollection(CollectionDefinition collectionDefinition) { + log.tracef( + "%s Starting collection : %s", + StringHelper.repeat( ">>", fetchOwnerStack.size() ), + collectionDefinition.getCollectionPersister().getRole() + ); + if ( fetchOwnerStack.isEmpty() ) { // this is a root... if ( ! supportsRootCollectionReturns() ) { @@ -100,7 +252,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder } final CollectionReturn collectionReturn = buildRootCollectionReturn( collectionDefinition ); addRootReturn( collectionReturn ); - fetchOwnerStack.push( collectionReturn ); + pushToCollectionStack( collectionReturn ); } } @@ -109,24 +261,96 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder } @Override - public void finishingCollection(CollectionDefinition collectionDefinition) { - // nothing to do + public void startingCollectionIndex(CollectionIndexDefinition collectionIndexDefinition) { + final Type indexType = collectionIndexDefinition.getType(); + if ( indexType.isAssociationType() || indexType.isComponentType() ) { + final CollectionReference collectionReference = collectionReferenceStack.peekFirst(); + final FetchOwner indexGraph = collectionReference.getIndexGraph(); + if ( indexGraph == null ) { + throw new WalkingException( "Collection reference did not return index handler" ); + } + pushToStack( indexGraph ); + } } @Override - public void startingComposite(CompositeDefinition compositeDefinition) { + public void finishingCollectionIndex(CollectionIndexDefinition collectionIndexDefinition) { + // nothing to do here + // - the element graph pushed while starting would be popped in finishing/Entity/finishingComposite + } + + @Override + public void startingCollectionElements(CollectionElementDefinition elementDefinition) { + if ( elementDefinition.getType().isAssociationType() || elementDefinition.getType().isComponentType() ) { + final CollectionReference collectionReference = collectionReferenceStack.peekFirst(); + final FetchOwner elementGraph = collectionReference.getElementGraph(); + if ( elementGraph == null ) { + throw new WalkingException( "Collection reference did not return element handler" ); + } + pushToStack( elementGraph ); + } + } + + @Override + public void finishingCollectionElements(CollectionElementDefinition elementDefinition) { + // nothing to do here + // - the element graph pushed while starting would be popped in finishing/Entity/finishingComposite + } + + @Override + public void finishingCollection(CollectionDefinition collectionDefinition) { + // pop the current fetch owner, and make sure what we just popped represents this collection + final CollectionReference collectionReference = popFromCollectionStack(); + if ( ! collectionReference.getCollectionPersister().equals( collectionDefinition.getCollectionPersister() ) ) { + throw new WalkingException( "Mismatched FetchOwner from stack on pop" ); + } + + log.tracef( + "%s Finished collection : %s", + StringHelper.repeat( "<<", fetchOwnerStack.size() ), + collectionDefinition.getCollectionPersister().getRole() + ); + } + + @Override + public void startingComposite(CompositionDefinition compositionDefinition) { + log.tracef( + "%s Starting composition : %s", + StringHelper.repeat( ">>", fetchOwnerStack.size() ), + compositionDefinition.getName() + ); + if ( fetchOwnerStack.isEmpty() ) { throw new HibernateException( "A component cannot be the root of a walk nor a graph" ); } } @Override - public void finishingComposite(CompositeDefinition compositeDefinition) { - // nothing to do + public void finishingComposite(CompositionDefinition compositionDefinition) { + // pop the current fetch owner, and make sure what we just popped represents this composition + final FetchOwner poppedFetchOwner = popFromStack(); + + if ( ! CompositeFetch.class.isInstance( poppedFetchOwner ) ) { + throw new WalkingException( "Mismatched FetchOwner from stack on pop" ); + } + + // NOTE : not much else we can really check here atm since on the walking spi side we do not have path + + log.tracef( + "%s Finished composition : %s", + StringHelper.repeat( "<<", fetchOwnerStack.size() ), + compositionDefinition.getName() + ); } @Override public boolean startingAttribute(AttributeDefinition attributeDefinition) { + log.tracef( + "%s Starting attribute %s", + StringHelper.repeat( ">>", fetchOwnerStack.size() ), + attributeDefinition + ); + final Type attributeType = attributeDefinition.getType(); final boolean isComponentType = attributeType.isComponentType(); @@ -136,30 +360,26 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder return true; } else if ( isComponentType ) { - return handleCompositeAttribute( (CompositeDefinition) attributeDefinition ); + return handleCompositeAttribute( (CompositionDefinition) attributeDefinition ); } else { return handleAssociationAttribute( (AssociationAttributeDefinition) attributeDefinition ); } } - @Override public void finishingAttribute(AttributeDefinition attributeDefinition) { - final Type attributeType = attributeDefinition.getType(); - - final boolean isComponentType = attributeType.isComponentType(); - final boolean isBasicType = ! ( isComponentType || attributeType.isAssociationType() ); - - if ( ! isBasicType ) { - fetchOwnerStack.removeLast(); - } + log.tracef( + "%s Finishing up attribute : %s", + StringHelper.repeat( "<<", fetchOwnerStack.size() ), + attributeDefinition + ); } - protected boolean handleCompositeAttribute(CompositeDefinition attributeDefinition) { - final FetchOwner fetchOwner = fetchOwnerStack.peekLast(); - final CompositeFetch fetch = buildCompositeFetch( fetchOwner, attributeDefinition ); - fetchOwnerStack.addLast( fetch ); + protected boolean handleCompositeAttribute(CompositionDefinition attributeDefinition) { + final FetchOwner fetchOwner = currentFetchOwner(); + final CompositeFetch fetch = fetchOwner.buildCompositeFetch( attributeDefinition, this ); + pushToStack( fetch ); return true; } @@ -169,17 +389,20 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder return false; } - final FetchOwner fetchOwner = fetchOwnerStack.peekLast(); + final FetchOwner fetchOwner = currentFetchOwner(); fetchOwner.validateFetchPlan( fetchStrategy ); final Fetch associationFetch; if ( attributeDefinition.isCollection() ) { - associationFetch = buildCollectionFetch( fetchOwner, attributeDefinition, fetchStrategy ); + associationFetch = fetchOwner.buildCollectionFetch( attributeDefinition, fetchStrategy, this ); } else { - associationFetch = buildEntityFetch( fetchOwner, attributeDefinition, fetchStrategy ); + associationFetch = fetchOwner.buildEntityFetch( attributeDefinition, fetchStrategy, this ); + } + + if ( FetchOwner.class.isInstance( associationFetch ) ) { + pushToStack( (FetchOwner) associationFetch ); } - fetchOwnerStack.addLast( associationFetch ); return true; } @@ -194,19 +417,309 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder return false; } + private void pushToStack(FetchOwner fetchOwner) { + log.trace( "Pushing fetch owner to stack : " + fetchOwner ); + fetchOwnerStack.addFirst( fetchOwner ); + } + + private FetchOwner popFromStack() { + final FetchOwner last = fetchOwnerStack.removeFirst(); + log.trace( "Popped fetch owner from stack : " + last ); + if ( FetchStackAware.class.isInstance( last ) ) { + ( (FetchStackAware) last ).poppedFromStack(); + } + return last; + } + + private void pushToCollectionStack(CollectionReference collectionReference) { + log.trace( "Pushing collection reference to stack : " + collectionReference ); + collectionReferenceStack.addFirst( collectionReference ); + } + + private CollectionReference popFromCollectionStack() { + final CollectionReference last = collectionReferenceStack.removeFirst(); + log.trace( "Popped collection reference from stack : " + last ); + if ( FetchStackAware.class.isInstance( last ) ) { + ( (FetchStackAware) last ).poppedFromStack(); + } + return last; + } + protected abstract EntityReturn buildRootEntityReturn(EntityDefinition entityDefinition); protected abstract CollectionReturn buildRootCollectionReturn(CollectionDefinition collectionDefinition); - protected abstract CollectionFetch buildCollectionFetch( - FetchOwner fetchOwner, - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy); - protected abstract EntityFetch buildEntityFetch( - FetchOwner fetchOwner, - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy); - protected abstract CompositeFetch buildCompositeFetch(FetchOwner fetchOwner, CompositeDefinition attributeDefinition); + // LoadPlanBuildingContext impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + private int currentSuffixBase; + private int implicitAliasUniqueness = 0; + + private String createImplicitAlias() { + return "ia" + implicitAliasUniqueness++; + } + + @Override + public SessionFactoryImplementor getSessionFactory() { + return sessionFactory(); + } + + @Override + public EntityAliases resolveEntityColumnAliases(AssociationAttributeDefinition attributeDefinition) { + return generateEntityColumnAliases( attributeDefinition.toEntityDefinition().getEntityPersister() ); + } + + protected EntityAliases generateEntityColumnAliases(EntityPersister persister) { + return new DefaultEntityAliases( (Loadable) persister, Integer.toString( currentSuffixBase++ ) + '_' ); + } + + @Override + public CollectionAliases resolveCollectionColumnAliases(AssociationAttributeDefinition attributeDefinition) { + return generateCollectionColumnAliases( attributeDefinition.toCollectionDefinition().getCollectionPersister() ); + } + + protected CollectionAliases generateCollectionColumnAliases(CollectionPersister persister) { + return new GeneratedCollectionAliases( persister, Integer.toString( currentSuffixBase++ ) + '_' ); + } + + @Override + public String resolveRootSourceAlias(EntityDefinition definition) { + return createImplicitAlias(); + } + + @Override + public String resolveRootSourceAlias(CollectionDefinition definition) { + return createImplicitAlias(); + } + + @Override + public String resolveFetchSourceAlias(AssociationAttributeDefinition attributeDefinition) { + return createImplicitAlias(); + } + + @Override + public String resolveFetchSourceAlias(CompositionDefinition compositionDefinition) { + return createImplicitAlias(); + } + + public static interface FetchStackAware { + public void poppedFromStack(); + } + + protected static abstract class AbstractIdentifierAttributeCollector + implements FetchOwner, EntityReference, FetchStackAware { + + protected final EntityReference entityReference; + private final PropertyPath propertyPath; + + protected final List identifierFetches = new ArrayList(); + protected final Map fetchToHydratedStateExtractorMap + = new HashMap(); + + public AbstractIdentifierAttributeCollector(EntityReference entityReference) { + this.entityReference = entityReference; + this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath().append( "" ); + } + + @Override + public String getAlias() { + return entityReference.getAlias(); + } + + @Override + public LockMode getLockMode() { + return entityReference.getLockMode(); + } + + @Override + public EntityPersister getEntityPersister() { + return entityReference.getEntityPersister(); + } + + @Override + public IdentifierDescription getIdentifierDescription() { + return entityReference.getIdentifierDescription(); + } + + @Override + public CollectionFetch buildCollectionFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + throw new WalkingException( "Entity identifier cannot contain persistent collections" ); + } + + @Override + public EntityFetch buildEntityFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + // we have a key-many-to-one + // + // IMPL NOTE: we pass ourselves as the FetchOwner which will route the fetch back throw our #addFetch + // impl. We collect them there and later build the IdentifierDescription + final EntityFetch fetch = LoadPlanBuildingHelper.buildStandardEntityFetch( + this, + attributeDefinition, + fetchStrategy, + loadPlanBuildingContext + ); + fetchToHydratedStateExtractorMap.put( fetch, attributeDefinition.getHydratedCompoundValueExtractor() ); + + return fetch; + } + + @Override + public CompositeFetch buildCompositeFetch( + CompositionDefinition attributeDefinition, LoadPlanBuildingContext loadPlanBuildingContext) { + // nested composition. Unusual, but not disallowed. + // + // IMPL NOTE: we pass ourselves as the FetchOwner which will route the fetch back throw our #addFetch + // impl. We collect them there and later build the IdentifierDescription + return LoadPlanBuildingHelper.buildStandardCompositeFetch( + this, + attributeDefinition, + loadPlanBuildingContext + ); + } + + @Override + public void poppedFromStack() { + final IdentifierDescription identifierDescription = buildIdentifierDescription(); + entityReference.injectIdentifierDescription( identifierDescription ); + } + + protected abstract IdentifierDescription buildIdentifierDescription(); + + @Override + public void addFetch(Fetch fetch) { + identifierFetches.add( (EntityFetch) fetch ); + } + + @Override + public Fetch[] getFetches() { + return ( (FetchOwner) entityReference ).getFetches(); + } + + @Override + public void validateFetchPlan(FetchStrategy fetchStrategy) { + ( (FetchOwner) entityReference ).validateFetchPlan( fetchStrategy ); + } + + @Override + public EntityPersister retrieveFetchSourcePersister() { + return ( (FetchOwner) entityReference ).retrieveFetchSourcePersister(); + } + + @Override + public PropertyPath getPropertyPath() { + return propertyPath; + } + + @Override + public void injectIdentifierDescription(IdentifierDescription identifierDescription) { + throw new WalkingException( + "IdentifierDescription collector should not get injected with IdentifierDescription" + ); + } + } + + protected static class EncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector { + public EncapsulatedIdentifierAttributeCollector(EntityReference entityReference) { + super( entityReference ); + } + + @Override + protected IdentifierDescription buildIdentifierDescription() { + return new IdentifierDescriptionImpl( + entityReference, + identifierFetches.toArray( new EntityFetch[ identifierFetches.size() ] ), + null + ); + } + } + + protected static class NonEncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector { + public NonEncapsulatedIdentifierAttributeCollector(EntityReference entityReference) { + super( entityReference ); + } + + @Override + protected IdentifierDescription buildIdentifierDescription() { + return new IdentifierDescriptionImpl( + entityReference, + identifierFetches.toArray( new EntityFetch[ identifierFetches.size() ] ), + fetchToHydratedStateExtractorMap + ); + } + } + + private static class IdentifierDescriptionImpl implements IdentifierDescription { + private final EntityReference entityReference; + private final EntityFetch[] identifierFetches; + private final Map fetchToHydratedStateExtractorMap; + + private IdentifierDescriptionImpl( + EntityReference entityReference, EntityFetch[] identifierFetches, + Map fetchToHydratedStateExtractorMap) { + this.entityReference = entityReference; + this.identifierFetches = identifierFetches; + this.fetchToHydratedStateExtractorMap = fetchToHydratedStateExtractorMap; + } + + @Override + public Fetch[] getFetches() { + return identifierFetches; + } + + @Override + public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { + final IdentifierResolutionContext ownerIdentifierResolutionContext = + context.getIdentifierResolutionContext( entityReference ); + final Serializable ownerIdentifierHydratedState = ownerIdentifierResolutionContext.getHydratedForm(); + + for ( EntityFetch fetch : identifierFetches ) { + final IdentifierResolutionContext identifierResolutionContext = + context.getIdentifierResolutionContext( fetch ); + // if the identifier was already hydrated, nothing to do + if ( identifierResolutionContext.getHydratedForm() != null ) { + continue; + } + + // try to extract the sub-hydrated value from the owners tuple array + if ( fetchToHydratedStateExtractorMap != null && ownerIdentifierHydratedState != null ) { + Serializable extracted = (Serializable) fetchToHydratedStateExtractorMap.get( fetch ) + .extract( ownerIdentifierHydratedState ); + identifierResolutionContext.registerHydratedForm( extracted ); + continue; + } + + // if we can't, then read from result set + fetch.hydrate( resultSet, context ); + } + } + + @Override + public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { + for ( EntityFetch fetch : identifierFetches ) { + final IdentifierResolutionContext identifierResolutionContext = + context.getIdentifierResolutionContext( fetch ); + if ( identifierResolutionContext.getEntityKey() != null ) { + continue; + } + + EntityKey fetchKey = fetch.resolveInIdentifier( resultSet, context ); + identifierResolutionContext.registerEntityKey( fetchKey ); + } + + final IdentifierResolutionContext ownerIdentifierResolutionContext = + context.getIdentifierResolutionContext( entityReference ); + Serializable hydratedState = ownerIdentifierResolutionContext.getHydratedForm(); + Serializable resolvedId = (Serializable) entityReference.getEntityPersister() + .getIdentifierType() + .resolve( hydratedState, context.getSession(), null ); + return context.getSession().generateEntityKey( resolvedId, entityReference.getEntityPersister() ); + } + } } 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 973d147795..b5ad2e9e04 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 @@ -23,58 +23,75 @@ */ package org.hibernate.loader.plan.spi; +import java.sql.ResultSet; +import java.sql.SQLException; + import org.hibernate.LockMode; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.CollectionAliases; import org.hibernate.loader.EntityAliases; -import org.hibernate.persister.collection.CollectionPersister; -import org.hibernate.persister.collection.QueryableCollection; -import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.loader.PropertyPath; +import org.hibernate.loader.spi.ResultSetProcessingContext; /** * @author Steve Ebersole */ -public class CollectionFetch extends AbstractFetch implements CollectionReference { - private final CollectionAliases collectionAliases; - private final EntityAliases elementEntityAliases; - - private final CollectionPersister persister; +public class CollectionFetch extends AbstractCollectionReference implements CollectionReference, Fetch { + private final FetchOwner fetchOwner; + private final FetchStrategy fetchStrategy; public CollectionFetch( SessionFactoryImplementor sessionFactory, String alias, LockMode lockMode, - AbstractFetchOwner owner, + FetchOwner fetchOwner, FetchStrategy fetchStrategy, String ownerProperty, CollectionAliases collectionAliases, EntityAliases elementEntityAliases) { - super( sessionFactory, alias, lockMode, owner, ownerProperty, fetchStrategy ); - this.collectionAliases = collectionAliases; - this.elementEntityAliases = elementEntityAliases; - - final String role = owner.retrieveFetchSourcePersister().getEntityName() + '.' + getOwnerPropertyName(); - this.persister = sessionFactory.getCollectionPersister( role ); + super( + sessionFactory, + alias, + lockMode, + sessionFactory.getCollectionPersister( + fetchOwner.retrieveFetchSourcePersister().getEntityName() + '.' + ownerProperty + ), + fetchOwner.getPropertyPath().append( ownerProperty ), + collectionAliases, + elementEntityAliases + ); + this.fetchOwner = fetchOwner; + this.fetchStrategy = fetchStrategy; } @Override - public CollectionAliases getCollectionAliases() { - return collectionAliases; + public FetchOwner getOwner() { + return fetchOwner; } @Override - public EntityAliases getElementEntityAliases() { - return elementEntityAliases; + public String getOwnerPropertyName() { + return getPropertyPath().getProperty(); } @Override - public CollectionPersister getCollectionPersister() { - return persister; + public FetchStrategy getFetchStrategy() { + return fetchStrategy; } @Override - public EntityPersister retrieveFetchSourcePersister() { - return ( (QueryableCollection) getCollectionPersister() ).getElementPersister(); + public PropertyPath getPropertyPath() { + return propertyPath(); + } + + @Override + public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { + 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/CollectionReference.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionReference.java index fa0460e922..da4acdcbb3 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionReference.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionReference.java @@ -55,6 +55,12 @@ public interface CollectionReference { */ public CollectionPersister getCollectionPersister(); + public FetchOwner getIndexGraph(); + + public FetchOwner getElementGraph(); + + public boolean hasEntityElements(); + /** * Returns the description of the aliases in the JDBC ResultSet that identify values "belonging" to the * this collection. diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionReferenceImplementor.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionReferenceImplementor.java new file mode 100644 index 0000000000..1738802aef --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionReferenceImplementor.java @@ -0,0 +1,30 @@ +/* + * 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.spi; + +/** + * @author Steve Ebersole + */ +public interface CollectionReferenceImplementor { +} 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 0dacc39806..5bb106c0af 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 @@ -23,28 +23,22 @@ */ package org.hibernate.loader.plan.spi; +import java.sql.ResultSet; +import java.sql.SQLException; + import org.hibernate.LockMode; -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.persister.collection.CollectionPersister; -import org.hibernate.persister.collection.QueryableCollection; -import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.loader.spi.ResultSetProcessingContext; /** * @author Steve Ebersole */ -public class CollectionReturn extends AbstractFetchOwner implements Return, FetchOwner, CollectionReference { +public class CollectionReturn extends AbstractCollectionReference implements Return, CollectionReference { private final String ownerEntityName; private final String ownerProperty; - private final CollectionAliases collectionAliases; - private final EntityAliases elementEntityAliases; - - private final CollectionPersister persister; - - private final PropertyPath propertyPath = new PropertyPath(); // its a root public CollectionReturn( SessionFactoryImplementor sessionFactory, @@ -54,14 +48,17 @@ public class CollectionReturn extends AbstractFetchOwner implements Return, Fetc String ownerProperty, CollectionAliases collectionAliases, EntityAliases elementEntityAliases) { - super( sessionFactory, alias, lockMode ); + super( + sessionFactory, + alias, + lockMode, + sessionFactory.getCollectionPersister( ownerEntityName + '.' + ownerProperty ), + new PropertyPath(), // its a root + collectionAliases, + elementEntityAliases + ); this.ownerEntityName = ownerEntityName; this.ownerProperty = ownerProperty; - this.collectionAliases = collectionAliases; - this.elementEntityAliases = elementEntityAliases; - - final String role = ownerEntityName + '.' + ownerProperty; - this.persister = sessionFactory.getCollectionPersister( role ); } /** @@ -83,31 +80,22 @@ public class CollectionReturn extends AbstractFetchOwner implements Return, Fetc } @Override - public CollectionAliases getCollectionAliases() { - return collectionAliases; + public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { + // todo : anything to do here? } @Override - public EntityAliases getElementEntityAliases() { - return elementEntityAliases; + public void resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { + // todo : anything to do here? } @Override - public CollectionPersister getCollectionPersister() { - return persister; + public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { + return null; //To change body of implemented methods use File | Settings | File Templates. } @Override - public void validateFetchPlan(FetchStrategy fetchStrategy) { - } - - @Override - public EntityPersister retrieveFetchSourcePersister() { - return ( (QueryableCollection) persister ).getElementPersister(); - } - - @Override - public PropertyPath getPropertyPath() { - return propertyPath; + public String toString() { + return "CollectionReturn(" + getCollectionPersister().getRole() + ")"; } } 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 new file mode 100644 index 0000000000..c1c450793b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeElementGraph.java @@ -0,0 +1,90 @@ +package org.hibernate.loader.plan.spi; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.HibernateException; +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.PropertyPath; +import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; +import org.hibernate.persister.walking.spi.CompositionDefinition; + +/** + * @author Steve Ebersole + */ +public class CompositeElementGraph extends AbstractPlanNode implements FetchOwner { + private final CollectionReference collectionReference; + private final PropertyPath propertyPath; + private final CollectionPersister collectionPersister; + + private List fetches; + public CompositeElementGraph( + SessionFactoryImplementor sessionFactory, + CollectionReference collectionReference, + PropertyPath collectionPath) { + super( sessionFactory ); + + this.collectionReference = collectionReference; + this.collectionPersister = collectionReference.getCollectionPersister(); + this.propertyPath = collectionPath.append( "" ); + } + + @Override + public void addFetch(Fetch fetch) { + if ( fetches == null ) { + fetches = new ArrayList(); + } + fetches.add( fetch ); + } + + @Override + public Fetch[] getFetches() { + return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] ); + } + + @Override + public void validateFetchPlan(FetchStrategy fetchStrategy) { + } + + @Override + public EntityPersister retrieveFetchSourcePersister() { + return collectionPersister.getOwnerEntityPersister(); + } + + @Override + public PropertyPath getPropertyPath() { + return propertyPath; + } + + @Override + public CollectionFetch buildCollectionFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + throw new HibernateException( "Collection composite element cannot define collections" ); + } + + @Override + public EntityFetch buildEntityFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardEntityFetch( + this, + attributeDefinition, + fetchStrategy, + loadPlanBuildingContext + ); + } + + @Override + public CompositeFetch buildCompositeFetch( + CompositionDefinition attributeDefinition, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, 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 48d2f4c415..77eb3ebd2f 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 @@ -23,12 +23,18 @@ */ package org.hibernate.loader.plan.spi; +import java.sql.ResultSet; +import java.sql.SQLException; + import org.hibernate.LockMode; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SessionFactoryImplementor; +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; /** * @author Steve Ebersole @@ -48,4 +54,36 @@ public class CompositeFetch extends AbstractFetch implements Fetch { public EntityPersister retrieveFetchSourcePersister() { return getOwner().retrieveFetchSourcePersister(); } + + @Override + public CollectionFetch buildCollectionFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public EntityFetch buildEntityFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public CompositeFetch buildCompositeFetch( + CompositionDefinition attributeDefinition, LoadPlanBuildingContext loadPlanBuildingContext) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { + 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 new file mode 100644 index 0000000000..8b482ac2e1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeIndexGraph.java @@ -0,0 +1,90 @@ +package org.hibernate.loader.plan.spi; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.HibernateException; +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.PropertyPath; +import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; +import org.hibernate.persister.walking.spi.CompositionDefinition; + +/** + * @author Steve Ebersole + */ +public class CompositeIndexGraph extends AbstractPlanNode implements FetchOwner { + private final CollectionReference collectionReference; + private final PropertyPath propertyPath; + private final CollectionPersister collectionPersister; + + private List fetches; + + public CompositeIndexGraph( + SessionFactoryImplementor sessionFactory, + CollectionReference collectionReference, + PropertyPath propertyPath) { + super( sessionFactory ); + this.collectionReference = collectionReference; + this.collectionPersister = collectionReference.getCollectionPersister(); + this.propertyPath = propertyPath.append( "" ); + } + + @Override + public void addFetch(Fetch fetch) { + if ( fetches == null ) { + fetches = new ArrayList(); + } + fetches.add( fetch ); + } + + @Override + public Fetch[] getFetches() { + return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] ); + } + + @Override + public void validateFetchPlan(FetchStrategy fetchStrategy) { + } + + @Override + public EntityPersister retrieveFetchSourcePersister() { + return collectionPersister.getOwnerEntityPersister(); + } + + @Override + public PropertyPath getPropertyPath() { + return propertyPath; + } + + @Override + public CollectionFetch buildCollectionFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + throw new HibernateException( "Composite index cannot define collections" ); + } + + @Override + public EntityFetch buildEntityFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardEntityFetch( + this, + attributeDefinition, + fetchStrategy, + loadPlanBuildingContext + ); + } + + @Override + public CompositeFetch buildCompositeFetch( + CompositionDefinition attributeDefinition, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, 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 new file mode 100644 index 0000000000..b7da167727 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityElementGraph.java @@ -0,0 +1,133 @@ +package org.hibernate.loader.plan.spi; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.LockMode; +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.PropertyPath; +import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; +import org.hibernate.persister.collection.CollectionPersister; +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; + +/** + * @author Steve Ebersole + */ +public class EntityElementGraph extends AbstractPlanNode implements FetchOwner, EntityReference { + private final CollectionReference collectionReference; + private final CollectionPersister collectionPersister; + private final AssociationType elementType; + private final EntityPersister elementPersister; + private final PropertyPath propertyPath; + + private List fetches; + + private IdentifierDescription identifierDescription; + + public EntityElementGraph( + SessionFactoryImplementor sessionFactory, + CollectionReference collectionReference, + PropertyPath collectionPath) { + super( sessionFactory ); + + this.collectionReference = collectionReference; + this.collectionPersister = collectionReference.getCollectionPersister(); + this.elementType = (AssociationType) collectionPersister.getElementType(); + this.elementPersister = (EntityPersister) this.elementType.getAssociatedJoinable( sessionFactory() ); + this.propertyPath = collectionPath.append( "" ); + } + + @Override + public String getAlias() { + return null; + } + + @Override + public LockMode getLockMode() { + return null; + } + + @Override + public EntityPersister getEntityPersister() { + return elementPersister; + } + + @Override + public IdentifierDescription getIdentifierDescription() { + return identifierDescription; + } + + @Override + public void addFetch(Fetch fetch) { + if ( fetches == null ) { + fetches = new ArrayList(); + } + fetches.add( fetch ); + } + + @Override + public Fetch[] getFetches() { + return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] ); + } + + @Override + public void validateFetchPlan(FetchStrategy fetchStrategy) { + } + + @Override + public EntityPersister retrieveFetchSourcePersister() { + return elementPersister; + } + + @Override + public PropertyPath getPropertyPath() { + return propertyPath; + } + + @Override + public CollectionFetch buildCollectionFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardCollectionFetch( + this, + attributeDefinition, + fetchStrategy, + loadPlanBuildingContext + ); + } + + @Override + public EntityFetch buildEntityFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardEntityFetch( + this, + attributeDefinition, + fetchStrategy, + loadPlanBuildingContext + ); + } + + @Override + public CompositeFetch buildCompositeFetch( + CompositionDefinition attributeDefinition, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); + } + + @Override + public void injectIdentifierDescription(IdentifierDescription identifierDescription) { + this.identifierDescription = identifierDescription; + } + + @Override + public String toString() { + return "EntityElementGraph(collection=" + collectionPersister.getRole() + ", type=" + elementPersister.getEntityName() + ")"; + } +} 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 252e3e0288..dac6350468 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 @@ -23,27 +23,40 @@ */ package org.hibernate.loader.plan.spi; +import java.sql.ResultSet; +import java.sql.SQLException; + import org.hibernate.LockMode; +import org.hibernate.WrongClassException; import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.FetchTiming; +import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.EntityAliases; +import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; +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.EntityType; /** * @author Steve Ebersole */ -public class EntityFetch extends AbstractFetch implements EntityReference { +public class EntityFetch extends AbstractFetch implements EntityReference, FetchOwner { private final String sqlTableAlias; private final EntityAliases entityAliases; + private final EntityType associationType; private final EntityPersister persister; + private IdentifierDescription identifierDescription; + public EntityFetch( SessionFactoryImplementor sessionFactory, String alias, LockMode lockMode, - AbstractFetchOwner owner, + FetchOwner owner, String ownerProperty, FetchStrategy fetchStrategy, String sqlTableAlias, @@ -52,8 +65,8 @@ public class EntityFetch extends AbstractFetch implements EntityReference { this.sqlTableAlias = sqlTableAlias; this.entityAliases = entityAliases; - final EntityType type = (EntityType) owner.retrieveFetchSourcePersister().getPropertyType( ownerProperty ); - this.persister = sessionFactory.getEntityPersister( type.getAssociatedEntityName() ); + this.associationType = (EntityType) owner.retrieveFetchSourcePersister().getPropertyType( ownerProperty ); + this.persister = sessionFactory.getEntityPersister( associationType.getAssociatedEntityName() ); } @Override @@ -62,17 +75,174 @@ public class EntityFetch extends AbstractFetch implements EntityReference { } @Override - public EntityAliases getEntityAliases() { - return entityAliases; - } - - @Override - public String getSqlTableAlias() { - return sqlTableAlias; + public IdentifierDescription getIdentifierDescription() { + return identifierDescription; } @Override public EntityPersister retrieveFetchSourcePersister() { return persister; } + + + @Override + public CollectionFetch buildCollectionFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardCollectionFetch( + this, + attributeDefinition, + fetchStrategy, + loadPlanBuildingContext + ); + } + + @Override + public EntityFetch buildEntityFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardEntityFetch( + this, + attributeDefinition, + fetchStrategy, + loadPlanBuildingContext + ); + } + + @Override + public CompositeFetch buildCompositeFetch( + CompositionDefinition attributeDefinition, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); + } + + @Override + public void injectIdentifierDescription(IdentifierDescription identifierDescription) { + this.identifierDescription = identifierDescription; + } + + @Override + public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { + EntityKey entityKey = context.getDictatedRootEntityKey(); + if ( entityKey != null ) { + context.getIdentifierResolutionContext( this ).registerEntityKey( entityKey ); + return; + } + + identifierDescription.hydrate( resultSet, context ); + + for ( Fetch fetch : getFetches() ) { + fetch.hydrate( resultSet, context ); + } + } + + @Override + public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { + final ResultSetProcessingContext.IdentifierResolutionContext identifierResolutionContext = context.getIdentifierResolutionContext( this ); + EntityKey entityKey = identifierResolutionContext.getEntityKey(); + if ( entityKey == null ) { + entityKey = identifierDescription.resolve( resultSet, context ); + if ( entityKey == null ) { + // register the non-existence (though only for one-to-one associations) + if ( associationType.isOneToOne() ) { + // first, find our owner's entity-key... + final EntityKey ownersEntityKey = context.getIdentifierResolutionContext( (EntityReference) getOwner() ).getEntityKey(); + if ( ownersEntityKey != null ) { + context.getSession().getPersistenceContext() + .addNullProperty( ownersEntityKey, associationType.getPropertyName() ); + } + } + } + + identifierResolutionContext.registerEntityKey( entityKey ); + + for ( Fetch fetch : getFetches() ) { + fetch.resolve( resultSet, context ); + } + } + + return entityKey; + } + + public EntityKey resolveInIdentifier(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { + // todo : may not need to do this if entitykey is already part of the resolution context + + final EntityKey entityKey = resolve( resultSet, context ); + + final Object existing = context.getSession().getEntityUsingInterceptor( entityKey ); + + if ( existing != null ) { + if ( !persister.isInstance( existing ) ) { + throw new WrongClassException( + "loaded object was of wrong class " + existing.getClass(), + entityKey.getIdentifier(), + persister.getEntityName() + ); + } + + if ( getLockMode() != null && getLockMode() != LockMode.NONE ) { + final boolean isVersionCheckNeeded = persister.isVersioned() + && context.getSession().getPersistenceContext().getEntry( existing ).getLockMode().lessThan( getLockMode() ); + + // we don't need to worry about existing version being uninitialized because this block isn't called + // by a re-entrant load (re-entrant loads _always_ have lock mode NONE) + if ( isVersionCheckNeeded ) { + //we only check the version when _upgrading_ lock modes + context.checkVersion( + resultSet, + persister, + entityAliases, + entityKey, + existing + ); + //we need to upgrade the lock mode to the mode requested + context.getSession().getPersistenceContext().getEntry( existing ).setLockMode( getLockMode() ); + } + } + } + else { + final String concreteEntityTypeName = context.getConcreteEntityTypeName( + resultSet, + persister, + entityAliases, + entityKey + ); + + final Object entityInstance = context.getSession().instantiate( + concreteEntityTypeName, + entityKey.getIdentifier() + ); + + //need to hydrate it. + + // grab its state from the ResultSet and keep it in the Session + // (but don't yet initialize the object itself) + // note that we acquire LockMode.READ even if it was not requested + LockMode acquiredLockMode = getLockMode() == LockMode.NONE ? LockMode.READ : getLockMode(); + + context.loadFromResultSet( + resultSet, + entityInstance, + concreteEntityTypeName, + entityKey, + entityAliases, + acquiredLockMode, + persister, + getFetchStrategy().getTiming() == FetchTiming.IMMEDIATE, + associationType + ); + + // materialize associations (and initialize the object) later + context.registerHydratedEntity( persister, entityKey, entityInstance ); + } + + return entityKey; + } + + @Override + public String toString() { + return "EntityFetch(" + getPropertyPath().getFullPath() + " -> " + persister.getEntityName() + ")"; + } } 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 new file mode 100644 index 0000000000..8d7c50de55 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityIndexGraph.java @@ -0,0 +1,150 @@ +/* + * 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.spi; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.LockMode; +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.PropertyPath; +import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; +import org.hibernate.persister.collection.CollectionPersister; +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; + +/** + * @author Steve Ebersole + */ +public class EntityIndexGraph extends AbstractPlanNode implements FetchOwner, EntityReference { + private final CollectionReference collectionReference; + private final CollectionPersister collectionPersister; + private final AssociationType indexType; + private final EntityPersister indexPersister; + private final PropertyPath propertyPath; + + private List fetches; + + private IdentifierDescription identifierDescription; + + public EntityIndexGraph( + SessionFactoryImplementor sessionFactory, + CollectionReference collectionReference, + PropertyPath collectionPath) { + super( sessionFactory ); + this.collectionReference = collectionReference; + this.collectionPersister = collectionReference.getCollectionPersister(); + this.indexType = (AssociationType) collectionPersister.getIndexType(); + this.indexPersister = (EntityPersister) this.indexType.getAssociatedJoinable( sessionFactory() ); + this.propertyPath = collectionPath.append( "" ); // todo : do we want the part? + } + + @Override + public String getAlias() { + return null; + } + + @Override + public LockMode getLockMode() { + return null; + } + + @Override + public EntityPersister getEntityPersister() { + return indexPersister; + } + + @Override + public IdentifierDescription getIdentifierDescription() { + return identifierDescription; + } + + @Override + public void addFetch(Fetch fetch) { + if ( fetches == null ) { + fetches = new ArrayList(); + } + fetches.add( fetch ); + } + + @Override + public Fetch[] getFetches() { + return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] ); + } + + @Override + public void validateFetchPlan(FetchStrategy fetchStrategy) { + } + + @Override + public EntityPersister retrieveFetchSourcePersister() { + return indexPersister; + } + + @Override + public PropertyPath getPropertyPath() { + return propertyPath; + } + + @Override + public CollectionFetch buildCollectionFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardCollectionFetch( + this, + attributeDefinition, + fetchStrategy, + loadPlanBuildingContext + ); + } + + @Override + public EntityFetch buildEntityFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardEntityFetch( + this, + attributeDefinition, + fetchStrategy, + loadPlanBuildingContext + ); + } + + @Override + public CompositeFetch buildCompositeFetch( + CompositionDefinition attributeDefinition, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); + } + + @Override + public void injectIdentifierDescription(IdentifierDescription identifierDescription) { + this.identifierDescription = identifierDescription; + } +} 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 d1102acc7b..401a478eed 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 @@ -24,7 +24,6 @@ package org.hibernate.loader.plan.spi; import org.hibernate.LockMode; -import org.hibernate.loader.EntityAliases; import org.hibernate.persister.entity.EntityPersister; /** @@ -32,7 +31,7 @@ import org.hibernate.persister.entity.EntityPersister; * * @author Steve Ebersole */ -public interface EntityReference { +public interface EntityReference extends IdentifierDescriptionInjectable { /** * Retrieve the alias associated with the persister (entity/collection). * @@ -54,20 +53,5 @@ public interface EntityReference { */ public EntityPersister getEntityPersister(); - /** - * Returns the description of the aliases in the JDBC ResultSet that identify values "belonging" to the this entity. - * - * @return The ResultSet alias descriptor. - */ - public EntityAliases getEntityAliases(); - - /** - * Obtain the SQL table alias associated with this entity. - * - * TODO : eventually this needs to not be a String, but a representation like I did for the Antlr3 branch - * (AliasRoot, I think it was called) - * - * @return The SQL table alias for this entity - */ - public String getSqlTableAlias(); + public IdentifierDescription getIdentifierDescription(); } 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 eb14ecf297..7ec852d714 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 @@ -23,12 +23,25 @@ */ package org.hibernate.loader.plan.spi; +import java.io.Serializable; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.EntityAliases; import org.hibernate.loader.PropertyPath; +import org.hibernate.loader.internal.ResultSetProcessorHelper; +import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; +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 static org.hibernate.loader.spi.ResultSetProcessingContext.IdentifierResolutionContext; /** * @author Steve Ebersole @@ -41,6 +54,8 @@ public class EntityReturn extends AbstractFetchOwner implements Return, FetchOwn private final PropertyPath propertyPath = new PropertyPath(); // its a root + private IdentifierDescription identifierDescription; + public EntityReturn( SessionFactoryImplementor sessionFactory, String alias, @@ -71,13 +86,8 @@ public class EntityReturn extends AbstractFetchOwner implements Return, FetchOwn } @Override - public EntityAliases getEntityAliases() { - return entityAliases; - } - - @Override - public String getSqlTableAlias() { - return sqlTableAlias; + public IdentifierDescription getIdentifierDescription() { + return identifierDescription; } @Override @@ -93,4 +103,83 @@ public class EntityReturn extends AbstractFetchOwner implements Return, FetchOwn public PropertyPath getPropertyPath() { return propertyPath; } + + @Override + public CollectionFetch buildCollectionFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardCollectionFetch( + this, + attributeDefinition, + fetchStrategy, + loadPlanBuildingContext + ); + } + + @Override + public EntityFetch buildEntityFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardEntityFetch( + this, + attributeDefinition, + fetchStrategy, + loadPlanBuildingContext + ); + } + + @Override + public CompositeFetch buildCompositeFetch( + CompositionDefinition attributeDefinition, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); + } + + @Override + public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { + EntityKey entityKey = context.getDictatedRootEntityKey(); + if ( entityKey != null ) { + context.getIdentifierResolutionContext( this ).registerEntityKey( entityKey ); + return; + } + + identifierDescription.hydrate( resultSet, context ); + + for ( Fetch fetch : getFetches() ) { + fetch.hydrate( resultSet, context ); + } + } + + @Override + public void resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { + final IdentifierResolutionContext identifierResolutionContext = context.getIdentifierResolutionContext( this ); + EntityKey entityKey = identifierResolutionContext.getEntityKey(); + if ( entityKey == null ) { + return; + } + + entityKey = identifierDescription.resolve( resultSet, context ); + identifierResolutionContext.registerEntityKey( entityKey ); + + for ( Fetch fetch : getFetches() ) { + fetch.resolve( resultSet, context ); + } + } + + @Override + public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { + return null; + } + + @Override + public void injectIdentifierDescription(IdentifierDescription identifierDescription) { + this.identifierDescription = identifierDescription; + } + + @Override + public String toString() { + return "EntityReturn(" + persister.getEntityName() + ")"; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java index 4be5ca8bbc..7213e18336 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java @@ -23,8 +23,12 @@ */ package org.hibernate.loader.plan.spi; +import java.sql.ResultSet; +import java.sql.SQLException; + import org.hibernate.engine.FetchStrategy; import org.hibernate.loader.PropertyPath; +import org.hibernate.loader.spi.ResultSetProcessingContext; /** * Contract for associations that are being fetched. @@ -33,7 +37,7 @@ import org.hibernate.loader.PropertyPath; * * @author Steve Ebersole */ -public interface Fetch extends FetchOwner { +public interface Fetch { /** * Obtain the owner of this fetch. * @@ -56,4 +60,8 @@ public interface Fetch extends FetchOwner { * @return The property path */ public PropertyPath getPropertyPath(); + + public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException; + + public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException; } 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 e28daf1b24..a01e88ab2e 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 @@ -26,6 +26,8 @@ package org.hibernate.loader.plan.spi; import org.hibernate.engine.FetchStrategy; import org.hibernate.loader.PropertyPath; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; +import org.hibernate.persister.walking.spi.CompositionDefinition; /** * Contract for owners of fetches. Any non-scalar return could be a fetch owner. @@ -38,6 +40,14 @@ public interface FetchOwner { */ public static final Fetch[] NO_FETCHES = new Fetch[0]; + /** + * Contract to add fetches to this owner. Care should be taken in calling this method; it is intended + * for Hibernate usage + * + * @param fetch The fetch to add + */ + public void addFetch(Fetch fetch); + /** * Retrieve the fetches owned by this return. * @@ -65,4 +75,19 @@ public interface FetchOwner { * @return The property path */ public PropertyPath getPropertyPath(); + + public CollectionFetch buildCollectionFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext); + + public EntityFetch buildEntityFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext); + + public CompositeFetch buildCompositeFetch( + CompositionDefinition attributeDefinition, + LoadPlanBuildingContext loadPlanBuildingContext); + } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/IdentifierDescription.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/IdentifierDescription.java new file mode 100644 index 0000000000..62d8371196 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/IdentifierDescription.java @@ -0,0 +1,41 @@ +/* + * 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.spi; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.hibernate.engine.spi.EntityKey; +import org.hibernate.loader.spi.ResultSetProcessingContext; + +/** + * @author Steve Ebersole + */ +public interface IdentifierDescription { + public Fetch[] getFetches(); + + public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException; + + public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException; +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/IdentifierDescriptionInjectable.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/IdentifierDescriptionInjectable.java new file mode 100644 index 0000000000..4528bfa605 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/IdentifierDescriptionInjectable.java @@ -0,0 +1,33 @@ +/* + * 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.spi; + +/** + * Ugh + * + * @author Steve Ebersole + */ +public interface IdentifierDescriptionInjectable { + public void injectIdentifierDescription(IdentifierDescription identifierDescription); +} 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 0173b39714..035f6c0a84 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 @@ -55,6 +55,7 @@ public interface LoadPlan { public List getReturns(); + // 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/LoadPlanBuildingContext.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlanBuildingContext.java new file mode 100644 index 0000000000..152133f44c --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlanBuildingContext.java @@ -0,0 +1,48 @@ +/* + * 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.spi; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.CollectionAliases; +import org.hibernate.loader.EntityAliases; +import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; +import org.hibernate.persister.walking.spi.CollectionDefinition; +import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.persister.walking.spi.EntityDefinition; + +/** + * @author Steve Ebersole + */ +public interface LoadPlanBuildingContext { + public SessionFactoryImplementor getSessionFactory(); + + public CollectionAliases resolveCollectionColumnAliases(AssociationAttributeDefinition attributeDefinition); + public EntityAliases resolveEntityColumnAliases(AssociationAttributeDefinition attributeDefinition); + + public String resolveRootSourceAlias(EntityDefinition definition); + public String resolveRootSourceAlias(CollectionDefinition definition); + + public String resolveFetchSourceAlias(AssociationAttributeDefinition attributeDefinition); + public String resolveFetchSourceAlias(CompositionDefinition compositionDefinition); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ReturnVisitationStrategy.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlanVisitationStrategy.java similarity index 97% rename from hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ReturnVisitationStrategy.java rename to hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlanVisitationStrategy.java index 4140249b3a..976177ed33 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ReturnVisitationStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlanVisitationStrategy.java @@ -26,16 +26,16 @@ package org.hibernate.loader.plan.spi; /** * @author Steve Ebersole */ -public interface ReturnVisitationStrategy { +public interface LoadPlanVisitationStrategy { /** * Notification we are preparing to start visitation. */ - public void start(); + public void start(LoadPlan loadPlan); /** * Notification we are finished visitation. */ - public void finish(); + public void finish(LoadPlan loadPlan); /** * Notification that a new root return branch is being started. Will be followed by calls to one of the following diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlanVisitationStrategyAdapter.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlanVisitationStrategyAdapter.java new file mode 100644 index 0000000000..6734ef990c --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlanVisitationStrategyAdapter.java @@ -0,0 +1,104 @@ +/* + * 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.spi; + +/** + * @author Steve Ebersole + */ +public class LoadPlanVisitationStrategyAdapter implements LoadPlanVisitationStrategy { + @Override + public void start(LoadPlan loadPlan) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void finish(LoadPlan loadPlan) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void startingRootReturn(Return rootReturn) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void finishingRootReturn(Return rootReturn) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void handleScalarReturn(ScalarReturn scalarReturn) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void handleEntityReturn(EntityReturn rootEntityReturn) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void handleCollectionReturn(CollectionReturn rootCollectionReturn) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void startingFetches(FetchOwner fetchOwner) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void finishingFetches(FetchOwner fetchOwner) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void startingEntityFetch(EntityFetch entityFetch) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @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. + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ReturnVisitor.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlanVisitor.java similarity index 78% rename from hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ReturnVisitor.java rename to hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlanVisitor.java index cf4aaf6ef9..9a371aa011 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ReturnVisitor.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlanVisitor.java @@ -28,25 +28,25 @@ package org.hibernate.loader.plan.spi; * * @author Steve Ebersole */ -public class ReturnVisitor { - public static void visit(Return[] rootReturns, ReturnVisitationStrategy strategy) { - new ReturnVisitor( strategy ).visitReturns( rootReturns ); +public class LoadPlanVisitor { + public static void visit(LoadPlan loadPlan, LoadPlanVisitationStrategy strategy) { + new LoadPlanVisitor( strategy ).visit( loadPlan ); } - private final ReturnVisitationStrategy strategy; + private final LoadPlanVisitationStrategy strategy; - public ReturnVisitor(ReturnVisitationStrategy strategy) { + public LoadPlanVisitor(LoadPlanVisitationStrategy strategy) { this.strategy = strategy; } - private void visitReturns(Return[] rootReturns) { - strategy.start(); + private void visit(LoadPlan loadPlan) { + strategy.start( loadPlan ); - for ( Return rootReturn : rootReturns ) { + for ( Return rootReturn : loadPlan.getReturns() ) { visitRootReturn( rootReturn ); } - strategy.finish(); + strategy.finish( loadPlan ); } private void visitRootReturn(Return rootReturn) { @@ -69,7 +69,9 @@ public class ReturnVisitor { } else if ( CollectionReturn.class.isInstance( rootReturn ) ) { strategy.handleCollectionReturn( (CollectionReturn) rootReturn ); - visitFetches( (CollectionReturn) rootReturn ); + final CollectionReturn collectionReturn = (CollectionReturn) rootReturn; + visitFetches( collectionReturn.getIndexGraph() ); + visitFetches( collectionReturn.getElementGraph() ); } else { throw new IllegalStateException( @@ -92,17 +94,18 @@ public class ReturnVisitor { private void visitFetch(Fetch fetch) { if ( EntityFetch.class.isInstance( fetch ) ) { strategy.startingEntityFetch( (EntityFetch) fetch ); - visitFetches( fetch ); + visitFetches( (EntityFetch) fetch ); strategy.finishingEntityFetch( (EntityFetch) fetch ); } else if ( CollectionFetch.class.isInstance( fetch ) ) { strategy.startingCollectionFetch( (CollectionFetch) fetch ); - visitFetches( fetch ); + visitFetches( ( (CollectionFetch) fetch ).getIndexGraph() ); + visitFetches( ( (CollectionFetch) fetch ).getElementGraph() ); strategy.finishingCollectionFetch( (CollectionFetch) fetch ); } else if ( CompositeFetch.class.isInstance( fetch ) ) { strategy.startingCompositeFetch( (CompositeFetch) fetch ); - visitFetches( fetch ); + visitFetches( (CompositeFetch) fetch ); strategy.finishingCompositeFetch( (CompositeFetch) fetch ); } else { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Return.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Return.java index b94adfae9d..19c700f57c 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Return.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Return.java @@ -23,12 +23,15 @@ */ package org.hibernate.loader.plan.spi; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.hibernate.loader.spi.ResultSetProcessingContext; + /** * Represents a return value in the query results. Not the same as a result (column) in the JDBC ResultSet! *

- * This is merely a unifying contract; it defines no behavior. - *

- * Return is distinctly different from a {@link Fetch}. + * Return is distinctly different from a {@link Fetch} and so modeled as completely separate hierarchy. * * @see ScalarReturn * @see EntityReturn @@ -37,4 +40,28 @@ package org.hibernate.loader.plan.spi; * @author Steve Ebersole */ public interface Return { + public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException; + + /** + * Effectively performs first phase of two-phase loading. For scalar results first/second phase is one. For + * entities, first phase is to resolve identifiers; second phase is to resolve the entity instances. + * + * @param resultSet The result set being processed + * @param context The context for the processing + * + * @throws SQLException Indicates a problem access the JDBC result set + */ + public void resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException; + + /** + * Essentially performs the second phase of two-phase loading. + * + * @param resultSet The result set being processed + * @param context The context for the processing + * + * @return The read object + * + * @throws SQLException Indicates a problem access the JDBC result set + */ + public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException; } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ScalarReturn.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ScalarReturn.java index 9a3d434bbc..d8a86ef117 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ScalarReturn.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ScalarReturn.java @@ -23,7 +23,12 @@ */ package org.hibernate.loader.plan.spi; +import java.sql.ResultSet; +import java.sql.SQLException; + import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.internal.ResultSetProcessingContextImpl; +import org.hibernate.loader.spi.ResultSetProcessingContext; import org.hibernate.type.Type; /** @@ -34,19 +39,30 @@ import org.hibernate.type.Type; */ public class ScalarReturn extends AbstractPlanNode implements Return { private final Type type; - private final String columnAlias; + private final String[] columnAliases; - public ScalarReturn(SessionFactoryImplementor factory, Type type, String columnAlias) { + public ScalarReturn(SessionFactoryImplementor factory, Type type, String[] columnAliases) { super( factory ); this.type = type; - this.columnAlias = columnAlias; + this.columnAliases = columnAliases; } public Type getType() { return type; } - public String getColumnAlias() { - return columnAlias; + @Override + public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) { + // nothing to do + } + + @Override + public void resolve(ResultSet resultSet, ResultSetProcessingContext context) { + // nothing to do + } + + @Override + public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { + return type.nullSafeGet( resultSet, columnAliases, context.getSession(), null ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/spi/AfterLoadAction.java b/hibernate-core/src/main/java/org/hibernate/loader/spi/AfterLoadAction.java new file mode 100644 index 0000000000..3fa9257b9a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/spi/AfterLoadAction.java @@ -0,0 +1,34 @@ +/* + * 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.spi; + +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.persister.entity.Loadable; + +/** +* @author Steve Ebersole +*/ +public interface AfterLoadAction { + public void afterLoad(SessionImplementor session, Object entity, Loadable persister); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/spi/Loader.java b/hibernate-core/src/main/java/org/hibernate/loader/spi/Loader.java index 28c5cc36be..1513d61a82 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/spi/Loader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/spi/Loader.java @@ -33,28 +33,22 @@ import org.hibernate.loader.plan.spi.LoadPlan; import org.hibernate.transform.ResultTransformer; /** - * Definition of the Loader contract. - *

- * Capabilities I'd like to see added (todo):

    - *
  • - * expose the underlying "query" (although what I see here relies heavily on - * https://github.com/hibernate/hibernate-orm/wiki/Proposal---SQL-generation) - *
  • - *
- * + * Definition of the Loader contract. A Loader is intended to perform loading based on a query and a load-plan. + * Under the covers it uses many delegates to perform that work that might be better used individually in + * different situations. In general, Loader is intended for being fed a set of results and processing through + * all of those result rows in one swoop. For cases that do not fit that template, it is probably better to + * individually use the delegates to perform the work. * * @author Gavin King * @author Steve Ebersole */ public interface Loader { - public LoadPlan getLoadPlan(); - /** - * Obtain the on-demand form of this loader, if possible. + * Obtain the LoadPlan this Loader is following. * - * @return The on-demand version of this loader + * @return */ - public OnDemandLoader asOnDemandLoader(); + public LoadPlan getLoadPlan(); public List extractResults( ResultSet resultSet, diff --git a/hibernate-core/src/main/java/org/hibernate/loader/spi/NamedParameterContext.java b/hibernate-core/src/main/java/org/hibernate/loader/spi/NamedParameterContext.java new file mode 100644 index 0000000000..d8b33e98ee --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/spi/NamedParameterContext.java @@ -0,0 +1,36 @@ +/* + * 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.spi; + +/** + * The context for named parameters. + *

+ * NOTE : the hope with the SQL-redesign stuff is that this whole concept goes away, the idea being that + * the parameters are encoded into the query tree and "bind themselves". + * + * @author Steve Ebersole + */ +public interface NamedParameterContext { + public int[] getNamedParameterLocations(String name); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/spi/OnDemandLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/spi/OnDemandResultSetProcessor.java similarity index 74% rename from hibernate-core/src/main/java/org/hibernate/loader/spi/OnDemandLoader.java rename to hibernate-core/src/main/java/org/hibernate/loader/spi/OnDemandResultSetProcessor.java index 1a87626f76..27ba73b5a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/spi/OnDemandLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/spi/OnDemandResultSetProcessor.java @@ -1,7 +1,7 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * Copyright (c) 2012, 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. @@ -25,22 +25,24 @@ package org.hibernate.loader.spi; import java.sql.ResultSet; -import org.hibernate.HibernateException; import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.SessionImplementor; /** - * Represents an on-demand loading strategy as need for processing single *logical* rows one at a time as required - * for {@link org.hibernate.ScrollableResults} implementations. + * Contract for processing JDBC ResultSets a single logical row at a time. These are intended for use by + * {@link org.hibernate.ScrollableResults} implementations. + * + * NOTE : these methods initially taken directly from {@link org.hibernate.loader.Loader} counterparts in an effort + * to break Loader into manageable pieces, especially in regards to the processing of result sets. * * @author Steve Ebersole */ -public interface OnDemandLoader { +public interface OnDemandResultSetProcessor { /** - * Given a ResultSet, extract just a single result row. + * Give a ResultSet, extract just a single result row. * - * Copy of {@link org.hibernate.loader.Loader#loadSingleRow(ResultSet, SessionImplementor, QueryParameters, boolean)} + * Copy of {@link org.hibernate.loader.Loader#loadSingleRow(java.sql.ResultSet, org.hibernate.engine.spi.SessionImplementor, org.hibernate.engine.spi.QueryParameters, boolean)} * but dropping the 'returnProxies' (that method has only one use in the entire codebase and it always passes in * false...) * @@ -50,19 +52,19 @@ public interface OnDemandLoader { * * @return The extracted result row * - * @throws HibernateException Indicates a problem extracting values from the result set. + * @throws org.hibernate.HibernateException Indicates a problem extracting values from the result set. */ public Object extractSingleRow( ResultSet resultSet, SessionImplementor session, - QueryParameters queryParameters) throws HibernateException; + QueryParameters queryParameters); /** * Given a ResultSet extract "sequential rows". This is used in cases where we have multi-row fetches that * are sequential within the ResultSet due to ordering. Multiple ResultSet rows are read into a single query * result "row". * - * Copy of {@link org.hibernate.loader.Loader#loadSequentialRowsForward(ResultSet, SessionImplementor, QueryParameters, boolean)} + * Copy of {@link org.hibernate.loader.Loader#loadSequentialRowsForward(java.sql.ResultSet, org.hibernate.engine.spi.SessionImplementor, org.hibernate.engine.spi.QueryParameters, boolean)} * but dropping the 'returnProxies' (that method has only one use in the entire codebase and it always passes in * false...) * @@ -77,12 +79,12 @@ public interface OnDemandLoader { public Object extractSequentialRowsForward( final ResultSet resultSet, final SessionImplementor session, - final QueryParameters queryParameters) throws HibernateException; + final QueryParameters queryParameters); /** * Like {@link #extractSequentialRowsForward} but here moving back through the ResultSet. * - * Copy of {@link org.hibernate.loader.Loader#loadSequentialRowsReverse(ResultSet, SessionImplementor, QueryParameters, boolean, boolean)} + * Copy of {@link org.hibernate.loader.Loader#loadSequentialRowsReverse(java.sql.ResultSet, org.hibernate.engine.spi.SessionImplementor, org.hibernate.engine.spi.QueryParameters, boolean, boolean)} * but dropping the 'returnProxies' (that method has only one use in the entire codebase and it always passes in * false...). * @@ -101,5 +103,5 @@ public interface OnDemandLoader { ResultSet resultSet, SessionImplementor session, QueryParameters queryParameters, - boolean isLogicallyAfterLast) throws HibernateException; + boolean isLogicallyAfterLast); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/spi/ResultBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/spi/ResultBuilder.java new file mode 100644 index 0000000000..157e6e22d8 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/spi/ResultBuilder.java @@ -0,0 +1,7 @@ +package org.hibernate.loader.spi; + +/** + * @author Steve Ebersole + */ +public class ResultBuilder { +} 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 new file mode 100644 index 0000000000..6af2fb8da1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/spi/ResultSetProcessingContext.java @@ -0,0 +1,91 @@ +/* + * 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.spi; + +import java.io.Serializable; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +import org.hibernate.LockMode; +import org.hibernate.engine.spi.EntityKey; +import org.hibernate.engine.spi.QueryParameters; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.loader.EntityAliases; +import org.hibernate.loader.plan.spi.EntityReference; +import org.hibernate.loader.plan.spi.Return; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.type.AssociationType; +import org.hibernate.type.EntityType; + +/** + * @author Steve Ebersole + */ +public interface ResultSetProcessingContext { + public SessionImplementor getSession(); + + public QueryParameters getQueryParameters(); + + public EntityKey getDictatedRootEntityKey(); + + public IdentifierResolutionContext getIdentifierResolutionContext(EntityReference entityReference); + + public static interface IdentifierResolutionContext { + public EntityReference getEntityReference(); + + public void registerHydratedForm(Serializable hydratedForm); + + public Serializable getHydratedForm(); + + public void registerEntityKey(EntityKey entityKey); + + public EntityKey getEntityKey(); + } + + public void registerHydratedEntity(EntityPersister persister, EntityKey entityKey, Object entityInstance); + + public void checkVersion( + ResultSet resultSet, + EntityPersister persister, + EntityAliases entityAliases, + EntityKey entityKey, + Object entityInstance) throws SQLException; + + public String getConcreteEntityTypeName( + ResultSet resultSet, + EntityPersister persister, + EntityAliases entityAliases, + EntityKey entityKey) throws SQLException; + + public void loadFromResultSet( + ResultSet resultSet, + Object entityInstance, + String concreteEntityTypeName, + EntityKey entityKey, + EntityAliases entityAliases, + LockMode acquiredLockMode, + EntityPersister persister, + boolean eagerFetch, + EntityType associationType) throws SQLException; +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/spi/ResultSetProcessor.java b/hibernate-core/src/main/java/org/hibernate/loader/spi/ResultSetProcessor.java new file mode 100644 index 0000000000..4dc630c469 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/spi/ResultSetProcessor.java @@ -0,0 +1,70 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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.spi; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +import org.hibernate.engine.spi.QueryParameters; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.transform.ResultTransformer; + +/** + * Contract for processing JDBC ResultSets. Separated because ResultSets can be chained and we'd really like to + * reuse this logic across all result sets. + *

+ * todo : investigate having this work with non-JDBC results; maybe just typed as Object? or a special Result contract? + * + * @author Steve Ebersole + */ +public interface ResultSetProcessor { + + public OnDemandResultSetProcessor toOnDemandForm(); + + /** + * Process an entire ResultSet, performing all extractions. + * + * Semi-copy of {@link org.hibernate.loader.Loader#doQuery}, with focus on just the ResultSet processing bit. + * + * @param resultSet The result set being processed. + * @param session The originating session + * @param queryParameters The "parameters" used to build the query + * @param returnProxies Can proxies be returned (not the same as can they be created!) + * @param forcedResultTransformer My old "friend" ResultTransformer... + * + * @return The extracted results list. + * + * @throws java.sql.SQLException Indicates a problem access the JDBC ResultSet + */ + public List extractResults( + ResultSet resultSet, + SessionImplementor session, + QueryParameters queryParameters, + NamedParameterContext namedParameterContext, + boolean returnProxies, + boolean readOnly, + ResultTransformer forcedResultTransformer, + List afterLoadActions) throws SQLException; +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java index 0db82ac72c..3503962eea 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java @@ -82,7 +82,7 @@ import org.hibernate.persister.entity.Queryable; import org.hibernate.persister.walking.spi.CollectionDefinition; import org.hibernate.persister.walking.spi.CollectionElementDefinition; import org.hibernate.persister.walking.spi.CollectionIndexDefinition; -import org.hibernate.persister.walking.spi.CompositeDefinition; +import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.pretty.MessageHelper; import org.hibernate.sql.Alias; @@ -1974,7 +1974,7 @@ public abstract class AbstractCollectionPersister } @Override - public CompositeDefinition toCompositeDefinition() { + public CompositionDefinition toCompositeDefinition() { if ( ! getType().isComponentType() ) { throw new IllegalStateException( "Cannot treat entity collection index type as composite" ); } @@ -2006,7 +2006,7 @@ public abstract class AbstractCollectionPersister } @Override - public CompositeDefinition toCompositeDefinition() { + public CompositionDefinition toCompositeDefinition() { if ( ! getType().isComponentType() ) { throw new IllegalStateException( "Cannot treat entity collection element type as composite" ); } 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 5456404f04..5f4c423a2b 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 @@ -110,6 +110,11 @@ import org.hibernate.metamodel.binding.SingularAttributeBinding; import org.hibernate.metamodel.relational.DerivedValue; import org.hibernate.metamodel.relational.Value; import org.hibernate.persister.walking.spi.AttributeDefinition; +import org.hibernate.persister.walking.spi.AttributeSource; +import org.hibernate.persister.walking.spi.EncapsulatedEntityIdentifierDefinition; +import org.hibernate.persister.walking.spi.EntityDefinition; +import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; +import org.hibernate.persister.walking.spi.NonEncapsulatedEntityIdentifierDefinition; import org.hibernate.pretty.MessageHelper; import org.hibernate.property.BackrefPropertyAccessor; import org.hibernate.sql.Alias; @@ -5076,11 +5081,12 @@ public abstract class AbstractEntityPersister // EntityDefinition impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + private EntityIdentifierDefinition entityIdentifierDefinition; private Iterable embeddedCompositeIdentifierAttributes; private Iterable attributeDefinitions; protected void generateEntityDefinition() { - collectEmbeddedCompositeIdentifierAttributeDefinitions(); + prepareEntityIdentifierDefinition(); collectAttributeDefinitions(); } @@ -5090,8 +5096,8 @@ public abstract class AbstractEntityPersister } @Override - public Iterable getEmbeddedCompositeIdentifierAttributes() { - return embeddedCompositeIdentifierAttributes; + public EntityIdentifierDefinition getEntityKeyDefinition() { + return entityIdentifierDefinition; } @Override @@ -5099,52 +5105,85 @@ public abstract class AbstractEntityPersister return attributeDefinitions; } - private synchronized void collectEmbeddedCompositeIdentifierAttributeDefinitions() { + + private void prepareEntityIdentifierDefinition() { final Type idType = getIdentifierType(); + if ( !idType.isComponentType() ) { + entityIdentifierDefinition = buildEncapsulatedIdentifierDefinition(); return; } final CompositeType cidType = (CompositeType) idType; if ( !cidType.isEmbedded() ) { + entityIdentifierDefinition = buildEncapsulatedIdentifierDefinition(); return; } - // we have an embedded composite identifier. Most likely we need to process the composite - // properties separately, although there is an edge case where the identifier is really - // a simple identifier (single value) wrapped in a JPA @IdClass or even in the case of a - // a simple identifier (single value) wrapped in a Hibernate composite type. - // - // We really do not have a built-in method to determine that. However, generally the - // persister would report that there is single, physical identifier property which is - // explicitly at odds with the notion of "embedded composite". So we use that for now - if ( getEntityMetamodel().getIdentifierProperty().isEmbedded() ) { - this.embeddedCompositeIdentifierAttributes = new Iterable() { - @Override - public Iterator iterator() { - return new Iterator() { - private final int numberOfAttributes = countSubclassProperties(); - private int currentAttributeNumber = 0; + entityIdentifierDefinition = new NonEncapsulatedEntityIdentifierDefinition() { + @Override + public Iterable getAttributes() { + // todo : implement + throw new NotYetImplementedException(); + } - @Override - public boolean hasNext() { - return currentAttributeNumber < numberOfAttributes; - } + @Override + public Class getSeparateIdentifierMappingClass() { + // todo : implement + throw new NotYetImplementedException(); + } - @Override - public AttributeDefinition next() { - // todo : implement - throw new NotYetImplementedException(); - } + @Override + public boolean isEncapsulated() { + return false; + } - @Override - public void remove() { - throw new UnsupportedOperationException( "Remove operation not supported here" ); - } - }; - } - }; - } + @Override + public EntityDefinition getEntityDefinition() { + return AbstractEntityPersister.this; + } + }; + } + + private EntityIdentifierDefinition buildEncapsulatedIdentifierDefinition() { + final AttributeDefinition simpleIdentifierAttributeAdapter = new AttributeDefinition() { + @Override + public String getName() { + return entityMetamodel.getIdentifierProperty().getName(); + } + + @Override + public Type getType() { + return entityMetamodel.getIdentifierProperty().getType(); + } + + @Override + public AttributeSource getSource() { + return AbstractEntityPersister.this; + } + + @Override + public String toString() { + return ""; + } + }; + + return new EncapsulatedEntityIdentifierDefinition() { + @Override + public AttributeDefinition getAttributeDefinition() { + return simpleIdentifierAttributeAdapter; + } + + @Override + public boolean isEncapsulated() { + return true; + } + + @Override + public EntityDefinition getEntityDefinition() { + return AbstractEntityPersister.this; + } + }; } private void collectAttributeDefinitions() { diff --git a/hibernate-core/src/main/java/org/hibernate/persister/spi/HydratedCompoundValueHandler.java b/hibernate-core/src/main/java/org/hibernate/persister/spi/HydratedCompoundValueHandler.java new file mode 100644 index 0000000000..4c723a6b2f --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/spi/HydratedCompoundValueHandler.java @@ -0,0 +1,44 @@ +/* + * 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.persister.spi; + +/** + * Where to begin... :) + * + * This gets to the internal concept of 2-phase loading of entity data and how specifically it is done. Essentially + * for composite values, the process of hydration results in a tuple array comprising the composition "atomic" values. + * For example, a Name component's hydrated state might look like {@code ["Steve", "L", "Ebersole"]}. + * + * There are times when we need to be able to extract individual pieces out of the hydrated tuple array. For example, + * for an entity with a composite identifier part of which is an association (a key-many-to-one) we often need to + * attempt 2-phase processing on the association portion of the identifier's hydrated tuple array. + * + * This contract allows us access to portions of the hydrated tuple state. + * + * @author Steve Ebersole + */ +public interface HydratedCompoundValueHandler { + public Object extract(Object hydratedState); + public void inject(Object hydratedState, Object value); +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationAttributeDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationAttributeDefinition.java index 383b3d4e38..719eb51803 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationAttributeDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationAttributeDefinition.java @@ -27,6 +27,7 @@ import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.loader.PropertyPath; +import org.hibernate.persister.spi.HydratedCompoundValueHandler; /** * @author Steve Ebersole @@ -43,4 +44,6 @@ public interface AssociationAttributeDefinition extends AttributeDefinition { public FetchStrategy determineFetchPlan(LoadQueryInfluencers loadQueryInfluencers, PropertyPath propertyPath); public CascadeStyle determineCascadeStyle(); + + public HydratedCompoundValueHandler getHydratedCompoundValueExtractor(); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java index 1cb649b0d8..285ec22270 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java @@ -40,11 +40,20 @@ public interface AssociationVisitationStrategy { public void startingEntity(EntityDefinition entityDefinition); public void finishingEntity(EntityDefinition entityDefinition); + public void startingEntityIdentifier(EntityIdentifierDefinition entityIdentifierDefinition); + public void finishingEntityIdentifier(EntityIdentifierDefinition entityIdentifierDefinition); + public void startingCollection(CollectionDefinition collectionDefinition); public void finishingCollection(CollectionDefinition collectionDefinition); - public void startingComposite(CompositeDefinition compositeDefinition); - public void finishingComposite(CompositeDefinition compositeDefinition); + public void startingCollectionIndex(CollectionIndexDefinition collectionIndexDefinition); + public void finishingCollectionIndex(CollectionIndexDefinition collectionIndexDefinition); + + public void startingCollectionElements(CollectionElementDefinition elementDefinition); + public void finishingCollectionElements(CollectionElementDefinition elementDefinition); + + public void startingComposite(CompositionDefinition compositionDefinition); + public void finishingComposite(CompositionDefinition compositionDefinition); public boolean startingAttribute(AttributeDefinition attributeDefinition); public void finishingAttribute(AttributeDefinition attributeDefinition); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionElementDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionElementDefinition.java index fd48366c3a..b42f8b0ddc 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionElementDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionElementDefinition.java @@ -35,5 +35,5 @@ public interface CollectionElementDefinition { public EntityDefinition toEntityDefinition(); - public CompositeDefinition toCompositeDefinition(); + public CompositionDefinition toCompositeDefinition(); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionIndexDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionIndexDefinition.java index 700e2b5070..ece06e6e36 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionIndexDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionIndexDefinition.java @@ -35,5 +35,5 @@ public interface CollectionIndexDefinition { public EntityDefinition toEntityDefinition(); - public CompositeDefinition toCompositeDefinition(); + public CompositionDefinition toCompositeDefinition(); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositeDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositionDefinition.java similarity index 92% rename from hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositeDefinition.java rename to hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositionDefinition.java index 51c88333f4..e9d7c251cf 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositeDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositionDefinition.java @@ -26,5 +26,5 @@ package org.hibernate.persister.walking.spi; /** * @author Steve Ebersole */ -public interface CompositeDefinition extends AttributeDefinition, AttributeSource { +public interface CompositionDefinition extends AttributeDefinition, AttributeSource { } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/EncapsulatedEntityIdentifierDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/EncapsulatedEntityIdentifierDefinition.java new file mode 100644 index 0000000000..657f2da50d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/EncapsulatedEntityIdentifierDefinition.java @@ -0,0 +1,31 @@ +/* + * 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.persister.walking.spi; + +/** + * @author Steve Ebersole + */ +public interface EncapsulatedEntityIdentifierDefinition extends EntityIdentifierDefinition { + public AttributeDefinition getAttributeDefinition(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/EntityDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/EntityDefinition.java index 1e02b42106..38faf26c4a 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/EntityDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/EntityDefinition.java @@ -32,5 +32,5 @@ import org.hibernate.persister.entity.EntityPersister; */ public interface EntityDefinition extends AttributeSource { public EntityPersister getEntityPersister(); - public Iterable getEmbeddedCompositeIdentifierAttributes(); + public EntityIdentifierDefinition getEntityKeyDefinition(); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/EntityIdentifierDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/EntityIdentifierDefinition.java new file mode 100644 index 0000000000..8ecb616a64 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/EntityIdentifierDefinition.java @@ -0,0 +1,43 @@ +/* + * 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.persister.walking.spi; + +/** + * Describes aspects of the identifier for an entity + * + * @author Steve Ebersole + */ +public interface EntityIdentifierDefinition { + /** + * Is the entity identifier encapsulated? Meaning, is it represented by a single attribute? + * + * @return {@code true} indicates the identifier is encapsulated (and therefore this is castable to + * {@link EncapsulatedEntityIdentifierDefinition}); {@code false} means it is not encapsulated (and therefore + * castable to {@link NonEncapsulatedEntityIdentifierDefinition}). + * + */ + public boolean isEncapsulated(); + + public EntityDefinition getEntityDefinition(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java index ccbbceb6bb..dd205aa9e8 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java @@ -81,26 +81,26 @@ public class MetadataDrivenModelGraphVisitor { private void visitEntityDefinition(EntityDefinition entityDefinition) { strategy.startingEntity( entityDefinition ); - try { - visitAttributes( entityDefinition ); - optionallyVisitEmbeddedCompositeIdentifier( entityDefinition ); - } - finally { - strategy.finishingEntity( entityDefinition ); - } + + visitAttributes( entityDefinition ); + visitIdentifierDefinition( entityDefinition.getEntityKeyDefinition() ); + + strategy.finishingEntity( entityDefinition ); } - private void optionallyVisitEmbeddedCompositeIdentifier(EntityDefinition entityDefinition) { - // if the entity has a composite identifier, see if we need to handle its sub-properties separately - final Iterable embeddedCompositeIdentifierAttributes = - entityDefinition.getEmbeddedCompositeIdentifierAttributes(); - if ( embeddedCompositeIdentifierAttributes == null ) { - return; + private void visitIdentifierDefinition(EntityIdentifierDefinition entityIdentifierDefinition) { + strategy.startingEntityIdentifier( entityIdentifierDefinition ); + + if ( entityIdentifierDefinition.isEncapsulated() ) { + visitAttributeDefinition( ( (EncapsulatedEntityIdentifierDefinition) entityIdentifierDefinition).getAttributeDefinition() ); + } + else { + for ( AttributeDefinition attributeDefinition : ( (NonEncapsulatedEntityIdentifierDefinition) entityIdentifierDefinition).getAttributes() ) { + visitAttributeDefinition( attributeDefinition ); + } } - for ( AttributeDefinition attributeDefinition : embeddedCompositeIdentifierAttributes ) { - visitAttributeDefinition( attributeDefinition ); - } + strategy.finishingEntityIdentifier( entityIdentifierDefinition ); } private void visitAttributes(AttributeSource attributeSource) { @@ -122,7 +122,7 @@ public class MetadataDrivenModelGraphVisitor { visitAssociation( (AssociationAttributeDefinition) attributeDefinition ); } else if ( attributeDefinition.getType().isComponentType() ) { - visitCompositeDefinition( (CompositeDefinition) attributeDefinition ); + visitCompositeDefinition( (CompositionDefinition) attributeDefinition ); } } finally { @@ -148,42 +148,34 @@ public class MetadataDrivenModelGraphVisitor { } } - private void visitCompositeDefinition(CompositeDefinition compositeDefinition) { - strategy.startingComposite( compositeDefinition ); - try { - visitAttributes( compositeDefinition ); - } - finally { - strategy.finishingComposite( compositeDefinition ); - } + private void visitCompositeDefinition(CompositionDefinition compositionDefinition) { + strategy.startingComposite( compositionDefinition ); + + visitAttributes( compositionDefinition ); + + strategy.finishingComposite( compositionDefinition ); } private void visitCollectionDefinition(CollectionDefinition collectionDefinition) { strategy.startingCollection( collectionDefinition ); - try { - visitCollectionIndex( collectionDefinition.getIndexDefinition() ); + visitCollectionIndex( collectionDefinition ); + visitCollectionElements( collectionDefinition ); - final CollectionElementDefinition elementDefinition = collectionDefinition.getElementDefinition(); - if ( elementDefinition.getType().isComponentType() ) { - visitCompositeDefinition( elementDefinition.toCompositeDefinition() ); - } - else { - visitEntityDefinition( elementDefinition.toEntityDefinition() ); - } - } - finally { - strategy.finishingCollection( collectionDefinition ); - } + strategy.finishingCollection( collectionDefinition ); } - private void visitCollectionIndex(CollectionIndexDefinition collectionIndexDefinition) { + private void visitCollectionIndex(CollectionDefinition collectionDefinition) { + final CollectionIndexDefinition collectionIndexDefinition = collectionDefinition.getIndexDefinition(); if ( collectionIndexDefinition == null ) { return; } - log.debug( "Visiting collection index : " + currentPropertyPath.getFullPath() ); - currentPropertyPath = currentPropertyPath.append( "" ); + strategy.startingCollectionIndex( collectionIndexDefinition ); + + log.debug( "Visiting index for collection : " + currentPropertyPath.getFullPath() ); + currentPropertyPath = currentPropertyPath.append( "" ); + try { final Type collectionIndexType = collectionIndexDefinition.getType(); if ( collectionIndexType.isComponentType() ) { @@ -196,6 +188,22 @@ public class MetadataDrivenModelGraphVisitor { finally { currentPropertyPath = currentPropertyPath.getParent(); } + + strategy.finishingCollectionIndex( collectionIndexDefinition ); + } + + private void visitCollectionElements(CollectionDefinition collectionDefinition) { + final CollectionElementDefinition elementDefinition = collectionDefinition.getElementDefinition(); + strategy.startingCollectionElements( elementDefinition ); + + if ( elementDefinition.getType().isComponentType() ) { + visitCompositeDefinition( elementDefinition.toCompositeDefinition() ); + } + else { + visitEntityDefinition( elementDefinition.toEntityDefinition() ); + } + + strategy.finishingCollectionElements( elementDefinition ); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/NonEncapsulatedEntityIdentifierDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/NonEncapsulatedEntityIdentifierDefinition.java new file mode 100644 index 0000000000..4a07579867 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/NonEncapsulatedEntityIdentifierDefinition.java @@ -0,0 +1,33 @@ +/* + * 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.persister.walking.spi; + +/** + * @author Steve Ebersole + */ +public interface NonEncapsulatedEntityIdentifierDefinition extends EntityIdentifierDefinition { + public Iterable getAttributes(); + + public Class getSeparateIdentifierMappingClass(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/WalkingException.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/WalkingException.java new file mode 100644 index 0000000000..881c32c392 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/WalkingException.java @@ -0,0 +1,41 @@ +/* + * 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.persister.walking.spi; + +import org.hibernate.HibernateError; + +/** + * Indicates a problem walking the domain tree. Almost always this indicates an internal error in Hibernate + * + * @author Steve Ebersole + */ +public class WalkingException extends HibernateError { + public WalkingException(String message) { + super( message ); + } + + public WalkingException(String message, Throwable root) { + super( message, root ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/result/internal/ResultImpl.java b/hibernate-core/src/main/java/org/hibernate/result/internal/ResultImpl.java index fe405ec56f..9377303efa 100644 --- a/hibernate-core/src/main/java/org/hibernate/result/internal/ResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/result/internal/ResultImpl.java @@ -37,6 +37,7 @@ import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.loader.custom.CustomLoader; import org.hibernate.loader.custom.CustomQuery; import org.hibernate.loader.custom.sql.SQLQueryReturnProcessor; +import org.hibernate.loader.spi.AfterLoadAction; import org.hibernate.result.NoMoreReturnsException; import org.hibernate.result.Result; import org.hibernate.result.Return; diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/AbstractNonIdentifierAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/AbstractNonIdentifierAttribute.java index b01458fe64..9b9a161370 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/AbstractNonIdentifierAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/AbstractNonIdentifierAttribute.java @@ -126,8 +126,12 @@ public abstract class AbstractNonIdentifierAttribute extends AbstractAttribute i return attributeInformation.getFetchMode(); } + protected String loggableMetadata() { + return "non-identifier"; + } + @Override public String toString() { - return "Attribute[non-identifier]( " + getName() + ")"; + return "Attribute(name=" + getName() + ", type=" + getType().getName() + " [" + loggableMetadata() + "])"; } } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java b/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java index 8db4f2048e..977ab5350c 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java @@ -54,7 +54,7 @@ import org.hibernate.property.PropertyAccessor; import org.hibernate.property.PropertyAccessorFactory; import org.hibernate.tuple.entity.EntityBasedAssociationAttribute; import org.hibernate.tuple.entity.EntityBasedBasicAttribute; -import org.hibernate.tuple.entity.EntityBasedCompositeAttribute; +import org.hibernate.tuple.entity.EntityBasedCompositionAttribute; import org.hibernate.tuple.entity.VersionProperty; import org.hibernate.type.AssociationType; import org.hibernate.type.CompositeType; @@ -282,7 +282,7 @@ public class PropertyFactory { ); } case COMPOSITE: { - return new EntityBasedCompositeAttribute( + return new EntityBasedCompositionAttribute( persister, sessionFactory, attributeNumber, diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeBasedAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeBasedAttribute.java index 5c62859939..a225b8a05f 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeBasedAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeBasedAttribute.java @@ -39,7 +39,7 @@ public abstract class AbstractCompositeBasedAttribute private final int ownerAttributeNumber; public AbstractCompositeBasedAttribute( - AbstractCompositeDefinition source, + AbstractCompositionDefinition source, SessionFactoryImplementor sessionFactory, int attributeNumber, String attributeName, @@ -55,7 +55,7 @@ public abstract class AbstractCompositeBasedAttribute } @Override - public AbstractCompositeDefinition getSource() { - return (AbstractCompositeDefinition) super.getSource(); + public AbstractCompositionDefinition getSource() { + return (AbstractCompositionDefinition) super.getSource(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeDefinition.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionDefinition.java similarity index 76% rename from hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeDefinition.java rename to hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionDefinition.java index 17b4b0ccb1..0584f843ff 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionDefinition.java @@ -32,7 +32,7 @@ import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.persister.walking.spi.AssociationKey; import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.AttributeSource; -import org.hibernate.persister.walking.spi.CompositeDefinition; +import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.tuple.AbstractNonIdentifierAttribute; import org.hibernate.tuple.BaselineAttributeInformation; @@ -48,8 +48,9 @@ import static org.hibernate.engine.internal.JoinHelper.getRHSColumnNames; /** * @author Steve Ebersole */ -public abstract class AbstractCompositeDefinition extends AbstractNonIdentifierAttribute implements CompositeDefinition { - protected AbstractCompositeDefinition( +public abstract class AbstractCompositionDefinition extends AbstractNonIdentifierAttribute implements + CompositionDefinition { + protected AbstractCompositionDefinition( AttributeSource source, SessionFactoryImplementor sessionFactory, int attributeNumber, @@ -119,41 +120,41 @@ public abstract class AbstractCompositeDefinition extends AbstractNonIdentifierA } return new CompositeBasedAssociationAttribute( - AbstractCompositeDefinition.this, + AbstractCompositionDefinition.this, sessionFactory(), currentAttributeNumber, name, (AssociationType) type, new BaselineAttributeInformation.Builder() - .setInsertable( AbstractCompositeDefinition.this.isInsertable() ) - .setUpdateable( AbstractCompositeDefinition.this.isUpdateable() ) - .setInsertGenerated( AbstractCompositeDefinition.this.isInsertGenerated() ) - .setUpdateGenerated( AbstractCompositeDefinition.this.isUpdateGenerated() ) + .setInsertable( AbstractCompositionDefinition.this.isInsertable() ) + .setUpdateable( AbstractCompositionDefinition.this.isUpdateable() ) + .setInsertGenerated( AbstractCompositionDefinition.this.isInsertGenerated() ) + .setUpdateGenerated( AbstractCompositionDefinition.this.isUpdateGenerated() ) .setNullable( getType().getPropertyNullability()[currentAttributeNumber] ) .setDirtyCheckable( true ) - .setVersionable( AbstractCompositeDefinition.this.isVersionable() ) + .setVersionable( AbstractCompositionDefinition.this.isVersionable() ) .setCascadeStyle( getType().getCascadeStyle( currentAttributeNumber ) ) .setFetchMode( getType().getFetchMode( currentAttributeNumber ) ) .createInformation(), - AbstractCompositeDefinition.this.attributeNumber(), + AbstractCompositionDefinition.this.attributeNumber(), associationKey ); } else if ( type.isComponentType() ) { - return new CompositeBasedCompositeAttribute( - AbstractCompositeDefinition.this, + return new CompositionBasedCompositionAttribute( + AbstractCompositionDefinition.this, sessionFactory(), currentAttributeNumber, name, (CompositeType) type, new BaselineAttributeInformation.Builder() - .setInsertable( AbstractCompositeDefinition.this.isInsertable() ) - .setUpdateable( AbstractCompositeDefinition.this.isUpdateable() ) - .setInsertGenerated( AbstractCompositeDefinition.this.isInsertGenerated() ) - .setUpdateGenerated( AbstractCompositeDefinition.this.isUpdateGenerated() ) + .setInsertable( AbstractCompositionDefinition.this.isInsertable() ) + .setUpdateable( AbstractCompositionDefinition.this.isUpdateable() ) + .setInsertGenerated( AbstractCompositionDefinition.this.isInsertGenerated() ) + .setUpdateGenerated( AbstractCompositionDefinition.this.isUpdateGenerated() ) .setNullable( getType().getPropertyNullability()[currentAttributeNumber] ) .setDirtyCheckable( true ) - .setVersionable( AbstractCompositeDefinition.this.isVersionable() ) + .setVersionable( AbstractCompositionDefinition.this.isVersionable() ) .setCascadeStyle( getType().getCascadeStyle( currentAttributeNumber ) ) .setFetchMode( getType().getFetchMode( currentAttributeNumber ) ) .createInformation() @@ -161,19 +162,19 @@ public abstract class AbstractCompositeDefinition extends AbstractNonIdentifierA } else { return new CompositeBasedBasicAttribute( - AbstractCompositeDefinition.this, + AbstractCompositionDefinition.this, sessionFactory(), currentAttributeNumber, name, type, new BaselineAttributeInformation.Builder() - .setInsertable( AbstractCompositeDefinition.this.isInsertable() ) - .setUpdateable( AbstractCompositeDefinition.this.isUpdateable() ) - .setInsertGenerated( AbstractCompositeDefinition.this.isInsertGenerated() ) - .setUpdateGenerated( AbstractCompositeDefinition.this.isUpdateGenerated() ) + .setInsertable( AbstractCompositionDefinition.this.isInsertable() ) + .setUpdateable( AbstractCompositionDefinition.this.isUpdateable() ) + .setInsertGenerated( AbstractCompositionDefinition.this.isInsertGenerated() ) + .setUpdateGenerated( AbstractCompositionDefinition.this.isUpdateGenerated() ) .setNullable( getType().getPropertyNullability()[currentAttributeNumber] ) .setDirtyCheckable( true ) - .setVersionable( AbstractCompositeDefinition.this.isVersionable() ) + .setVersionable( AbstractCompositionDefinition.this.isVersionable() ) .setCascadeStyle( getType().getCascadeStyle( currentAttributeNumber ) ) .setFetchMode( getType().getFetchMode( currentAttributeNumber ) ) .createInformation() @@ -195,8 +196,13 @@ public abstract class AbstractCompositeDefinition extends AbstractNonIdentifierA return ( (EntityDefinition) getSource() ).getEntityPersister(); } else { - return ( (AbstractCompositeDefinition) getSource() ).locateOwningPersister(); + return ( (AbstractCompositionDefinition) getSource() ).locateOwningPersister(); } } + + @Override + protected String loggableMetadata() { + return super.loggableMetadata() + ",composition"; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java index 0a8be97a92..eb7224d94d 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java @@ -23,6 +23,8 @@ */ package org.hibernate.tuple.component; +import java.io.Serializable; + import org.hibernate.FetchMode; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStyle; @@ -34,6 +36,7 @@ import org.hibernate.loader.PropertyPath; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Joinable; +import org.hibernate.persister.spi.HydratedCompoundValueHandler; import org.hibernate.persister.walking.internal.Helper; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.AssociationKey; @@ -54,7 +57,7 @@ public class CompositeBasedAssociationAttribute private Joinable joinable; public CompositeBasedAssociationAttribute( - AbstractCompositeDefinition source, + AbstractCompositionDefinition source, SessionFactoryImplementor factory, int attributeNumber, String attributeName, @@ -154,4 +157,29 @@ public class CompositeBasedAssociationAttribute final CompositeType compositeType = (CompositeType) locateOwningPersister().getPropertyType( getName() ); return compositeType.getCascadeStyle( attributeNumber() ); } + + private HydratedCompoundValueHandler hydratedCompoundValueHandler; + + @Override + public HydratedCompoundValueHandler getHydratedCompoundValueExtractor() { + if ( hydratedCompoundValueHandler == null ) { + hydratedCompoundValueHandler = new HydratedCompoundValueHandler() { + @Override + public Object extract(Object hydratedState) { + return ( (Object[] ) hydratedState )[ attributeNumber() ]; + } + + @Override + public void inject(Object hydratedState, Object value) { + ( (Object[] ) hydratedState )[ attributeNumber() ] = value; + } + }; + } + return hydratedCompoundValueHandler; + } + + @Override + protected String loggableMetadata() { + return super.loggableMetadata() + ",association"; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedCompositeAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositionBasedCompositionAttribute.java similarity index 85% rename from hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedCompositeAttribute.java rename to hibernate-core/src/main/java/org/hibernate/tuple/component/CompositionBasedCompositionAttribute.java index 4094ededd1..7beee25984 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedCompositeAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositionBasedCompositionAttribute.java @@ -24,18 +24,18 @@ package org.hibernate.tuple.component; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.persister.walking.spi.CompositeDefinition; +import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.tuple.BaselineAttributeInformation; import org.hibernate.type.CompositeType; /** * @author Steve Ebersole */ -public class CompositeBasedCompositeAttribute - extends AbstractCompositeDefinition - implements CompositeDefinition { - public CompositeBasedCompositeAttribute( - CompositeDefinition source, +public class CompositionBasedCompositionAttribute + extends AbstractCompositionDefinition + implements CompositionDefinition { + public CompositionBasedCompositionAttribute( + CompositionDefinition source, SessionFactoryImplementor sessionFactory, int attributeNumber, String attributeName, diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedAssociationAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedAssociationAttribute.java index 7177552bbd..d5641ebd87 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedAssociationAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedAssociationAttribute.java @@ -33,6 +33,7 @@ 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.persister.spi.HydratedCompoundValueHandler; import org.hibernate.persister.walking.internal.Helper; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.AssociationKey; @@ -152,4 +153,29 @@ public class EntityBasedAssociationAttribute public CascadeStyle determineCascadeStyle() { return getSource().getEntityPersister().getPropertyCascadeStyles()[attributeNumber()]; } + + private HydratedCompoundValueHandler hydratedCompoundValueHandler; + + @Override + public HydratedCompoundValueHandler getHydratedCompoundValueExtractor() { + if ( hydratedCompoundValueHandler == null ) { + hydratedCompoundValueHandler = new HydratedCompoundValueHandler() { + @Override + public Object extract(Object hydratedState) { + return ( (Object[] ) hydratedState )[ attributeNumber() ]; + } + + @Override + public void inject(Object hydratedState, Object value) { + ( (Object[] ) hydratedState )[ attributeNumber() ] = value; + } + }; + } + return hydratedCompoundValueHandler; + } + + @Override + protected String loggableMetadata() { + return super.loggableMetadata() + ",association"; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedCompositeAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedCompositionAttribute.java similarity index 84% rename from hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedCompositeAttribute.java rename to hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedCompositionAttribute.java index 8f3a9a6e80..e283cae94b 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedCompositeAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedCompositionAttribute.java @@ -25,19 +25,19 @@ package org.hibernate.tuple.entity; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.tuple.component.AbstractCompositeDefinition; -import org.hibernate.persister.walking.spi.CompositeDefinition; +import org.hibernate.tuple.component.AbstractCompositionDefinition; +import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.tuple.BaselineAttributeInformation; import org.hibernate.type.CompositeType; /** * @author Steve Ebersole */ -public class EntityBasedCompositeAttribute - extends AbstractCompositeDefinition - implements CompositeDefinition { +public class EntityBasedCompositionAttribute + extends AbstractCompositionDefinition + implements CompositionDefinition { - public EntityBasedCompositeAttribute( + public EntityBasedCompositionAttribute( EntityPersister source, SessionFactoryImplementor factory, int attributeNumber, 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 597b900344..9491d61b95 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 @@ -119,9 +119,9 @@ public class LoadPlanBuilderTest extends BaseCoreFunctionalTestCase { CollectionReturn collectionReturn = ExtraAssertions.assertTyping( CollectionReturn.class, rtn ); assertEquals( "abc", collectionReturn.getAlias() ); - assertNotNull( collectionReturn.getFetches() ); - assertEquals( 1, collectionReturn.getFetches().length ); // the collection elements are fetched - Fetch fetch = collectionReturn.getFetches()[0]; + assertNotNull( collectionReturn.getElementGraph().getFetches() ); + assertEquals( 1, collectionReturn.getElementGraph().getFetches().length ); // the collection elements are fetched + Fetch fetch = collectionReturn.getElementGraph().getFetches()[0]; EntityFetch entityFetch = ExtraAssertions.assertTyping( EntityFetch.class, fetch ); assertNotNull( entityFetch.getFetches() ); assertEquals( 0, entityFetch.getFetches().length ); @@ -130,8 +130,8 @@ public class LoadPlanBuilderTest extends BaseCoreFunctionalTestCase { @Entity( name = "Message" ) public static class Message { @Id - private Integer id; - private String name; + private Integer mid; + private String msgTxt; @ManyToOne( cascade = CascadeType.MERGE ) @JoinColumn private Poster poster; @@ -140,7 +140,7 @@ public class LoadPlanBuilderTest extends BaseCoreFunctionalTestCase { @Entity( name = "Poster" ) public static class Poster { @Id - private Integer id; + private Integer pid; private String name; @OneToMany(mappedBy = "poster") private List messages; diff --git a/hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java b/hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java index 07c193bb13..da8bc7f961 100644 --- a/hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java @@ -35,8 +35,11 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.walking.spi.AssociationVisitationStrategy; import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.CollectionDefinition; -import org.hibernate.persister.walking.spi.CompositeDefinition; +import org.hibernate.persister.walking.spi.CollectionElementDefinition; +import org.hibernate.persister.walking.spi.CollectionIndexDefinition; +import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.persister.walking.spi.EntityDefinition; +import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; import org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor; import org.junit.Test; @@ -91,6 +94,16 @@ public class BasicWalkingTest extends BaseCoreFunctionalTestCase { ); } + @Override + public void startingEntityIdentifier(EntityIdentifierDefinition entityIdentifierDefinition) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void finishingEntityIdentifier(EntityIdentifierDefinition entityIdentifierDefinition) { + //To change body of implemented methods use File | Settings | File Templates. + } + @Override public void startingCollection(CollectionDefinition collectionDefinition) { System.out.println( @@ -114,23 +127,43 @@ public class BasicWalkingTest extends BaseCoreFunctionalTestCase { } @Override - public void startingComposite(CompositeDefinition compositeDefinition) { + public void startingCollectionIndex(CollectionIndexDefinition collectionIndexDefinition) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void finishingCollectionIndex(CollectionIndexDefinition collectionIndexDefinition) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void startingCollectionElements(CollectionElementDefinition elementDefinition) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void finishingCollectionElements(CollectionElementDefinition elementDefinition) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void startingComposite(CompositionDefinition compositionDefinition) { System.out.println( String.format( "%s Starting composite (%s)", StringHelper.repeat( ">>", ++depth ), - compositeDefinition.toString() + compositionDefinition.toString() ) ); } @Override - public void finishingComposite(CompositeDefinition compositeDefinition) { + public void finishingComposite(CompositionDefinition compositionDefinition) { System.out.println( String.format( "%s Finishing composite (%s)", StringHelper.repeat( ">>", depth-- ), - compositeDefinition.toString() + compositionDefinition.toString() ) ); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java index 6af678c8a5..cd85045871 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java +++ b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java @@ -58,6 +58,7 @@ import org.hibernate.persister.spi.PersisterClassResolver; import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.CollectionElementDefinition; import org.hibernate.persister.walking.spi.CollectionIndexDefinition; +import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.tuple.entity.EntityTuplizer; import org.hibernate.tuple.entity.NonPojoInstrumentationMetadata; @@ -590,8 +591,8 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver { } @Override - public Iterable getEmbeddedCompositeIdentifierAttributes() { - throw new NotYetImplementedException(); + public EntityIdentifierDefinition getEntityKeyDefinition() { + return null; //To change body of implemented methods use File | Settings | File Templates. } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java index 55b96323ab..ba4ff61bf5 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java +++ b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java @@ -36,6 +36,7 @@ import org.hibernate.mapping.PersistentClass; import org.hibernate.metadata.ClassMetadata; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.walking.spi.AttributeDefinition; +import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.tuple.entity.EntityTuplizer; import org.hibernate.tuple.entity.NonPojoInstrumentationMetadata; @@ -680,7 +681,7 @@ public class CustomPersister implements EntityPersister { } @Override - public Iterable getEmbeddedCompositeIdentifierAttributes() { + public EntityIdentifierDefinition getEntityKeyDefinition() { throw new NotYetImplementedException(); } diff --git a/hibernate-core/src/test/resources/log4j.properties b/hibernate-core/src/test/resources/log4j.properties index 686aae8fc2..73d5935aea 100644 --- a/hibernate-core/src/test/resources/log4j.properties +++ b/hibernate-core/src/test/resources/log4j.properties @@ -9,6 +9,8 @@ log4j.rootLogger=info, stdout log4j.logger.org.hibernate.tool.hbm2ddl=trace log4j.logger.org.hibernate.testing.cache=debug +log4j.logger.org.hibernate.loader.plan=trace + # SQL Logging - HHH-6833 log4j.logger.org.hibernate.SQL=debug