HHH-7841 - Redesign Loader

This commit is contained in:
Steve Ebersole 2013-03-11 15:55:53 -05:00
parent c75dafbedd
commit 3d332371bd
76 changed files with 3729 additions and 431 deletions

View File

@ -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 );
}
}

View File

@ -63,6 +63,7 @@ import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.loader.BasicLoader; import org.hibernate.loader.BasicLoader;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.Loadable; import org.hibernate.persister.entity.Loadable;

View File

@ -79,6 +79,7 @@ import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.FetchingScrollableResultsImpl; import org.hibernate.internal.FetchingScrollableResultsImpl;
import org.hibernate.internal.ScrollableResultsImpl; import org.hibernate.internal.ScrollableResultsImpl;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable; import org.hibernate.persister.entity.Loadable;
@ -245,10 +246,6 @@ public abstract class Loader {
: sql; : sql;
} }
protected static interface AfterLoadAction {
public void afterLoad(SessionImplementor session, Object entity, Loadable persister);
}
protected boolean shouldUseFollowOnLocking( protected boolean shouldUseFollowOnLocking(
QueryParameters parameters, QueryParameters parameters,
Dialect dialect, Dialect dialect,
@ -509,7 +506,7 @@ public abstract class Loader {
} }
// We call getKeyFromResultSet() here so that we can know the // 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 // it is also then called from getRowFromResultSet() which is certainly
// not the most efficient. But the call here is needed, and there // not the most efficient. But the call here is needed, and there
// currently is no other way without refactoring of the doQuery()/getRowFromResultSet() // currently is no other way without refactoring of the doQuery()/getRowFromResultSet()
@ -527,7 +524,7 @@ public abstract class Loader {
catch ( SQLException sqle ) { catch ( SQLException sqle ) {
throw factory.getSQLExceptionHelper().convert( throw factory.getSQLExceptionHelper().convert(
sqle, sqle,
"could not doAfterTransactionCompletion sequential read of results (forward)", "could not perform sequential read of results (forward)",
getSQLString() getSQLString()
); );
} }

View File

@ -43,6 +43,7 @@ import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.loader.JoinWalker; import org.hibernate.loader.JoinWalker;
import org.hibernate.loader.Loader; import org.hibernate.loader.Loader;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.pretty.MessageHelper; import org.hibernate.pretty.MessageHelper;
import org.hibernate.type.Type; import org.hibernate.type.Type;

View File

@ -45,6 +45,7 @@ import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.CriteriaImpl; import org.hibernate.internal.CriteriaImpl;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.loader.OuterJoinLoader; import org.hibernate.loader.OuterJoinLoader;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.persister.entity.Loadable; import org.hibernate.persister.entity.Loadable;
import org.hibernate.persister.entity.Lockable; import org.hibernate.persister.entity.Lockable;
import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.persister.entity.OuterJoinLoadable;

View File

@ -27,7 +27,6 @@ import java.sql.ResultSet;
import java.sql.ResultSetMetaData; import java.sql.ResultSetMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -41,10 +40,7 @@ import org.hibernate.QueryException;
import org.hibernate.ScrollableResults; import org.hibernate.ScrollableResults;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.dialect.Dialect; 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.QueryParameters;
import org.hibernate.engine.spi.RowSelection;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.hql.internal.HolderInstantiator; 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.CollectionAliases;
import org.hibernate.loader.EntityAliases; import org.hibernate.loader.EntityAliases;
import org.hibernate.loader.Loader; import org.hibernate.loader.Loader;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.Loadable; import org.hibernate.persister.entity.Loadable;
import org.hibernate.persister.entity.Lockable;
import org.hibernate.persister.entity.Queryable; import org.hibernate.persister.entity.Queryable;
import org.hibernate.transform.ResultTransformer; import org.hibernate.transform.ResultTransformer;
import org.hibernate.type.CollectionType; import org.hibernate.type.CollectionType;

View File

@ -43,6 +43,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.pretty.MessageHelper; import org.hibernate.pretty.MessageHelper;

View File

@ -52,6 +52,7 @@ import org.hibernate.hql.internal.ast.tree.SelectClause;
import org.hibernate.internal.IteratorImpl; import org.hibernate.internal.IteratorImpl;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.loader.BasicLoader; import org.hibernate.loader.BasicLoader;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.param.ParameterSpecification; import org.hibernate.param.ParameterSpecification;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.persister.collection.QueryableCollection;

View File

@ -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.
}
}

View File

@ -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<HydratedEntityRegistration> currentRowHydratedEntityRegistrationList;
private Map<EntityPersister,Set<EntityKey>> subselectLoadableEntityKeyMap;
private List<HydratedEntityRegistration> 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<EntityReference,IdentifierResolutionContext> identifierResolutionContextMap;
@Override
public IdentifierResolutionContext getIdentifierResolutionContext(final EntityReference entityReference) {
if ( identifierResolutionContextMap == null ) {
identifierResolutionContextMap = new HashMap<EntityReference, IdentifierResolutionContext>();
}
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<HydratedEntityRegistration>();
}
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<HydratedEntityRegistration>();
}
hydratedEntityRegistrationList.addAll( currentRowHydratedEntityRegistrationList );
// managing the map forms needed for subselect fetch generation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if ( ! hadSubselectFetches ) {
return;
}
if ( subselectLoadableEntityKeyMap == null ) {
subselectLoadableEntityKeyMap = new HashMap<EntityPersister, Set<EntityKey>>();
}
for ( HydratedEntityRegistration registration : currentRowHydratedEntityRegistrationList ) {
Set<EntityKey> entityKeys = subselectLoadableEntityKeyMap.get( registration.persister );
if ( entityKeys == null ) {
entityKeys = new HashSet<EntityKey>();
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<AfterLoadAction> afterLoadActionList) {
initializeEntitiesAndCollections( afterLoadActionList );
createSubselects();
if ( hydratedEntityRegistrationList != null ) {
hydratedEntityRegistrationList.clear();
hydratedEntityRegistrationList = null;
}
if ( subselectLoadableEntityKeyMap != null ) {
subselectLoadableEntityKeyMap.clear();
subselectLoadableEntityKeyMap = null;
}
}
private void initializeEntitiesAndCollections(List<AfterLoadAction> 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<AfterLoadAction> 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<String, int[]> namedParameterLocMap =
ResultSetProcessorHelper.buildNamedParameterLocMap( queryParameters, namedParameterContext );
for ( Map.Entry<EntityPersister, Set<EntityKey>> 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;
}
}
}

View File

@ -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<String, int[]> buildNamedParameterLocMap(
QueryParameters queryParameters,
NamedParameterContext namedParameterContext) {
if ( queryParameters.getNamedParameters() == null || queryParameters.getNamedParameters().isEmpty() ) {
return null;
}
final Map<String, int[]> namedParameterLocMap = new HashMap<String, int[]>();
for ( String name : queryParameters.getNamedParameters().keySet() ) {
namedParameterLocMap.put(
name,
namedParameterContext.getNamedParameterLocations( name )
);
}
return namedParameterLocMap;
}
}

View File

@ -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<AfterLoadAction> 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;
}
}
}

View File

@ -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()
);
}
}

View File

@ -32,26 +32,18 @@ import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.CollectionAliases; import org.hibernate.loader.CollectionAliases;
import org.hibernate.loader.DefaultEntityAliases;
import org.hibernate.loader.EntityAliases; import org.hibernate.loader.EntityAliases;
import org.hibernate.loader.GeneratedCollectionAliases;
import org.hibernate.loader.PropertyPath; 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.AbstractLoadPlanBuilderStrategy;
import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CollectionReturn; 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.EntityReturn;
import org.hibernate.loader.plan.spi.FetchOwner;
import org.hibernate.loader.plan.spi.LoadPlan; import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.LoadPlanBuilderStrategy; import org.hibernate.loader.plan.spi.LoadPlanBuilderStrategy;
import org.hibernate.loader.plan.spi.Return; import org.hibernate.loader.plan.spi.Return;
import org.hibernate.persister.collection.CollectionPersister; 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.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.CollectionDefinition; import org.hibernate.persister.walking.spi.CollectionDefinition;
import org.hibernate.persister.walking.spi.CompositeDefinition;
import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.persister.walking.spi.EntityDefinition;
import org.hibernate.type.EntityType; import org.hibernate.type.EntityType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
@ -74,7 +66,6 @@ public class SingleRootReturnLoadPlanBuilderStrategy
private final LoadQueryInfluencers loadQueryInfluencers; private final LoadQueryInfluencers loadQueryInfluencers;
private final String rootAlias; private final String rootAlias;
private int currentSuffixBase;
private Return rootReturn; private Return rootReturn;
@ -85,10 +76,9 @@ public class SingleRootReturnLoadPlanBuilderStrategy
LoadQueryInfluencers loadQueryInfluencers, LoadQueryInfluencers loadQueryInfluencers,
String rootAlias, String rootAlias,
int suffixSeed) { int suffixSeed) {
super( sessionFactory ); super( sessionFactory, suffixSeed );
this.loadQueryInfluencers = loadQueryInfluencers; this.loadQueryInfluencers = loadQueryInfluencers;
this.rootAlias = rootAlias; this.rootAlias = rootAlias;
this.currentSuffixBase = suffixSeed;
} }
@Override @Override
@ -153,10 +143,7 @@ public class SingleRootReturnLoadPlanBuilderStrategy
LockMode.NONE, // todo : for now LockMode.NONE, // todo : for now
entityName, entityName,
StringHelper.generateAlias( StringHelper.unqualifyEntityName( entityName ), currentDepth() ), StringHelper.generateAlias( StringHelper.unqualifyEntityName( entityName ), currentDepth() ),
new DefaultEntityAliases( generateEntityColumnAliases( entityDefinition.getEntityPersister() )
(Loadable) entityDefinition.getEntityPersister(),
Integer.toString( currentSuffixBase++ ) + '_'
)
); );
} }
@ -165,17 +152,16 @@ public class SingleRootReturnLoadPlanBuilderStrategy
final CollectionPersister persister = collectionDefinition.getCollectionPersister(); final CollectionPersister persister = collectionDefinition.getCollectionPersister();
final String collectionRole = persister.getRole(); final String collectionRole = persister.getRole();
final CollectionAliases collectionAliases = new GeneratedCollectionAliases( final CollectionAliases collectionAliases = generateCollectionColumnAliases(
collectionDefinition.getCollectionPersister(), collectionDefinition.getCollectionPersister()
Integer.toString( currentSuffixBase++ ) + '_'
); );
final Type elementType = collectionDefinition.getCollectionPersister().getElementType(); final Type elementType = collectionDefinition.getCollectionPersister().getElementType();
final EntityAliases elementAliases; final EntityAliases elementAliases;
if ( elementType.isEntityType() ) { if ( elementType.isEntityType() ) {
final EntityType entityElementType = (EntityType) elementType; final EntityType entityElementType = (EntityType) elementType;
elementAliases = new DefaultEntityAliases( elementAliases = generateEntityColumnAliases(
(Loadable) entityElementType.getAssociatedJoinable( sessionFactory() ), (EntityPersister) entityElementType.getAssociatedJoinable( sessionFactory() )
Integer.toString( currentSuffixBase++ ) + '_'
); );
} }
else { 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( // LoadPlanBuildingContext impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sessionFactory(),
createImplicitAlias(), @Override
LockMode.NONE, // todo : for now public String resolveRootSourceAlias(EntityDefinition definition) {
(AbstractFetchOwner) fetchOwner, return rootAlias;
fetchStrategy,
attributeDefinition.getName(),
collectionAliases,
elementAliases
);
} }
@Override @Override
protected EntityFetch buildEntityFetch( public String resolveRootSourceAlias(CollectionDefinition definition) {
FetchOwner fetchOwner, return rootAlias;
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++;
} }
} }

View File

@ -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();
}
}

View File

@ -44,7 +44,7 @@ public abstract class AbstractFetch extends AbstractFetchOwner implements Fetch
SessionFactoryImplementor factory, SessionFactoryImplementor factory,
String alias, String alias,
LockMode lockMode, LockMode lockMode,
AbstractFetchOwner owner, FetchOwner owner,
String ownerProperty, String ownerProperty,
FetchStrategy fetchStrategy) { FetchStrategy fetchStrategy) {
super( factory, alias, lockMode ); super( factory, alias, lockMode );
@ -85,4 +85,9 @@ public abstract class AbstractFetch extends AbstractFetchOwner implements Fetch
public PropertyPath getPropertyPath() { public PropertyPath getPropertyPath() {
return propertyPath; return propertyPath;
} }
@Override
public String toString() {
return "Fetch(" + propertyPath.getFullPath() + ")";
}
} }

View File

@ -56,7 +56,8 @@ public abstract class AbstractFetchOwner extends AbstractPlanNode implements Fet
return lockMode; return lockMode;
} }
void addFetch(Fetch fetch) { @Override
public void addFetch(Fetch fetch) {
if ( fetch.getOwner() != this ) { if ( fetch.getOwner() != this ) {
throw new IllegalArgumentException( "Fetch and owner did not match" ); throw new IllegalArgumentException( "Fetch and owner did not match" );
} }

View File

@ -23,29 +23,62 @@
*/ */
package org.hibernate.loader.plan.spi; 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.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.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SessionFactoryImplementor; 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.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.CollectionDefinition; 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.EntityDefinition;
import org.hibernate.persister.walking.spi.EntityIdentifierDefinition;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import static org.hibernate.loader.spi.ResultSetProcessingContext.IdentifierResolutionContext;
/** /**
* @author Steve Ebersole * @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 final SessionFactoryImplementor sessionFactory;
private ArrayDeque<FetchOwner> fetchOwnerStack = new ArrayDeque<FetchOwner>(); private ArrayDeque<FetchOwner> fetchOwnerStack = new ArrayDeque<FetchOwner>();
private ArrayDeque<CollectionReference> collectionReferenceStack = new ArrayDeque<CollectionReference>();
protected AbstractLoadPlanBuilderStrategy(SessionFactoryImplementor sessionFactory) { protected AbstractLoadPlanBuilderStrategy(SessionFactoryImplementor sessionFactory, int suffixSeed) {
this.sessionFactory = sessionFactory; this.sessionFactory = sessionFactory;
this.currentSuffixBase = suffixSeed;
} }
public SessionFactoryImplementor sessionFactory() { public SessionFactoryImplementor sessionFactory() {
@ -53,21 +86,39 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
} }
protected FetchOwner currentFetchOwner() { protected FetchOwner currentFetchOwner() {
return fetchOwnerStack.peekLast(); return fetchOwnerStack.peekFirst();
} }
@Override @Override
public void start() { 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 @Override
public void finish() { public void finish() {
// nothing to do fetchOwnerStack.clear();
collectionReferenceStack.clear();
} }
@Override @Override
public void startingEntity(EntityDefinition entityDefinition) { public void startingEntity(EntityDefinition entityDefinition) {
log.tracef(
"%s Starting entity : %s",
StringHelper.repeat( ">>", fetchOwnerStack.size() ),
entityDefinition.getEntityPersister().getEntityName()
);
if ( fetchOwnerStack.isEmpty() ) { if ( fetchOwnerStack.isEmpty() ) {
// this is a root... // this is a root...
if ( ! supportsRootEntityReturns() ) { if ( ! supportsRootEntityReturns() ) {
@ -75,7 +126,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
} }
final EntityReturn entityReturn = buildRootEntityReturn( entityDefinition ); final EntityReturn entityReturn = buildRootEntityReturn( entityDefinition );
addRootReturn( entityReturn ); addRootReturn( entityReturn );
fetchOwnerStack.push( entityReturn ); pushToStack( entityReturn );
} }
// otherwise this call should represent a fetch which should have been handled in #startingAttribute // 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 @Override
public void finishingEntity(EntityDefinition entityDefinition) { 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 @Override
public void startingCollection(CollectionDefinition collectionDefinition) { public void startingCollection(CollectionDefinition collectionDefinition) {
log.tracef(
"%s Starting collection : %s",
StringHelper.repeat( ">>", fetchOwnerStack.size() ),
collectionDefinition.getCollectionPersister().getRole()
);
if ( fetchOwnerStack.isEmpty() ) { if ( fetchOwnerStack.isEmpty() ) {
// this is a root... // this is a root...
if ( ! supportsRootCollectionReturns() ) { if ( ! supportsRootCollectionReturns() ) {
@ -100,7 +252,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
} }
final CollectionReturn collectionReturn = buildRootCollectionReturn( collectionDefinition ); final CollectionReturn collectionReturn = buildRootCollectionReturn( collectionDefinition );
addRootReturn( collectionReturn ); addRootReturn( collectionReturn );
fetchOwnerStack.push( collectionReturn ); pushToCollectionStack( collectionReturn );
} }
} }
@ -109,24 +261,96 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
} }
@Override @Override
public void finishingCollection(CollectionDefinition collectionDefinition) { public void startingCollectionIndex(CollectionIndexDefinition collectionIndexDefinition) {
// nothing to do 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 @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() ) { if ( fetchOwnerStack.isEmpty() ) {
throw new HibernateException( "A component cannot be the root of a walk nor a graph" ); throw new HibernateException( "A component cannot be the root of a walk nor a graph" );
} }
} }
@Override @Override
public void finishingComposite(CompositeDefinition compositeDefinition) { public void finishingComposite(CompositionDefinition compositionDefinition) {
// nothing to do // 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 @Override
public boolean startingAttribute(AttributeDefinition attributeDefinition) { public boolean startingAttribute(AttributeDefinition attributeDefinition) {
log.tracef(
"%s Starting attribute %s",
StringHelper.repeat( ">>", fetchOwnerStack.size() ),
attributeDefinition
);
final Type attributeType = attributeDefinition.getType(); final Type attributeType = attributeDefinition.getType();
final boolean isComponentType = attributeType.isComponentType(); final boolean isComponentType = attributeType.isComponentType();
@ -136,30 +360,26 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
return true; return true;
} }
else if ( isComponentType ) { else if ( isComponentType ) {
return handleCompositeAttribute( (CompositeDefinition) attributeDefinition ); return handleCompositeAttribute( (CompositionDefinition) attributeDefinition );
} }
else { else {
return handleAssociationAttribute( (AssociationAttributeDefinition) attributeDefinition ); return handleAssociationAttribute( (AssociationAttributeDefinition) attributeDefinition );
} }
} }
@Override @Override
public void finishingAttribute(AttributeDefinition attributeDefinition) { public void finishingAttribute(AttributeDefinition attributeDefinition) {
final Type attributeType = attributeDefinition.getType(); log.tracef(
"%s Finishing up attribute : %s",
final boolean isComponentType = attributeType.isComponentType(); StringHelper.repeat( "<<", fetchOwnerStack.size() ),
final boolean isBasicType = ! ( isComponentType || attributeType.isAssociationType() ); attributeDefinition
);
if ( ! isBasicType ) {
fetchOwnerStack.removeLast();
}
} }
protected boolean handleCompositeAttribute(CompositeDefinition attributeDefinition) { protected boolean handleCompositeAttribute(CompositionDefinition attributeDefinition) {
final FetchOwner fetchOwner = fetchOwnerStack.peekLast(); final FetchOwner fetchOwner = currentFetchOwner();
final CompositeFetch fetch = buildCompositeFetch( fetchOwner, attributeDefinition ); final CompositeFetch fetch = fetchOwner.buildCompositeFetch( attributeDefinition, this );
fetchOwnerStack.addLast( fetch ); pushToStack( fetch );
return true; return true;
} }
@ -169,17 +389,20 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
return false; return false;
} }
final FetchOwner fetchOwner = fetchOwnerStack.peekLast(); final FetchOwner fetchOwner = currentFetchOwner();
fetchOwner.validateFetchPlan( fetchStrategy ); fetchOwner.validateFetchPlan( fetchStrategy );
final Fetch associationFetch; final Fetch associationFetch;
if ( attributeDefinition.isCollection() ) { if ( attributeDefinition.isCollection() ) {
associationFetch = buildCollectionFetch( fetchOwner, attributeDefinition, fetchStrategy ); associationFetch = fetchOwner.buildCollectionFetch( attributeDefinition, fetchStrategy, this );
} }
else { 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; return true;
} }
@ -194,19 +417,309 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
return false; 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 EntityReturn buildRootEntityReturn(EntityDefinition entityDefinition);
protected abstract CollectionReturn buildRootCollectionReturn(CollectionDefinition collectionDefinition); 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<EntityFetch> identifierFetches = new ArrayList<EntityFetch>();
protected final Map<EntityFetch,HydratedCompoundValueHandler> fetchToHydratedStateExtractorMap
= new HashMap<EntityFetch, HydratedCompoundValueHandler>();
public AbstractIdentifierAttributeCollector(EntityReference entityReference) {
this.entityReference = entityReference;
this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath().append( "<id>" );
}
@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<EntityFetch,HydratedCompoundValueHandler> fetchToHydratedStateExtractorMap;
private IdentifierDescriptionImpl(
EntityReference entityReference, EntityFetch[] identifierFetches,
Map<EntityFetch, HydratedCompoundValueHandler> 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() );
}
}
} }

View File

@ -23,58 +23,75 @@
*/ */
package org.hibernate.loader.plan.spi; package org.hibernate.loader.plan.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.CollectionAliases; import org.hibernate.loader.CollectionAliases;
import org.hibernate.loader.EntityAliases; import org.hibernate.loader.EntityAliases;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.loader.PropertyPath;
import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.loader.spi.ResultSetProcessingContext;
import org.hibernate.persister.entity.EntityPersister;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class CollectionFetch extends AbstractFetch implements CollectionReference { public class CollectionFetch extends AbstractCollectionReference implements CollectionReference, Fetch {
private final CollectionAliases collectionAliases; private final FetchOwner fetchOwner;
private final EntityAliases elementEntityAliases; private final FetchStrategy fetchStrategy;
private final CollectionPersister persister;
public CollectionFetch( public CollectionFetch(
SessionFactoryImplementor sessionFactory, SessionFactoryImplementor sessionFactory,
String alias, String alias,
LockMode lockMode, LockMode lockMode,
AbstractFetchOwner owner, FetchOwner fetchOwner,
FetchStrategy fetchStrategy, FetchStrategy fetchStrategy,
String ownerProperty, String ownerProperty,
CollectionAliases collectionAliases, CollectionAliases collectionAliases,
EntityAliases elementEntityAliases) { EntityAliases elementEntityAliases) {
super( sessionFactory, alias, lockMode, owner, ownerProperty, fetchStrategy ); super(
this.collectionAliases = collectionAliases; sessionFactory,
this.elementEntityAliases = elementEntityAliases; alias,
lockMode,
final String role = owner.retrieveFetchSourcePersister().getEntityName() + '.' + getOwnerPropertyName(); sessionFactory.getCollectionPersister(
this.persister = sessionFactory.getCollectionPersister( role ); fetchOwner.retrieveFetchSourcePersister().getEntityName() + '.' + ownerProperty
),
fetchOwner.getPropertyPath().append( ownerProperty ),
collectionAliases,
elementEntityAliases
);
this.fetchOwner = fetchOwner;
this.fetchStrategy = fetchStrategy;
} }
@Override @Override
public CollectionAliases getCollectionAliases() { public FetchOwner getOwner() {
return collectionAliases; return fetchOwner;
} }
@Override @Override
public EntityAliases getElementEntityAliases() { public String getOwnerPropertyName() {
return elementEntityAliases; return getPropertyPath().getProperty();
} }
@Override @Override
public CollectionPersister getCollectionPersister() { public FetchStrategy getFetchStrategy() {
return persister; return fetchStrategy;
} }
@Override @Override
public EntityPersister retrieveFetchSourcePersister() { public PropertyPath getPropertyPath() {
return ( (QueryableCollection) getCollectionPersister() ).getElementPersister(); 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.
} }
} }

View File

@ -55,6 +55,12 @@ public interface CollectionReference {
*/ */
public CollectionPersister getCollectionPersister(); 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 * Returns the description of the aliases in the JDBC ResultSet that identify values "belonging" to the
* this collection. * this collection.

View File

@ -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 {
}

View File

@ -23,28 +23,22 @@
*/ */
package org.hibernate.loader.plan.spi; package org.hibernate.loader.plan.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.CollectionAliases; import org.hibernate.loader.CollectionAliases;
import org.hibernate.loader.EntityAliases; import org.hibernate.loader.EntityAliases;
import org.hibernate.loader.PropertyPath; import org.hibernate.loader.PropertyPath;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.loader.spi.ResultSetProcessingContext;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
/** /**
* @author Steve Ebersole * @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 ownerEntityName;
private final String ownerProperty; 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( public CollectionReturn(
SessionFactoryImplementor sessionFactory, SessionFactoryImplementor sessionFactory,
@ -54,14 +48,17 @@ public class CollectionReturn extends AbstractFetchOwner implements Return, Fetc
String ownerProperty, String ownerProperty,
CollectionAliases collectionAliases, CollectionAliases collectionAliases,
EntityAliases elementEntityAliases) { 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.ownerEntityName = ownerEntityName;
this.ownerProperty = ownerProperty; 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 @Override
public CollectionAliases getCollectionAliases() { public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
return collectionAliases; // todo : anything to do here?
} }
@Override @Override
public EntityAliases getElementEntityAliases() { public void resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
return elementEntityAliases; // todo : anything to do here?
} }
@Override @Override
public CollectionPersister getCollectionPersister() { public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
return persister; return null; //To change body of implemented methods use File | Settings | File Templates.
} }
@Override @Override
public void validateFetchPlan(FetchStrategy fetchStrategy) { public String toString() {
} return "CollectionReturn(" + getCollectionPersister().getRole() + ")";
@Override
public EntityPersister retrieveFetchSourcePersister() {
return ( (QueryableCollection) persister ).getElementPersister();
}
@Override
public PropertyPath getPropertyPath() {
return propertyPath;
} }
} }

View File

@ -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<Fetch> fetches;
public CompositeElementGraph(
SessionFactoryImplementor sessionFactory,
CollectionReference collectionReference,
PropertyPath collectionPath) {
super( sessionFactory );
this.collectionReference = collectionReference;
this.collectionPersister = collectionReference.getCollectionPersister();
this.propertyPath = collectionPath.append( "<elements>" );
}
@Override
public void addFetch(Fetch fetch) {
if ( fetches == null ) {
fetches = new ArrayList<Fetch>();
}
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 );
}
}

View File

@ -23,12 +23,18 @@
*/ */
package org.hibernate.loader.plan.spi; package org.hibernate.loader.plan.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.spi.ResultSetProcessingContext;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
@ -48,4 +54,36 @@ public class CompositeFetch extends AbstractFetch implements Fetch {
public EntityPersister retrieveFetchSourcePersister() { public EntityPersister retrieveFetchSourcePersister() {
return getOwner().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.
}
} }

View File

@ -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<Fetch> fetches;
public CompositeIndexGraph(
SessionFactoryImplementor sessionFactory,
CollectionReference collectionReference,
PropertyPath propertyPath) {
super( sessionFactory );
this.collectionReference = collectionReference;
this.collectionPersister = collectionReference.getCollectionPersister();
this.propertyPath = propertyPath.append( "<index>" );
}
@Override
public void addFetch(Fetch fetch) {
if ( fetches == null ) {
fetches = new ArrayList<Fetch>();
}
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 );
}
}

View File

@ -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<Fetch> 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( "<elements>" );
}
@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<Fetch>();
}
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() + ")";
}
}

View File

@ -23,27 +23,40 @@
*/ */
package org.hibernate.loader.plan.spi; package org.hibernate.loader.plan.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.WrongClassException;
import org.hibernate.engine.FetchStrategy; 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.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.EntityAliases; 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.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.type.EntityType; import org.hibernate.type.EntityType;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class EntityFetch extends AbstractFetch implements EntityReference { public class EntityFetch extends AbstractFetch implements EntityReference, FetchOwner {
private final String sqlTableAlias; private final String sqlTableAlias;
private final EntityAliases entityAliases; private final EntityAliases entityAliases;
private final EntityType associationType;
private final EntityPersister persister; private final EntityPersister persister;
private IdentifierDescription identifierDescription;
public EntityFetch( public EntityFetch(
SessionFactoryImplementor sessionFactory, SessionFactoryImplementor sessionFactory,
String alias, String alias,
LockMode lockMode, LockMode lockMode,
AbstractFetchOwner owner, FetchOwner owner,
String ownerProperty, String ownerProperty,
FetchStrategy fetchStrategy, FetchStrategy fetchStrategy,
String sqlTableAlias, String sqlTableAlias,
@ -52,8 +65,8 @@ public class EntityFetch extends AbstractFetch implements EntityReference {
this.sqlTableAlias = sqlTableAlias; this.sqlTableAlias = sqlTableAlias;
this.entityAliases = entityAliases; this.entityAliases = entityAliases;
final EntityType type = (EntityType) owner.retrieveFetchSourcePersister().getPropertyType( ownerProperty ); this.associationType = (EntityType) owner.retrieveFetchSourcePersister().getPropertyType( ownerProperty );
this.persister = sessionFactory.getEntityPersister( type.getAssociatedEntityName() ); this.persister = sessionFactory.getEntityPersister( associationType.getAssociatedEntityName() );
} }
@Override @Override
@ -62,17 +75,174 @@ public class EntityFetch extends AbstractFetch implements EntityReference {
} }
@Override @Override
public EntityAliases getEntityAliases() { public IdentifierDescription getIdentifierDescription() {
return entityAliases; return identifierDescription;
}
@Override
public String getSqlTableAlias() {
return sqlTableAlias;
} }
@Override @Override
public EntityPersister retrieveFetchSourcePersister() { public EntityPersister retrieveFetchSourcePersister() {
return persister; 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() + ")";
}
} }

View File

@ -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<Fetch> 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( "<index>" ); // todo : do we want the <index> 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<Fetch>();
}
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;
}
}

View File

@ -24,7 +24,6 @@
package org.hibernate.loader.plan.spi; package org.hibernate.loader.plan.spi;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.loader.EntityAliases;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
/** /**
@ -32,7 +31,7 @@ import org.hibernate.persister.entity.EntityPersister;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface EntityReference { public interface EntityReference extends IdentifierDescriptionInjectable {
/** /**
* Retrieve the alias associated with the persister (entity/collection). * Retrieve the alias associated with the persister (entity/collection).
* *
@ -54,20 +53,5 @@ public interface EntityReference {
*/ */
public EntityPersister getEntityPersister(); public EntityPersister getEntityPersister();
/** public IdentifierDescription getIdentifierDescription();
* 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();
} }

View File

@ -23,12 +23,25 @@
*/ */
package org.hibernate.loader.plan.spi; 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.LockMode;
import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.EntityAliases; import org.hibernate.loader.EntityAliases;
import org.hibernate.loader.PropertyPath; 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.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 * @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 final PropertyPath propertyPath = new PropertyPath(); // its a root
private IdentifierDescription identifierDescription;
public EntityReturn( public EntityReturn(
SessionFactoryImplementor sessionFactory, SessionFactoryImplementor sessionFactory,
String alias, String alias,
@ -71,13 +86,8 @@ public class EntityReturn extends AbstractFetchOwner implements Return, FetchOwn
} }
@Override @Override
public EntityAliases getEntityAliases() { public IdentifierDescription getIdentifierDescription() {
return entityAliases; return identifierDescription;
}
@Override
public String getSqlTableAlias() {
return sqlTableAlias;
} }
@Override @Override
@ -93,4 +103,83 @@ public class EntityReturn extends AbstractFetchOwner implements Return, FetchOwn
public PropertyPath getPropertyPath() { public PropertyPath getPropertyPath() {
return propertyPath; 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() + ")";
}
} }

View File

@ -23,8 +23,12 @@
*/ */
package org.hibernate.loader.plan.spi; package org.hibernate.loader.plan.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStrategy;
import org.hibernate.loader.PropertyPath; import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.spi.ResultSetProcessingContext;
/** /**
* Contract for associations that are being fetched. * Contract for associations that are being fetched.
@ -33,7 +37,7 @@ import org.hibernate.loader.PropertyPath;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface Fetch extends FetchOwner { public interface Fetch {
/** /**
* Obtain the owner of this fetch. * Obtain the owner of this fetch.
* *
@ -56,4 +60,8 @@ public interface Fetch extends FetchOwner {
* @return The property path * @return The property path
*/ */
public PropertyPath getPropertyPath(); public PropertyPath getPropertyPath();
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
} }

View File

@ -26,6 +26,8 @@ package org.hibernate.loader.plan.spi;
import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStrategy;
import org.hibernate.loader.PropertyPath; import org.hibernate.loader.PropertyPath;
import org.hibernate.persister.entity.EntityPersister; 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. * 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]; 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. * Retrieve the fetches owned by this return.
* *
@ -65,4 +75,19 @@ public interface FetchOwner {
* @return The property path * @return The property path
*/ */
public PropertyPath getPropertyPath(); 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);
} }

View File

@ -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;
}

View File

@ -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);
}

View File

@ -55,6 +55,7 @@ public interface LoadPlan {
public List<Return> getReturns(); public List<Return> getReturns();
// todo : would also like to see "call back" style access for handling "subsequent actions" such as: // todo : would also like to see "call back" style access for handling "subsequent actions" such as:
// 1) follow-on locking // 1) follow-on locking
// 2) join fetch conversions to subselect fetches // 2) join fetch conversions to subselect fetches

View File

@ -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);
}

View File

@ -26,16 +26,16 @@ package org.hibernate.loader.plan.spi;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface ReturnVisitationStrategy { public interface LoadPlanVisitationStrategy {
/** /**
* Notification we are preparing to start visitation. * Notification we are preparing to start visitation.
*/ */
public void start(); public void start(LoadPlan loadPlan);
/** /**
* Notification we are finished visitation. * 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 * Notification that a new root return branch is being started. Will be followed by calls to one of the following

View File

@ -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.
}
}

View File

@ -28,25 +28,25 @@ package org.hibernate.loader.plan.spi;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class ReturnVisitor { public class LoadPlanVisitor {
public static void visit(Return[] rootReturns, ReturnVisitationStrategy strategy) { public static void visit(LoadPlan loadPlan, LoadPlanVisitationStrategy strategy) {
new ReturnVisitor( strategy ).visitReturns( rootReturns ); new LoadPlanVisitor( strategy ).visit( loadPlan );
} }
private final ReturnVisitationStrategy strategy; private final LoadPlanVisitationStrategy strategy;
public ReturnVisitor(ReturnVisitationStrategy strategy) { public LoadPlanVisitor(LoadPlanVisitationStrategy strategy) {
this.strategy = strategy; this.strategy = strategy;
} }
private void visitReturns(Return[] rootReturns) { private void visit(LoadPlan loadPlan) {
strategy.start(); strategy.start( loadPlan );
for ( Return rootReturn : rootReturns ) { for ( Return rootReturn : loadPlan.getReturns() ) {
visitRootReturn( rootReturn ); visitRootReturn( rootReturn );
} }
strategy.finish(); strategy.finish( loadPlan );
} }
private void visitRootReturn(Return rootReturn) { private void visitRootReturn(Return rootReturn) {
@ -69,7 +69,9 @@ public class ReturnVisitor {
} }
else if ( CollectionReturn.class.isInstance( rootReturn ) ) { else if ( CollectionReturn.class.isInstance( rootReturn ) ) {
strategy.handleCollectionReturn( (CollectionReturn) rootReturn ); strategy.handleCollectionReturn( (CollectionReturn) rootReturn );
visitFetches( (CollectionReturn) rootReturn ); final CollectionReturn collectionReturn = (CollectionReturn) rootReturn;
visitFetches( collectionReturn.getIndexGraph() );
visitFetches( collectionReturn.getElementGraph() );
} }
else { else {
throw new IllegalStateException( throw new IllegalStateException(
@ -92,17 +94,18 @@ public class ReturnVisitor {
private void visitFetch(Fetch fetch) { private void visitFetch(Fetch fetch) {
if ( EntityFetch.class.isInstance( fetch ) ) { if ( EntityFetch.class.isInstance( fetch ) ) {
strategy.startingEntityFetch( (EntityFetch) fetch ); strategy.startingEntityFetch( (EntityFetch) fetch );
visitFetches( fetch ); visitFetches( (EntityFetch) fetch );
strategy.finishingEntityFetch( (EntityFetch) fetch ); strategy.finishingEntityFetch( (EntityFetch) fetch );
} }
else if ( CollectionFetch.class.isInstance( fetch ) ) { else if ( CollectionFetch.class.isInstance( fetch ) ) {
strategy.startingCollectionFetch( (CollectionFetch) fetch ); strategy.startingCollectionFetch( (CollectionFetch) fetch );
visitFetches( fetch ); visitFetches( ( (CollectionFetch) fetch ).getIndexGraph() );
visitFetches( ( (CollectionFetch) fetch ).getElementGraph() );
strategy.finishingCollectionFetch( (CollectionFetch) fetch ); strategy.finishingCollectionFetch( (CollectionFetch) fetch );
} }
else if ( CompositeFetch.class.isInstance( fetch ) ) { else if ( CompositeFetch.class.isInstance( fetch ) ) {
strategy.startingCompositeFetch( (CompositeFetch) fetch ); strategy.startingCompositeFetch( (CompositeFetch) fetch );
visitFetches( fetch ); visitFetches( (CompositeFetch) fetch );
strategy.finishingCompositeFetch( (CompositeFetch) fetch ); strategy.finishingCompositeFetch( (CompositeFetch) fetch );
} }
else { else {

View File

@ -23,12 +23,15 @@
*/ */
package org.hibernate.loader.plan.spi; 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! * Represents a return value in the query results. Not the same as a result (column) in the JDBC ResultSet!
* <p/> * <p/>
* This is merely a unifying contract; it defines no behavior. * Return is distinctly different from a {@link Fetch} and so modeled as completely separate hierarchy.
* <p/>
* Return is distinctly different from a {@link Fetch}.
* *
* @see ScalarReturn * @see ScalarReturn
* @see EntityReturn * @see EntityReturn
@ -37,4 +40,28 @@ package org.hibernate.loader.plan.spi;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface Return { 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;
} }

View File

@ -23,7 +23,12 @@
*/ */
package org.hibernate.loader.plan.spi; package org.hibernate.loader.plan.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.internal.ResultSetProcessingContextImpl;
import org.hibernate.loader.spi.ResultSetProcessingContext;
import org.hibernate.type.Type; import org.hibernate.type.Type;
/** /**
@ -34,19 +39,30 @@ import org.hibernate.type.Type;
*/ */
public class ScalarReturn extends AbstractPlanNode implements Return { public class ScalarReturn extends AbstractPlanNode implements Return {
private final Type type; 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 ); super( factory );
this.type = type; this.type = type;
this.columnAlias = columnAlias; this.columnAliases = columnAliases;
} }
public Type getType() { public Type getType() {
return type; return type;
} }
public String getColumnAlias() { @Override
return columnAlias; 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 );
} }
} }

View File

@ -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);
}

View File

@ -33,28 +33,22 @@ import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.transform.ResultTransformer; import org.hibernate.transform.ResultTransformer;
/** /**
* Definition of the Loader contract. * Definition of the Loader contract. A Loader is intended to perform loading based on a query and a load-plan.
* <p/> * Under the covers it uses many delegates to perform that work that might be better used individually in
* Capabilities I'd like to see added (todo):<ul> * different situations. In general, Loader is intended for being fed a set of results and processing through
* <li> * all of those result rows in one swoop. For cases that do not fit that template, it is probably better to
* expose the underlying "query" (although what I see here relies heavily on * individually use the delegates to perform the work.
* https://github.com/hibernate/hibernate-orm/wiki/Proposal---SQL-generation)
* </li>
* </ul>
*
* *
* @author Gavin King * @author Gavin King
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface Loader { 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( public List extractResults(
ResultSet resultSet, ResultSet resultSet,

View File

@ -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.
* <p/>
* 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);
}

View File

@ -1,7 +1,7 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * 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 * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc. * distributed under license by Red Hat Inc.
@ -25,22 +25,24 @@ package org.hibernate.loader.spi;
import java.sql.ResultSet; import java.sql.ResultSet;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionImplementor; 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 * Contract for processing JDBC ResultSets a single logical row at a time. These are intended for use by
* for {@link org.hibernate.ScrollableResults} implementations. * {@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 * @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 * but dropping the 'returnProxies' (that method has only one use in the entire codebase and it always passes in
* false...) * false...)
* *
@ -50,19 +52,19 @@ public interface OnDemandLoader {
* *
* @return The extracted result row * @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( public Object extractSingleRow(
ResultSet resultSet, ResultSet resultSet,
SessionImplementor session, 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 * 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 * are sequential within the ResultSet due to ordering. Multiple ResultSet rows are read into a single query
* result "row". * 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 * but dropping the 'returnProxies' (that method has only one use in the entire codebase and it always passes in
* false...) * false...)
* *
@ -77,12 +79,12 @@ public interface OnDemandLoader {
public Object extractSequentialRowsForward( public Object extractSequentialRowsForward(
final ResultSet resultSet, final ResultSet resultSet,
final SessionImplementor session, final SessionImplementor session,
final QueryParameters queryParameters) throws HibernateException; final QueryParameters queryParameters);
/** /**
* Like {@link #extractSequentialRowsForward} but here moving back through the ResultSet. * 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 * but dropping the 'returnProxies' (that method has only one use in the entire codebase and it always passes in
* false...). * false...).
* *
@ -101,5 +103,5 @@ public interface OnDemandLoader {
ResultSet resultSet, ResultSet resultSet,
SessionImplementor session, SessionImplementor session,
QueryParameters queryParameters, QueryParameters queryParameters,
boolean isLogicallyAfterLast) throws HibernateException; boolean isLogicallyAfterLast);
} }

View File

@ -0,0 +1,7 @@
package org.hibernate.loader.spi;
/**
* @author Steve Ebersole
*/
public class ResultBuilder {
}

View File

@ -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;
}

View File

@ -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.
* <p/>
* 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<AfterLoadAction> afterLoadActions) throws SQLException;
}

View File

@ -82,7 +82,7 @@ import org.hibernate.persister.entity.Queryable;
import org.hibernate.persister.walking.spi.CollectionDefinition; import org.hibernate.persister.walking.spi.CollectionDefinition;
import org.hibernate.persister.walking.spi.CollectionElementDefinition; import org.hibernate.persister.walking.spi.CollectionElementDefinition;
import org.hibernate.persister.walking.spi.CollectionIndexDefinition; 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.persister.walking.spi.EntityDefinition;
import org.hibernate.pretty.MessageHelper; import org.hibernate.pretty.MessageHelper;
import org.hibernate.sql.Alias; import org.hibernate.sql.Alias;
@ -1974,7 +1974,7 @@ public abstract class AbstractCollectionPersister
} }
@Override @Override
public CompositeDefinition toCompositeDefinition() { public CompositionDefinition toCompositeDefinition() {
if ( ! getType().isComponentType() ) { if ( ! getType().isComponentType() ) {
throw new IllegalStateException( "Cannot treat entity collection index type as composite" ); throw new IllegalStateException( "Cannot treat entity collection index type as composite" );
} }
@ -2006,7 +2006,7 @@ public abstract class AbstractCollectionPersister
} }
@Override @Override
public CompositeDefinition toCompositeDefinition() { public CompositionDefinition toCompositeDefinition() {
if ( ! getType().isComponentType() ) { if ( ! getType().isComponentType() ) {
throw new IllegalStateException( "Cannot treat entity collection element type as composite" ); throw new IllegalStateException( "Cannot treat entity collection element type as composite" );
} }

View File

@ -110,6 +110,11 @@ import org.hibernate.metamodel.binding.SingularAttributeBinding;
import org.hibernate.metamodel.relational.DerivedValue; import org.hibernate.metamodel.relational.DerivedValue;
import org.hibernate.metamodel.relational.Value; import org.hibernate.metamodel.relational.Value;
import org.hibernate.persister.walking.spi.AttributeDefinition; 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.pretty.MessageHelper;
import org.hibernate.property.BackrefPropertyAccessor; import org.hibernate.property.BackrefPropertyAccessor;
import org.hibernate.sql.Alias; import org.hibernate.sql.Alias;
@ -5076,11 +5081,12 @@ public abstract class AbstractEntityPersister
// EntityDefinition impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // EntityDefinition impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private EntityIdentifierDefinition entityIdentifierDefinition;
private Iterable<AttributeDefinition> embeddedCompositeIdentifierAttributes; private Iterable<AttributeDefinition> embeddedCompositeIdentifierAttributes;
private Iterable<AttributeDefinition> attributeDefinitions; private Iterable<AttributeDefinition> attributeDefinitions;
protected void generateEntityDefinition() { protected void generateEntityDefinition() {
collectEmbeddedCompositeIdentifierAttributeDefinitions(); prepareEntityIdentifierDefinition();
collectAttributeDefinitions(); collectAttributeDefinitions();
} }
@ -5090,8 +5096,8 @@ public abstract class AbstractEntityPersister
} }
@Override @Override
public Iterable<AttributeDefinition> getEmbeddedCompositeIdentifierAttributes() { public EntityIdentifierDefinition getEntityKeyDefinition() {
return embeddedCompositeIdentifierAttributes; return entityIdentifierDefinition;
} }
@Override @Override
@ -5099,52 +5105,85 @@ public abstract class AbstractEntityPersister
return attributeDefinitions; return attributeDefinitions;
} }
private synchronized void collectEmbeddedCompositeIdentifierAttributeDefinitions() {
private void prepareEntityIdentifierDefinition() {
final Type idType = getIdentifierType(); final Type idType = getIdentifierType();
if ( !idType.isComponentType() ) { if ( !idType.isComponentType() ) {
entityIdentifierDefinition = buildEncapsulatedIdentifierDefinition();
return; return;
} }
final CompositeType cidType = (CompositeType) idType; final CompositeType cidType = (CompositeType) idType;
if ( !cidType.isEmbedded() ) { if ( !cidType.isEmbedded() ) {
entityIdentifierDefinition = buildEncapsulatedIdentifierDefinition();
return; return;
} }
// we have an embedded composite identifier. Most likely we need to process the composite entityIdentifierDefinition = new NonEncapsulatedEntityIdentifierDefinition() {
// 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<AttributeDefinition>() {
@Override @Override
public Iterator<AttributeDefinition> iterator() { public Iterable<AttributeDefinition> getAttributes() {
return new Iterator<AttributeDefinition>() {
private final int numberOfAttributes = countSubclassProperties();
private int currentAttributeNumber = 0;
@Override
public boolean hasNext() {
return currentAttributeNumber < numberOfAttributes;
}
@Override
public AttributeDefinition next() {
// todo : implement // todo : implement
throw new NotYetImplementedException(); throw new NotYetImplementedException();
} }
@Override @Override
public void remove() { public Class getSeparateIdentifierMappingClass() {
throw new UnsupportedOperationException( "Remove operation not supported here" ); // todo : implement
throw new NotYetImplementedException();
}
@Override
public boolean isEncapsulated() {
return false;
}
@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 "<identifier-property:" + getName() + ">";
}
};
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() { private void collectAttributeDefinitions() {

View File

@ -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);
}

View File

@ -27,6 +27,7 @@ import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.loader.PropertyPath; import org.hibernate.loader.PropertyPath;
import org.hibernate.persister.spi.HydratedCompoundValueHandler;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
@ -43,4 +44,6 @@ public interface AssociationAttributeDefinition extends AttributeDefinition {
public FetchStrategy determineFetchPlan(LoadQueryInfluencers loadQueryInfluencers, PropertyPath propertyPath); public FetchStrategy determineFetchPlan(LoadQueryInfluencers loadQueryInfluencers, PropertyPath propertyPath);
public CascadeStyle determineCascadeStyle(); public CascadeStyle determineCascadeStyle();
public HydratedCompoundValueHandler getHydratedCompoundValueExtractor();
} }

View File

@ -40,11 +40,20 @@ public interface AssociationVisitationStrategy {
public void startingEntity(EntityDefinition entityDefinition); public void startingEntity(EntityDefinition entityDefinition);
public void finishingEntity(EntityDefinition entityDefinition); public void finishingEntity(EntityDefinition entityDefinition);
public void startingEntityIdentifier(EntityIdentifierDefinition entityIdentifierDefinition);
public void finishingEntityIdentifier(EntityIdentifierDefinition entityIdentifierDefinition);
public void startingCollection(CollectionDefinition collectionDefinition); public void startingCollection(CollectionDefinition collectionDefinition);
public void finishingCollection(CollectionDefinition collectionDefinition); public void finishingCollection(CollectionDefinition collectionDefinition);
public void startingComposite(CompositeDefinition compositeDefinition); public void startingCollectionIndex(CollectionIndexDefinition collectionIndexDefinition);
public void finishingComposite(CompositeDefinition compositeDefinition); 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 boolean startingAttribute(AttributeDefinition attributeDefinition);
public void finishingAttribute(AttributeDefinition attributeDefinition); public void finishingAttribute(AttributeDefinition attributeDefinition);

View File

@ -35,5 +35,5 @@ public interface CollectionElementDefinition {
public EntityDefinition toEntityDefinition(); public EntityDefinition toEntityDefinition();
public CompositeDefinition toCompositeDefinition(); public CompositionDefinition toCompositeDefinition();
} }

View File

@ -35,5 +35,5 @@ public interface CollectionIndexDefinition {
public EntityDefinition toEntityDefinition(); public EntityDefinition toEntityDefinition();
public CompositeDefinition toCompositeDefinition(); public CompositionDefinition toCompositeDefinition();
} }

View File

@ -26,5 +26,5 @@ package org.hibernate.persister.walking.spi;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface CompositeDefinition extends AttributeDefinition, AttributeSource { public interface CompositionDefinition extends AttributeDefinition, AttributeSource {
} }

View File

@ -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();
}

View File

@ -32,5 +32,5 @@ import org.hibernate.persister.entity.EntityPersister;
*/ */
public interface EntityDefinition extends AttributeSource { public interface EntityDefinition extends AttributeSource {
public EntityPersister getEntityPersister(); public EntityPersister getEntityPersister();
public Iterable<AttributeDefinition> getEmbeddedCompositeIdentifierAttributes(); public EntityIdentifierDefinition getEntityKeyDefinition();
} }

View File

@ -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();
}

View File

@ -81,28 +81,28 @@ public class MetadataDrivenModelGraphVisitor {
private void visitEntityDefinition(EntityDefinition entityDefinition) { private void visitEntityDefinition(EntityDefinition entityDefinition) {
strategy.startingEntity( entityDefinition ); strategy.startingEntity( entityDefinition );
try {
visitAttributes( entityDefinition ); visitAttributes( entityDefinition );
optionallyVisitEmbeddedCompositeIdentifier( entityDefinition ); visitIdentifierDefinition( entityDefinition.getEntityKeyDefinition() );
}
finally {
strategy.finishingEntity( entityDefinition ); strategy.finishingEntity( entityDefinition );
} }
}
private void optionallyVisitEmbeddedCompositeIdentifier(EntityDefinition entityDefinition) { private void visitIdentifierDefinition(EntityIdentifierDefinition entityIdentifierDefinition) {
// if the entity has a composite identifier, see if we need to handle its sub-properties separately strategy.startingEntityIdentifier( entityIdentifierDefinition );
final Iterable<AttributeDefinition> embeddedCompositeIdentifierAttributes =
entityDefinition.getEmbeddedCompositeIdentifierAttributes();
if ( embeddedCompositeIdentifierAttributes == null ) {
return;
}
for ( AttributeDefinition attributeDefinition : embeddedCompositeIdentifierAttributes ) { if ( entityIdentifierDefinition.isEncapsulated() ) {
visitAttributeDefinition( ( (EncapsulatedEntityIdentifierDefinition) entityIdentifierDefinition).getAttributeDefinition() );
}
else {
for ( AttributeDefinition attributeDefinition : ( (NonEncapsulatedEntityIdentifierDefinition) entityIdentifierDefinition).getAttributes() ) {
visitAttributeDefinition( attributeDefinition ); visitAttributeDefinition( attributeDefinition );
} }
} }
strategy.finishingEntityIdentifier( entityIdentifierDefinition );
}
private void visitAttributes(AttributeSource attributeSource) { private void visitAttributes(AttributeSource attributeSource) {
for ( AttributeDefinition attributeDefinition : attributeSource.getAttributes() ) { for ( AttributeDefinition attributeDefinition : attributeSource.getAttributes() ) {
visitAttributeDefinition( attributeDefinition ); visitAttributeDefinition( attributeDefinition );
@ -122,7 +122,7 @@ public class MetadataDrivenModelGraphVisitor {
visitAssociation( (AssociationAttributeDefinition) attributeDefinition ); visitAssociation( (AssociationAttributeDefinition) attributeDefinition );
} }
else if ( attributeDefinition.getType().isComponentType() ) { else if ( attributeDefinition.getType().isComponentType() ) {
visitCompositeDefinition( (CompositeDefinition) attributeDefinition ); visitCompositeDefinition( (CompositionDefinition) attributeDefinition );
} }
} }
finally { finally {
@ -148,42 +148,34 @@ public class MetadataDrivenModelGraphVisitor {
} }
} }
private void visitCompositeDefinition(CompositeDefinition compositeDefinition) { private void visitCompositeDefinition(CompositionDefinition compositionDefinition) {
strategy.startingComposite( compositeDefinition ); strategy.startingComposite( compositionDefinition );
try {
visitAttributes( compositeDefinition ); visitAttributes( compositionDefinition );
}
finally { strategy.finishingComposite( compositionDefinition );
strategy.finishingComposite( compositeDefinition );
}
} }
private void visitCollectionDefinition(CollectionDefinition collectionDefinition) { private void visitCollectionDefinition(CollectionDefinition collectionDefinition) {
strategy.startingCollection( collectionDefinition ); strategy.startingCollection( collectionDefinition );
try { visitCollectionIndex( collectionDefinition );
visitCollectionIndex( collectionDefinition.getIndexDefinition() ); 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 ) { if ( collectionIndexDefinition == null ) {
return; return;
} }
log.debug( "Visiting collection index : " + currentPropertyPath.getFullPath() ); strategy.startingCollectionIndex( collectionIndexDefinition );
currentPropertyPath = currentPropertyPath.append( "<key>" );
log.debug( "Visiting index for collection : " + currentPropertyPath.getFullPath() );
currentPropertyPath = currentPropertyPath.append( "<index>" );
try { try {
final Type collectionIndexType = collectionIndexDefinition.getType(); final Type collectionIndexType = collectionIndexDefinition.getType();
if ( collectionIndexType.isComponentType() ) { if ( collectionIndexType.isComponentType() ) {
@ -196,6 +188,22 @@ public class MetadataDrivenModelGraphVisitor {
finally { finally {
currentPropertyPath = currentPropertyPath.getParent(); 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 );
} }

View File

@ -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<AttributeDefinition> getAttributes();
public Class getSeparateIdentifierMappingClass();
}

View File

@ -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 );
}
}

View File

@ -37,6 +37,7 @@ import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.loader.custom.CustomLoader; import org.hibernate.loader.custom.CustomLoader;
import org.hibernate.loader.custom.CustomQuery; import org.hibernate.loader.custom.CustomQuery;
import org.hibernate.loader.custom.sql.SQLQueryReturnProcessor; import org.hibernate.loader.custom.sql.SQLQueryReturnProcessor;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.result.NoMoreReturnsException; import org.hibernate.result.NoMoreReturnsException;
import org.hibernate.result.Result; import org.hibernate.result.Result;
import org.hibernate.result.Return; import org.hibernate.result.Return;

View File

@ -126,8 +126,12 @@ public abstract class AbstractNonIdentifierAttribute extends AbstractAttribute i
return attributeInformation.getFetchMode(); return attributeInformation.getFetchMode();
} }
protected String loggableMetadata() {
return "non-identifier";
}
@Override @Override
public String toString() { public String toString() {
return "Attribute[non-identifier]( " + getName() + ")"; return "Attribute(name=" + getName() + ", type=" + getType().getName() + " [" + loggableMetadata() + "])";
} }
} }

View File

@ -54,7 +54,7 @@ import org.hibernate.property.PropertyAccessor;
import org.hibernate.property.PropertyAccessorFactory; import org.hibernate.property.PropertyAccessorFactory;
import org.hibernate.tuple.entity.EntityBasedAssociationAttribute; import org.hibernate.tuple.entity.EntityBasedAssociationAttribute;
import org.hibernate.tuple.entity.EntityBasedBasicAttribute; 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.tuple.entity.VersionProperty;
import org.hibernate.type.AssociationType; import org.hibernate.type.AssociationType;
import org.hibernate.type.CompositeType; import org.hibernate.type.CompositeType;
@ -282,7 +282,7 @@ public class PropertyFactory {
); );
} }
case COMPOSITE: { case COMPOSITE: {
return new EntityBasedCompositeAttribute( return new EntityBasedCompositionAttribute(
persister, persister,
sessionFactory, sessionFactory,
attributeNumber, attributeNumber,

View File

@ -39,7 +39,7 @@ public abstract class AbstractCompositeBasedAttribute
private final int ownerAttributeNumber; private final int ownerAttributeNumber;
public AbstractCompositeBasedAttribute( public AbstractCompositeBasedAttribute(
AbstractCompositeDefinition source, AbstractCompositionDefinition source,
SessionFactoryImplementor sessionFactory, SessionFactoryImplementor sessionFactory,
int attributeNumber, int attributeNumber,
String attributeName, String attributeName,
@ -55,7 +55,7 @@ public abstract class AbstractCompositeBasedAttribute
} }
@Override @Override
public AbstractCompositeDefinition getSource() { public AbstractCompositionDefinition getSource() {
return (AbstractCompositeDefinition) super.getSource(); return (AbstractCompositionDefinition) super.getSource();
} }
} }

View File

@ -32,7 +32,7 @@ import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.persister.walking.spi.AssociationKey; import org.hibernate.persister.walking.spi.AssociationKey;
import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeSource; 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.persister.walking.spi.EntityDefinition;
import org.hibernate.tuple.AbstractNonIdentifierAttribute; import org.hibernate.tuple.AbstractNonIdentifierAttribute;
import org.hibernate.tuple.BaselineAttributeInformation; import org.hibernate.tuple.BaselineAttributeInformation;
@ -48,8 +48,9 @@ import static org.hibernate.engine.internal.JoinHelper.getRHSColumnNames;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public abstract class AbstractCompositeDefinition extends AbstractNonIdentifierAttribute implements CompositeDefinition { public abstract class AbstractCompositionDefinition extends AbstractNonIdentifierAttribute implements
protected AbstractCompositeDefinition( CompositionDefinition {
protected AbstractCompositionDefinition(
AttributeSource source, AttributeSource source,
SessionFactoryImplementor sessionFactory, SessionFactoryImplementor sessionFactory,
int attributeNumber, int attributeNumber,
@ -119,41 +120,41 @@ public abstract class AbstractCompositeDefinition extends AbstractNonIdentifierA
} }
return new CompositeBasedAssociationAttribute( return new CompositeBasedAssociationAttribute(
AbstractCompositeDefinition.this, AbstractCompositionDefinition.this,
sessionFactory(), sessionFactory(),
currentAttributeNumber, currentAttributeNumber,
name, name,
(AssociationType) type, (AssociationType) type,
new BaselineAttributeInformation.Builder() new BaselineAttributeInformation.Builder()
.setInsertable( AbstractCompositeDefinition.this.isInsertable() ) .setInsertable( AbstractCompositionDefinition.this.isInsertable() )
.setUpdateable( AbstractCompositeDefinition.this.isUpdateable() ) .setUpdateable( AbstractCompositionDefinition.this.isUpdateable() )
.setInsertGenerated( AbstractCompositeDefinition.this.isInsertGenerated() ) .setInsertGenerated( AbstractCompositionDefinition.this.isInsertGenerated() )
.setUpdateGenerated( AbstractCompositeDefinition.this.isUpdateGenerated() ) .setUpdateGenerated( AbstractCompositionDefinition.this.isUpdateGenerated() )
.setNullable( getType().getPropertyNullability()[currentAttributeNumber] ) .setNullable( getType().getPropertyNullability()[currentAttributeNumber] )
.setDirtyCheckable( true ) .setDirtyCheckable( true )
.setVersionable( AbstractCompositeDefinition.this.isVersionable() ) .setVersionable( AbstractCompositionDefinition.this.isVersionable() )
.setCascadeStyle( getType().getCascadeStyle( currentAttributeNumber ) ) .setCascadeStyle( getType().getCascadeStyle( currentAttributeNumber ) )
.setFetchMode( getType().getFetchMode( currentAttributeNumber ) ) .setFetchMode( getType().getFetchMode( currentAttributeNumber ) )
.createInformation(), .createInformation(),
AbstractCompositeDefinition.this.attributeNumber(), AbstractCompositionDefinition.this.attributeNumber(),
associationKey associationKey
); );
} }
else if ( type.isComponentType() ) { else if ( type.isComponentType() ) {
return new CompositeBasedCompositeAttribute( return new CompositionBasedCompositionAttribute(
AbstractCompositeDefinition.this, AbstractCompositionDefinition.this,
sessionFactory(), sessionFactory(),
currentAttributeNumber, currentAttributeNumber,
name, name,
(CompositeType) type, (CompositeType) type,
new BaselineAttributeInformation.Builder() new BaselineAttributeInformation.Builder()
.setInsertable( AbstractCompositeDefinition.this.isInsertable() ) .setInsertable( AbstractCompositionDefinition.this.isInsertable() )
.setUpdateable( AbstractCompositeDefinition.this.isUpdateable() ) .setUpdateable( AbstractCompositionDefinition.this.isUpdateable() )
.setInsertGenerated( AbstractCompositeDefinition.this.isInsertGenerated() ) .setInsertGenerated( AbstractCompositionDefinition.this.isInsertGenerated() )
.setUpdateGenerated( AbstractCompositeDefinition.this.isUpdateGenerated() ) .setUpdateGenerated( AbstractCompositionDefinition.this.isUpdateGenerated() )
.setNullable( getType().getPropertyNullability()[currentAttributeNumber] ) .setNullable( getType().getPropertyNullability()[currentAttributeNumber] )
.setDirtyCheckable( true ) .setDirtyCheckable( true )
.setVersionable( AbstractCompositeDefinition.this.isVersionable() ) .setVersionable( AbstractCompositionDefinition.this.isVersionable() )
.setCascadeStyle( getType().getCascadeStyle( currentAttributeNumber ) ) .setCascadeStyle( getType().getCascadeStyle( currentAttributeNumber ) )
.setFetchMode( getType().getFetchMode( currentAttributeNumber ) ) .setFetchMode( getType().getFetchMode( currentAttributeNumber ) )
.createInformation() .createInformation()
@ -161,19 +162,19 @@ public abstract class AbstractCompositeDefinition extends AbstractNonIdentifierA
} }
else { else {
return new CompositeBasedBasicAttribute( return new CompositeBasedBasicAttribute(
AbstractCompositeDefinition.this, AbstractCompositionDefinition.this,
sessionFactory(), sessionFactory(),
currentAttributeNumber, currentAttributeNumber,
name, name,
type, type,
new BaselineAttributeInformation.Builder() new BaselineAttributeInformation.Builder()
.setInsertable( AbstractCompositeDefinition.this.isInsertable() ) .setInsertable( AbstractCompositionDefinition.this.isInsertable() )
.setUpdateable( AbstractCompositeDefinition.this.isUpdateable() ) .setUpdateable( AbstractCompositionDefinition.this.isUpdateable() )
.setInsertGenerated( AbstractCompositeDefinition.this.isInsertGenerated() ) .setInsertGenerated( AbstractCompositionDefinition.this.isInsertGenerated() )
.setUpdateGenerated( AbstractCompositeDefinition.this.isUpdateGenerated() ) .setUpdateGenerated( AbstractCompositionDefinition.this.isUpdateGenerated() )
.setNullable( getType().getPropertyNullability()[currentAttributeNumber] ) .setNullable( getType().getPropertyNullability()[currentAttributeNumber] )
.setDirtyCheckable( true ) .setDirtyCheckable( true )
.setVersionable( AbstractCompositeDefinition.this.isVersionable() ) .setVersionable( AbstractCompositionDefinition.this.isVersionable() )
.setCascadeStyle( getType().getCascadeStyle( currentAttributeNumber ) ) .setCascadeStyle( getType().getCascadeStyle( currentAttributeNumber ) )
.setFetchMode( getType().getFetchMode( currentAttributeNumber ) ) .setFetchMode( getType().getFetchMode( currentAttributeNumber ) )
.createInformation() .createInformation()
@ -195,8 +196,13 @@ public abstract class AbstractCompositeDefinition extends AbstractNonIdentifierA
return ( (EntityDefinition) getSource() ).getEntityPersister(); return ( (EntityDefinition) getSource() ).getEntityPersister();
} }
else { else {
return ( (AbstractCompositeDefinition) getSource() ).locateOwningPersister(); return ( (AbstractCompositionDefinition) getSource() ).locateOwningPersister();
} }
}
@Override
protected String loggableMetadata() {
return super.loggableMetadata() + ",composition";
} }
} }

View File

@ -23,6 +23,8 @@
*/ */
package org.hibernate.tuple.component; package org.hibernate.tuple.component;
import java.io.Serializable;
import org.hibernate.FetchMode; import org.hibernate.FetchMode;
import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchStyle;
@ -34,6 +36,7 @@ import org.hibernate.loader.PropertyPath;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable; import org.hibernate.persister.entity.Joinable;
import org.hibernate.persister.spi.HydratedCompoundValueHandler;
import org.hibernate.persister.walking.internal.Helper; import org.hibernate.persister.walking.internal.Helper;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AssociationKey; import org.hibernate.persister.walking.spi.AssociationKey;
@ -54,7 +57,7 @@ public class CompositeBasedAssociationAttribute
private Joinable joinable; private Joinable joinable;
public CompositeBasedAssociationAttribute( public CompositeBasedAssociationAttribute(
AbstractCompositeDefinition source, AbstractCompositionDefinition source,
SessionFactoryImplementor factory, SessionFactoryImplementor factory,
int attributeNumber, int attributeNumber,
String attributeName, String attributeName,
@ -154,4 +157,29 @@ public class CompositeBasedAssociationAttribute
final CompositeType compositeType = (CompositeType) locateOwningPersister().getPropertyType( getName() ); final CompositeType compositeType = (CompositeType) locateOwningPersister().getPropertyType( getName() );
return compositeType.getCascadeStyle( attributeNumber() ); 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";
}
} }

View File

@ -24,18 +24,18 @@
package org.hibernate.tuple.component; package org.hibernate.tuple.component;
import org.hibernate.engine.spi.SessionFactoryImplementor; 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.tuple.BaselineAttributeInformation;
import org.hibernate.type.CompositeType; import org.hibernate.type.CompositeType;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class CompositeBasedCompositeAttribute public class CompositionBasedCompositionAttribute
extends AbstractCompositeDefinition extends AbstractCompositionDefinition
implements CompositeDefinition { implements CompositionDefinition {
public CompositeBasedCompositeAttribute( public CompositionBasedCompositionAttribute(
CompositeDefinition source, CompositionDefinition source,
SessionFactoryImplementor sessionFactory, SessionFactoryImplementor sessionFactory,
int attributeNumber, int attributeNumber,
String attributeName, String attributeName,

View File

@ -33,6 +33,7 @@ import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable; import org.hibernate.persister.entity.Joinable;
import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.persister.spi.HydratedCompoundValueHandler;
import org.hibernate.persister.walking.internal.Helper; import org.hibernate.persister.walking.internal.Helper;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AssociationKey; import org.hibernate.persister.walking.spi.AssociationKey;
@ -152,4 +153,29 @@ public class EntityBasedAssociationAttribute
public CascadeStyle determineCascadeStyle() { public CascadeStyle determineCascadeStyle() {
return getSource().getEntityPersister().getPropertyCascadeStyles()[attributeNumber()]; 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";
}
} }

View File

@ -25,19 +25,19 @@ package org.hibernate.tuple.entity;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.tuple.component.AbstractCompositeDefinition; import org.hibernate.tuple.component.AbstractCompositionDefinition;
import org.hibernate.persister.walking.spi.CompositeDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.tuple.BaselineAttributeInformation; import org.hibernate.tuple.BaselineAttributeInformation;
import org.hibernate.type.CompositeType; import org.hibernate.type.CompositeType;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class EntityBasedCompositeAttribute public class EntityBasedCompositionAttribute
extends AbstractCompositeDefinition extends AbstractCompositionDefinition
implements CompositeDefinition { implements CompositionDefinition {
public EntityBasedCompositeAttribute( public EntityBasedCompositionAttribute(
EntityPersister source, EntityPersister source,
SessionFactoryImplementor factory, SessionFactoryImplementor factory,
int attributeNumber, int attributeNumber,

View File

@ -119,9 +119,9 @@ public class LoadPlanBuilderTest extends BaseCoreFunctionalTestCase {
CollectionReturn collectionReturn = ExtraAssertions.assertTyping( CollectionReturn.class, rtn ); CollectionReturn collectionReturn = ExtraAssertions.assertTyping( CollectionReturn.class, rtn );
assertEquals( "abc", collectionReturn.getAlias() ); assertEquals( "abc", collectionReturn.getAlias() );
assertNotNull( collectionReturn.getFetches() ); assertNotNull( collectionReturn.getElementGraph().getFetches() );
assertEquals( 1, collectionReturn.getFetches().length ); // the collection elements are fetched assertEquals( 1, collectionReturn.getElementGraph().getFetches().length ); // the collection elements are fetched
Fetch fetch = collectionReturn.getFetches()[0]; Fetch fetch = collectionReturn.getElementGraph().getFetches()[0];
EntityFetch entityFetch = ExtraAssertions.assertTyping( EntityFetch.class, fetch ); EntityFetch entityFetch = ExtraAssertions.assertTyping( EntityFetch.class, fetch );
assertNotNull( entityFetch.getFetches() ); assertNotNull( entityFetch.getFetches() );
assertEquals( 0, entityFetch.getFetches().length ); assertEquals( 0, entityFetch.getFetches().length );
@ -130,8 +130,8 @@ public class LoadPlanBuilderTest extends BaseCoreFunctionalTestCase {
@Entity( name = "Message" ) @Entity( name = "Message" )
public static class Message { public static class Message {
@Id @Id
private Integer id; private Integer mid;
private String name; private String msgTxt;
@ManyToOne( cascade = CascadeType.MERGE ) @ManyToOne( cascade = CascadeType.MERGE )
@JoinColumn @JoinColumn
private Poster poster; private Poster poster;
@ -140,7 +140,7 @@ public class LoadPlanBuilderTest extends BaseCoreFunctionalTestCase {
@Entity( name = "Poster" ) @Entity( name = "Poster" )
public static class Poster { public static class Poster {
@Id @Id
private Integer id; private Integer pid;
private String name; private String name;
@OneToMany(mappedBy = "poster") @OneToMany(mappedBy = "poster")
private List<Message> messages; private List<Message> messages;

View File

@ -35,8 +35,11 @@ import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AssociationVisitationStrategy; import org.hibernate.persister.walking.spi.AssociationVisitationStrategy;
import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.CollectionDefinition; 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.EntityDefinition;
import org.hibernate.persister.walking.spi.EntityIdentifierDefinition;
import org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor; import org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor;
import org.junit.Test; 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 @Override
public void startingCollection(CollectionDefinition collectionDefinition) { public void startingCollection(CollectionDefinition collectionDefinition) {
System.out.println( System.out.println(
@ -114,23 +127,43 @@ public class BasicWalkingTest extends BaseCoreFunctionalTestCase {
} }
@Override @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( System.out.println(
String.format( String.format(
"%s Starting composite (%s)", "%s Starting composite (%s)",
StringHelper.repeat( ">>", ++depth ), StringHelper.repeat( ">>", ++depth ),
compositeDefinition.toString() compositionDefinition.toString()
) )
); );
} }
@Override @Override
public void finishingComposite(CompositeDefinition compositeDefinition) { public void finishingComposite(CompositionDefinition compositionDefinition) {
System.out.println( System.out.println(
String.format( String.format(
"%s Finishing composite (%s)", "%s Finishing composite (%s)",
StringHelper.repeat( ">>", depth-- ), StringHelper.repeat( ">>", depth-- ),
compositeDefinition.toString() compositionDefinition.toString()
) )
); );
} }

View File

@ -58,6 +58,7 @@ import org.hibernate.persister.spi.PersisterClassResolver;
import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.CollectionElementDefinition; import org.hibernate.persister.walking.spi.CollectionElementDefinition;
import org.hibernate.persister.walking.spi.CollectionIndexDefinition; 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.EntityMetamodel;
import org.hibernate.tuple.entity.EntityTuplizer; import org.hibernate.tuple.entity.EntityTuplizer;
import org.hibernate.tuple.entity.NonPojoInstrumentationMetadata; import org.hibernate.tuple.entity.NonPojoInstrumentationMetadata;
@ -590,8 +591,8 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
} }
@Override @Override
public Iterable<AttributeDefinition> getEmbeddedCompositeIdentifierAttributes() { public EntityIdentifierDefinition getEntityKeyDefinition() {
throw new NotYetImplementedException(); return null; //To change body of implemented methods use File | Settings | File Templates.
} }
@Override @Override

View File

@ -36,6 +36,7 @@ import org.hibernate.mapping.PersistentClass;
import org.hibernate.metadata.ClassMetadata; import org.hibernate.metadata.ClassMetadata;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AttributeDefinition; 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.EntityMetamodel;
import org.hibernate.tuple.entity.EntityTuplizer; import org.hibernate.tuple.entity.EntityTuplizer;
import org.hibernate.tuple.entity.NonPojoInstrumentationMetadata; import org.hibernate.tuple.entity.NonPojoInstrumentationMetadata;
@ -680,7 +681,7 @@ public class CustomPersister implements EntityPersister {
} }
@Override @Override
public Iterable<AttributeDefinition> getEmbeddedCompositeIdentifierAttributes() { public EntityIdentifierDefinition getEntityKeyDefinition() {
throw new NotYetImplementedException(); throw new NotYetImplementedException();
} }

View File

@ -9,6 +9,8 @@ log4j.rootLogger=info, stdout
log4j.logger.org.hibernate.tool.hbm2ddl=trace log4j.logger.org.hibernate.tool.hbm2ddl=trace
log4j.logger.org.hibernate.testing.cache=debug log4j.logger.org.hibernate.testing.cache=debug
log4j.logger.org.hibernate.loader.plan=trace
# SQL Logging - HHH-6833 # SQL Logging - HHH-6833
log4j.logger.org.hibernate.SQL=debug log4j.logger.org.hibernate.SQL=debug