From a102bf2c31d931044504ad460bee0435695042be Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Mon, 4 Mar 2013 15:58:16 -0600 Subject: [PATCH] HHH-7841 - Redesign Loader --- .../org/hibernate/engine/FetchStrategy.java | 50 ++++ .../CascadeLoadPlanBuilderStrategy.java | 60 ++++ .../loader/plan/internal/LoadPlanImpl.java | 59 ++++ ...ngleRootReturnLoadPlanBuilderStrategy.java | 268 ++++++++++++++++++ .../loader/plan/spi/AbstractFetch.java | 88 ++++++ .../loader/plan/spi/AbstractFetchOwner.java | 75 +++++ .../spi/AbstractLoadPlanBuilderStrategy.java | 212 ++++++++++++++ .../loader/plan/spi/AbstractPlanNode.java | 43 +++ .../loader/plan/spi/CollectionFetch.java | 80 ++++++ .../loader/plan/spi/CollectionReference.java | 73 +++++ .../loader/plan/spi/CollectionReturn.java | 113 ++++++++ .../loader/plan/spi/CompositeFetch.java | 51 ++++ .../loader/plan/spi/EntityFetch.java | 78 +++++ .../loader/plan/spi/EntityReference.java | 73 +++++ .../loader/plan/spi/EntityReturn.java | 96 +++++++ .../org/hibernate/loader/plan/spi/Fetch.java | 59 ++++ .../hibernate/loader/plan/spi/FetchOwner.java | 68 +++++ .../hibernate/loader/plan/spi/LoadPlan.java | 61 ++++ .../loader/plan/spi/LoadPlanBuilder.java | 64 +++++ .../plan/spi/LoadPlanBuilderStrategy.java | 40 +++ .../org/hibernate/loader/plan/spi/Return.java | 40 +++ .../plan/spi/ReturnVisitationStrategy.java | 138 +++++++++ .../loader/plan/spi/ReturnVisitor.java | 116 ++++++++ .../loader/plan/spi/ScalarReturn.java | 52 ++++ .../AbstractCollectionPersister.java | 81 ++++++ .../collection/CollectionPersister.java | 3 +- .../entity/AbstractEntityPersister.java | 116 +++++++- .../persister/entity/EntityPersister.java | 15 +- .../persister/walking/internal/Helper.java | 160 +++++++++++ .../spi/AssociationAttributeDefinition.java | 46 +++ .../persister/walking/spi/AssociationKey.java | 53 ++++ .../spi/AssociationVisitationStrategy.java | 51 ++++ .../walking/spi/AttributeDefinition.java | 35 +++ .../walking/spi/AttributeSource.java | 31 ++ .../walking/spi/CollectionDefinition.java | 38 +++ .../spi/CollectionElementDefinition.java | 39 +++ .../spi/CollectionIndexDefinition.java | 39 +++ .../walking/spi/CompositeDefinition.java | 30 ++ .../walking/spi/EntityDefinition.java | 36 +++ .../spi/MetadataDrivenModelGraphVisitor.java | 208 ++++++++++++++ .../persister/walking/spi/package-info.java | 6 + .../hibernate/tuple/AbstractAttribute.java | 55 ++++ .../tuple/AbstractNonIdentifierAttribute.java | 133 +++++++++ .../java/org/hibernate/tuple/Attribute.java | 37 +++ .../tuple/BaselineAttributeInformation.java | 166 +++++++++++ .../hibernate/tuple/IdentifierAttribute.java | 21 ++ .../hibernate/tuple/IdentifierProperty.java | 23 +- .../tuple/NonIdentifierAttribute.java | 55 ++++ .../java/org/hibernate/tuple/Property.java | 48 +--- .../org/hibernate/tuple/PropertyFactory.java | 249 +++++++++++----- .../org/hibernate/tuple/StandardProperty.java | 159 ++++------- .../org/hibernate/tuple/VersionProperty.java | 81 ------ .../AbstractCompositeBasedAttribute.java | 61 ++++ .../AbstractCompositeDefinition.java | 202 +++++++++++++ .../tuple/component/ComponentMetamodel.java | 2 +- .../CompositeBasedAssociationAttribute.java | 157 ++++++++++ .../CompositeBasedBasicAttribute.java | 45 +++ .../CompositeBasedCompositeAttribute.java | 46 +++ .../entity/AbstractEntityBasedAttribute.java | 50 ++++ .../tuple/entity/AbstractEntityTuplizer.java | 5 +- .../EntityBasedAssociationAttribute.java | 132 +++++++++ .../entity/EntityBasedBasicAttribute.java | 44 +++ .../entity/EntityBasedCompositeAttribute.java | 49 ++++ .../tuple/entity/EntityMetamodel.java | 64 +++-- .../tuple/entity/VersionProperty.java | 70 +++++ .../java/org/hibernate/type/TypeHelper.java | 6 +- .../loader/plan/spi/LoadPlanBuilderTest.java | 149 ++++++++++ .../persister/walking/BasicWalkingTest.java | 177 ++++++++++++ .../GoofyPersisterClassProvider.java | 36 ++- .../test/legacy/CustomPersister.java | 17 ++ 70 files changed, 5048 insertions(+), 335 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/engine/FetchStrategy.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/internal/CascadeLoadPlanBuilderStrategy.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanImpl.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/internal/SingleRootReturnLoadPlanBuilderStrategy.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetch.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwner.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractLoadPlanBuilderStrategy.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractPlanNode.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionReference.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionReturn.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReference.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwner.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlan.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlanBuilder.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlanBuilderStrategy.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Return.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ReturnVisitationStrategy.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ReturnVisitor.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ScalarReturn.java create mode 100644 hibernate-core/src/main/java/org/hibernate/persister/walking/internal/Helper.java create mode 100644 hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationAttributeDefinition.java create mode 100644 hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationKey.java create mode 100644 hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java create mode 100644 hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AttributeDefinition.java create mode 100644 hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AttributeSource.java create mode 100644 hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionDefinition.java create mode 100644 hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionElementDefinition.java create mode 100644 hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionIndexDefinition.java create mode 100644 hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositeDefinition.java create mode 100644 hibernate-core/src/main/java/org/hibernate/persister/walking/spi/EntityDefinition.java create mode 100644 hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java create mode 100644 hibernate-core/src/main/java/org/hibernate/persister/walking/spi/package-info.java create mode 100644 hibernate-core/src/main/java/org/hibernate/tuple/AbstractAttribute.java create mode 100644 hibernate-core/src/main/java/org/hibernate/tuple/AbstractNonIdentifierAttribute.java create mode 100644 hibernate-core/src/main/java/org/hibernate/tuple/Attribute.java create mode 100644 hibernate-core/src/main/java/org/hibernate/tuple/BaselineAttributeInformation.java create mode 100644 hibernate-core/src/main/java/org/hibernate/tuple/IdentifierAttribute.java create mode 100644 hibernate-core/src/main/java/org/hibernate/tuple/NonIdentifierAttribute.java delete mode 100644 hibernate-core/src/main/java/org/hibernate/tuple/VersionProperty.java create mode 100644 hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeBasedAttribute.java create mode 100644 hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeDefinition.java create mode 100644 hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java create mode 100644 hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedBasicAttribute.java create mode 100644 hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedCompositeAttribute.java create mode 100644 hibernate-core/src/main/java/org/hibernate/tuple/entity/AbstractEntityBasedAttribute.java create mode 100644 hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedAssociationAttribute.java create mode 100644 hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedBasicAttribute.java create mode 100644 hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedCompositeAttribute.java create mode 100644 hibernate-core/src/main/java/org/hibernate/tuple/entity/VersionProperty.java create mode 100644 hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanBuilderTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/engine/FetchStrategy.java b/hibernate-core/src/main/java/org/hibernate/engine/FetchStrategy.java new file mode 100644 index 0000000000..8264eee79a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/FetchStrategy.java @@ -0,0 +1,50 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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.engine; + +/** + * Describes the strategy for fetching an association + *

+ * todo not really a fan of the name. not sure of a better one though. + * I'd almost rather see this be called the style, but then what to call FetchStyle? HowToFetch? + * + * @author Steve Ebersole + */ +public class FetchStrategy { + private final FetchTiming timing; + private final FetchStyle style; + + public FetchStrategy(FetchTiming timing, FetchStyle style) { + this.timing = timing; + this.style = style; + } + + public FetchTiming getTiming() { + return timing; + } + + public FetchStyle getStyle() { + return style; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/CascadeLoadPlanBuilderStrategy.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/CascadeLoadPlanBuilderStrategy.java new file mode 100644 index 0000000000..ce19b46079 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/CascadeLoadPlanBuilderStrategy.java @@ -0,0 +1,60 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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.engine.FetchStrategy; +import org.hibernate.engine.FetchStyle; +import org.hibernate.engine.FetchTiming; +import org.hibernate.engine.spi.CascadingAction; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; + +/** + * A LoadPlan building strategy for cascade processing; meaning, it builds the LoadPlan for loading related to + * cascading a particular action across associations + * + * @author Steve Ebersole + */ +public class CascadeLoadPlanBuilderStrategy extends SingleRootReturnLoadPlanBuilderStrategy { + private static final FetchStrategy EAGER = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN ); + private static final FetchStrategy DELAYED = new FetchStrategy( FetchTiming.DELAYED, FetchStyle.SELECT ); + + private final CascadingAction cascadeActionToMatch; + + public CascadeLoadPlanBuilderStrategy( + CascadingAction cascadeActionToMatch, + SessionFactoryImplementor sessionFactory, + LoadQueryInfluencers loadQueryInfluencers, + String rootAlias, + int suffixSeed) { + super( sessionFactory, loadQueryInfluencers, rootAlias, suffixSeed ); + this.cascadeActionToMatch = cascadeActionToMatch; + } + + @Override + protected FetchStrategy determineFetchPlan(AssociationAttributeDefinition attributeDefinition) { + return attributeDefinition.determineCascadeStyle().doCascade( cascadeActionToMatch ) ? EAGER : DELAYED; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanImpl.java new file mode 100644 index 0000000000..47daeb3c13 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanImpl.java @@ -0,0 +1,59 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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 java.util.Collections; +import java.util.List; + +import org.hibernate.loader.plan.spi.LoadPlan; +import org.hibernate.loader.plan.spi.Return; + +/** + * Implementation of LoadPlan. + * + * @author Steve Ebersole + */ +public class LoadPlanImpl implements LoadPlan { + private final boolean hasScalars; + private final List returns; + + public LoadPlanImpl(boolean hasScalars, List returns) { + this.hasScalars = hasScalars; + this.returns = returns; + } + + public LoadPlanImpl(boolean hasScalars, Return rootReturn) { + this( hasScalars, Collections.singletonList( rootReturn ) ); + } + + @Override + public boolean hasAnyScalarReturns() { + return hasScalars; + } + + @Override + public List getReturns() { + return returns; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/SingleRootReturnLoadPlanBuilderStrategy.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/SingleRootReturnLoadPlanBuilderStrategy.java new file mode 100644 index 0000000000..f329b30e26 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/SingleRootReturnLoadPlanBuilderStrategy.java @@ -0,0 +1,268 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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.HibernateException; +import org.hibernate.LockMode; +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.FetchStyle; +import org.hibernate.engine.FetchTiming; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.internal.util.StringHelper; +import org.hibernate.loader.CollectionAliases; +import org.hibernate.loader.DefaultEntityAliases; +import org.hibernate.loader.EntityAliases; +import org.hibernate.loader.GeneratedCollectionAliases; +import org.hibernate.loader.PropertyPath; +import org.hibernate.loader.plan.spi.AbstractFetchOwner; +import org.hibernate.loader.plan.spi.AbstractLoadPlanBuilderStrategy; +import org.hibernate.loader.plan.spi.CollectionFetch; +import org.hibernate.loader.plan.spi.CollectionReturn; +import org.hibernate.loader.plan.spi.CompositeFetch; +import org.hibernate.loader.plan.spi.EntityFetch; +import org.hibernate.loader.plan.spi.EntityReturn; +import org.hibernate.loader.plan.spi.FetchOwner; +import org.hibernate.loader.plan.spi.LoadPlan; +import org.hibernate.loader.plan.spi.LoadPlanBuilderStrategy; +import org.hibernate.loader.plan.spi.Return; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.entity.Loadable; +import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; +import org.hibernate.persister.walking.spi.CollectionDefinition; +import org.hibernate.persister.walking.spi.CompositeDefinition; +import org.hibernate.persister.walking.spi.EntityDefinition; +import org.hibernate.type.EntityType; +import org.hibernate.type.Type; + +/** + * LoadPlanBuilderStrategy implementation used for building LoadPlans with a single processing RootEntity LoadPlan building. + * + * Really this is a single-root LoadPlan building strategy for building LoadPlans for:

+ * + * @author Steve Ebersole + */ +public class SingleRootReturnLoadPlanBuilderStrategy + extends AbstractLoadPlanBuilderStrategy + implements LoadPlanBuilderStrategy { + + private final LoadQueryInfluencers loadQueryInfluencers; + + private final String rootAlias; + private int currentSuffixBase; + + private Return rootReturn; + + private PropertyPath propertyPath = new PropertyPath( "" ); + + public SingleRootReturnLoadPlanBuilderStrategy( + SessionFactoryImplementor sessionFactory, + LoadQueryInfluencers loadQueryInfluencers, + String rootAlias, + int suffixSeed) { + super( sessionFactory ); + this.loadQueryInfluencers = loadQueryInfluencers; + this.rootAlias = rootAlias; + this.currentSuffixBase = suffixSeed; + } + + @Override + protected boolean supportsRootEntityReturns() { + return true; + } + + @Override + protected boolean supportsRootCollectionReturns() { + return true; + } + + @Override + protected void addRootReturn(Return rootReturn) { + if ( this.rootReturn != null ) { + throw new HibernateException( "Root return already identified" ); + } + this.rootReturn = rootReturn; + } + + @Override + public LoadPlan buildLoadPlan() { + return new LoadPlanImpl( false, rootReturn ); + } + + @Override + protected FetchStrategy determineFetchPlan(AssociationAttributeDefinition attributeDefinition) { + FetchStrategy fetchStrategy = attributeDefinition.determineFetchPlan( loadQueryInfluencers, propertyPath ); + if ( fetchStrategy.getTiming() == FetchTiming.IMMEDIATE && fetchStrategy.getStyle() == FetchStyle.JOIN ) { + // see if we need to alter the join fetch to another form for any reason + fetchStrategy = adjustJoinFetchIfNeeded( attributeDefinition, fetchStrategy ); + } + return fetchStrategy; + } + + protected FetchStrategy adjustJoinFetchIfNeeded( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy) { + if ( currentDepth() > sessionFactory().getSettings().getMaximumFetchDepth() ) { + return new FetchStrategy( fetchStrategy.getTiming(), FetchStyle.SELECT ); + } + + if ( attributeDefinition.getType().isCollectionType() && isTooManyCollections() ) { + // todo : have this revert to batch or subselect fetching once "sql gen redesign" is in place + return new FetchStrategy( fetchStrategy.getTiming(), FetchStyle.SELECT ); + } + + return fetchStrategy; + } + + @Override + protected boolean isTooManyCollections() { + return false; + } + + @Override + protected EntityReturn buildRootEntityReturn(EntityDefinition entityDefinition) { + final String entityName = entityDefinition.getEntityPersister().getEntityName(); + return new EntityReturn( + sessionFactory(), + rootAlias, + LockMode.NONE, // todo : for now + entityName, + StringHelper.generateAlias( StringHelper.unqualifyEntityName( entityName ), currentDepth() ), + new DefaultEntityAliases( + (Loadable) entityDefinition.getEntityPersister(), + Integer.toString( currentSuffixBase++ ) + '_' + ) + ); + } + + @Override + protected CollectionReturn buildRootCollectionReturn(CollectionDefinition collectionDefinition) { + final CollectionPersister persister = collectionDefinition.getCollectionPersister(); + final String collectionRole = persister.getRole(); + + 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 CollectionReturn( + sessionFactory(), + rootAlias, + LockMode.NONE, // todo : for now + persister.getOwnerEntityPersister().getEntityName(), + StringHelper.unqualify( collectionRole ), + collectionAliases, + elementAliases + ); + } + + @Override + protected CollectionFetch buildCollectionFetch( + FetchOwner fetchOwner, + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy) { + final CollectionDefinition collectionDefinition = attributeDefinition.toCollectionDefinition(); + final CollectionAliases collectionAliases = new GeneratedCollectionAliases( + collectionDefinition.getCollectionPersister(), + Integer.toString( currentSuffixBase++ ) + '_' + ); + final Type elementType = collectionDefinition.getCollectionPersister().getElementType(); + final EntityAliases elementAliases; + if ( elementType.isEntityType() ) { + final EntityType entityElementType = (EntityType) elementType; + elementAliases = new DefaultEntityAliases( + (Loadable) entityElementType.getAssociatedJoinable( sessionFactory() ), + Integer.toString( currentSuffixBase++ ) + '_' + ); + } + else { + elementAliases = null; + } + + return new CollectionFetch( + sessionFactory(), + createImplicitAlias(), + LockMode.NONE, // todo : for now + (AbstractFetchOwner) fetchOwner, + fetchStrategy, + attributeDefinition.getName(), + collectionAliases, + elementAliases + ); + } + + @Override + protected EntityFetch buildEntityFetch( + FetchOwner fetchOwner, + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy) { + final EntityDefinition entityDefinition = attributeDefinition.toEntityDefinition(); + + return new EntityFetch( + sessionFactory(), + createImplicitAlias(), + LockMode.NONE, // todo : for now + (AbstractFetchOwner) fetchOwner, + attributeDefinition.getName(), + fetchStrategy, + StringHelper.generateAlias( entityDefinition.getEntityPersister().getEntityName(), currentDepth() ), + new DefaultEntityAliases( + (Loadable) entityDefinition.getEntityPersister(), + Integer.toString( currentSuffixBase++ ) + '_' + ) + ); + } + + @Override + protected CompositeFetch buildCompositeFetch(FetchOwner fetchOwner, CompositeDefinition attributeDefinition) { + return new CompositeFetch( + sessionFactory(), + createImplicitAlias(), + (AbstractFetchOwner) fetchOwner, + attributeDefinition.getName() + ); + } + + private int implicitAliasUniqueness = 0; + + private String createImplicitAlias() { + return "ia" + implicitAliasUniqueness++; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetch.java new file mode 100644 index 0000000000..e7d2751247 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetch.java @@ -0,0 +1,88 @@ +/* + * 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.plan.spi; + +import org.hibernate.HibernateException; +import org.hibernate.LockMode; +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.FetchStyle; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.PropertyPath; + +/** + * @author Steve Ebersole + */ +public abstract class AbstractFetch extends AbstractFetchOwner implements Fetch { + private final FetchOwner owner; + private final String ownerProperty; + private final FetchStrategy fetchStrategy; + + private final PropertyPath propertyPath; + + public AbstractFetch( + SessionFactoryImplementor factory, + String alias, + LockMode lockMode, + AbstractFetchOwner owner, + String ownerProperty, + FetchStrategy fetchStrategy) { + super( factory, alias, lockMode ); + this.owner = owner; + this.ownerProperty = ownerProperty; + this.fetchStrategy = fetchStrategy; + + owner.addFetch( this ); + + this.propertyPath = owner.getPropertyPath().append( ownerProperty ); + } + + @Override + public FetchOwner getOwner() { + return owner; + } + + @Override + public String getOwnerPropertyName() { + return ownerProperty; + } + + @Override + public FetchStrategy getFetchStrategy() { + return fetchStrategy; + } + + @Override + public void validateFetchPlan(FetchStrategy fetchStrategy) { + if ( fetchStrategy.getStyle() == FetchStyle.JOIN ) { + if ( this.fetchStrategy.getStyle() != FetchStyle.JOIN ) { + throw new HibernateException( "Cannot specify join fetch from owner that is a non-joined fetch" ); + } + } + } + + @Override + public PropertyPath getPropertyPath() { + return propertyPath; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwner.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwner.java new file mode 100644 index 0000000000..87063b9278 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwner.java @@ -0,0 +1,75 @@ +/* + * 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.plan.spi; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.HibernateException; +import org.hibernate.LockMode; +import org.hibernate.engine.spi.SessionFactoryImplementor; + +/** + * @author Steve Ebersole + */ +public abstract class AbstractFetchOwner extends AbstractPlanNode implements FetchOwner { + private final String alias; + private final LockMode lockMode; + + private List fetches; + + public AbstractFetchOwner(SessionFactoryImplementor factory, String alias, LockMode lockMode) { + super( factory ); + this.alias = alias; + if ( alias == null ) { + throw new HibernateException( "alias must be specified" ); + } + this.lockMode = lockMode; + } + + public String getAlias() { + return alias; + } + + public LockMode getLockMode() { + return lockMode; + } + + void addFetch(Fetch fetch) { + if ( fetch.getOwner() != this ) { + throw new IllegalArgumentException( "Fetch and owner did not match" ); + } + + if ( fetches == null ) { + fetches = new ArrayList(); + } + + fetches.add( fetch ); + } + + @Override + public Fetch[] getFetches() { + return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractLoadPlanBuilderStrategy.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractLoadPlanBuilderStrategy.java new file mode 100644 index 0000000000..f9324daa96 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractLoadPlanBuilderStrategy.java @@ -0,0 +1,212 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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.ArrayDeque; + +import org.hibernate.HibernateException; +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.FetchTiming; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; +import org.hibernate.persister.walking.spi.AttributeDefinition; +import org.hibernate.persister.walking.spi.CollectionDefinition; +import org.hibernate.persister.walking.spi.CompositeDefinition; +import org.hibernate.persister.walking.spi.EntityDefinition; +import org.hibernate.type.Type; + +/** + * @author Steve Ebersole + */ +public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilderStrategy { + private final SessionFactoryImplementor sessionFactory; + + private ArrayDeque fetchOwnerStack = new ArrayDeque(); + + protected AbstractLoadPlanBuilderStrategy(SessionFactoryImplementor sessionFactory) { + this.sessionFactory = sessionFactory; + } + + public SessionFactoryImplementor sessionFactory() { + return sessionFactory; + } + + protected FetchOwner currentFetchOwner() { + return fetchOwnerStack.peekLast(); + } + + @Override + public void start() { + // nothing to do + } + + @Override + public void finish() { + // nothing to do + } + + @Override + public void startingEntity(EntityDefinition entityDefinition) { + if ( fetchOwnerStack.isEmpty() ) { + // this is a root... + if ( ! supportsRootEntityReturns() ) { + throw new HibernateException( "This strategy does not support root entity returns" ); + } + final EntityReturn entityReturn = buildRootEntityReturn( entityDefinition ); + addRootReturn( entityReturn ); + fetchOwnerStack.push( entityReturn ); + } + // otherwise this call should represent a fetch which should have been handled in #startingAttribute + } + + protected boolean supportsRootEntityReturns() { + return false; + } + + protected abstract void addRootReturn(Return rootReturn); + + @Override + public void finishingEntity(EntityDefinition entityDefinition) { + // nothing to do + } + + @Override + public void startingCollection(CollectionDefinition collectionDefinition) { + if ( fetchOwnerStack.isEmpty() ) { + // this is a root... + if ( ! supportsRootCollectionReturns() ) { + throw new HibernateException( "This strategy does not support root collection returns" ); + } + final CollectionReturn collectionReturn = buildRootCollectionReturn( collectionDefinition ); + addRootReturn( collectionReturn ); + fetchOwnerStack.push( collectionReturn ); + } + } + + protected boolean supportsRootCollectionReturns() { + return false; + } + + @Override + public void finishingCollection(CollectionDefinition collectionDefinition) { + // nothing to do + } + + @Override + public void startingComposite(CompositeDefinition compositeDefinition) { + if ( fetchOwnerStack.isEmpty() ) { + throw new HibernateException( "A component cannot be the root of a walk nor a graph" ); + } + } + + @Override + public void finishingComposite(CompositeDefinition compositeDefinition) { + // nothing to do + } + + @Override + public boolean startingAttribute(AttributeDefinition attributeDefinition) { + final Type attributeType = attributeDefinition.getType(); + + final boolean isComponentType = attributeType.isComponentType(); + final boolean isBasicType = ! ( isComponentType || attributeType.isAssociationType() ); + + if ( isBasicType ) { + return true; + } + else if ( isComponentType ) { + return handleCompositeAttribute( (CompositeDefinition) attributeDefinition ); + } + else { + return handleAssociationAttribute( (AssociationAttributeDefinition) attributeDefinition ); + } + } + + + @Override + public void finishingAttribute(AttributeDefinition attributeDefinition) { + final Type attributeType = attributeDefinition.getType(); + + final boolean isComponentType = attributeType.isComponentType(); + final boolean isBasicType = ! ( isComponentType || attributeType.isAssociationType() ); + + if ( ! isBasicType ) { + fetchOwnerStack.removeLast(); + } + } + + protected boolean handleCompositeAttribute(CompositeDefinition attributeDefinition) { + final FetchOwner fetchOwner = fetchOwnerStack.peekLast(); + final CompositeFetch fetch = buildCompositeFetch( fetchOwner, attributeDefinition ); + fetchOwnerStack.addLast( fetch ); + return true; + } + + protected boolean handleAssociationAttribute(AssociationAttributeDefinition attributeDefinition) { + final FetchStrategy fetchStrategy = determineFetchPlan( attributeDefinition ); + if ( fetchStrategy.getTiming() != FetchTiming.IMMEDIATE ) { + return false; + } + + final FetchOwner fetchOwner = fetchOwnerStack.peekLast(); + fetchOwner.validateFetchPlan( fetchStrategy ); + + final Fetch associationFetch; + if ( attributeDefinition.isCollection() ) { + associationFetch = buildCollectionFetch( fetchOwner, attributeDefinition, fetchStrategy ); + } + else { + associationFetch = buildEntityFetch( fetchOwner, attributeDefinition, fetchStrategy ); + } + fetchOwnerStack.addLast( associationFetch ); + + return true; + } + + protected abstract FetchStrategy determineFetchPlan(AssociationAttributeDefinition attributeDefinition); + + protected int currentDepth() { + return fetchOwnerStack.size(); + } + + protected boolean isTooManyCollections() { + return false; + } + + protected abstract EntityReturn buildRootEntityReturn(EntityDefinition entityDefinition); + + protected abstract CollectionReturn buildRootCollectionReturn(CollectionDefinition collectionDefinition); + + protected abstract CollectionFetch buildCollectionFetch( + FetchOwner fetchOwner, + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy); + + protected abstract EntityFetch buildEntityFetch( + FetchOwner fetchOwner, + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy); + + protected abstract CompositeFetch buildCompositeFetch(FetchOwner fetchOwner, CompositeDefinition attributeDefinition); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractPlanNode.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractPlanNode.java new file mode 100644 index 0000000000..eb789612a2 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractPlanNode.java @@ -0,0 +1,43 @@ +/* + * 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.plan.spi; + +import org.hibernate.engine.spi.SessionFactoryImplementor; + +/** + * Base class for LoadPlan nodes to hold references to the session factory. + * + * @author Steve Ebersole + */ +public abstract class AbstractPlanNode { + private final SessionFactoryImplementor sessionFactory; + + public AbstractPlanNode(SessionFactoryImplementor sessionFactory) { + this.sessionFactory = sessionFactory; + } + + protected SessionFactoryImplementor sessionFactory() { + return sessionFactory; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java new file mode 100644 index 0000000000..973d147795 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java @@ -0,0 +1,80 @@ +/* + * 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.plan.spi; + +import org.hibernate.LockMode; +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.CollectionAliases; +import org.hibernate.loader.EntityAliases; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.collection.QueryableCollection; +import org.hibernate.persister.entity.EntityPersister; + +/** + * @author Steve Ebersole + */ +public class CollectionFetch extends AbstractFetch implements CollectionReference { + private final CollectionAliases collectionAliases; + private final EntityAliases elementEntityAliases; + + private final CollectionPersister persister; + + public CollectionFetch( + SessionFactoryImplementor sessionFactory, + String alias, + LockMode lockMode, + AbstractFetchOwner owner, + FetchStrategy fetchStrategy, + String ownerProperty, + CollectionAliases collectionAliases, + EntityAliases elementEntityAliases) { + super( sessionFactory, alias, lockMode, owner, ownerProperty, fetchStrategy ); + this.collectionAliases = collectionAliases; + this.elementEntityAliases = elementEntityAliases; + + final String role = owner.retrieveFetchSourcePersister().getEntityName() + '.' + getOwnerPropertyName(); + this.persister = sessionFactory.getCollectionPersister( role ); + } + + @Override + public CollectionAliases getCollectionAliases() { + return collectionAliases; + } + + @Override + public EntityAliases getElementEntityAliases() { + return elementEntityAliases; + } + + @Override + public CollectionPersister getCollectionPersister() { + return persister; + } + + @Override + public EntityPersister retrieveFetchSourcePersister() { + return ( (QueryableCollection) getCollectionPersister() ).getElementPersister(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionReference.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionReference.java new file mode 100644 index 0000000000..fa0460e922 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionReference.java @@ -0,0 +1,73 @@ +/* + * 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.plan.spi; + +import org.hibernate.LockMode; +import org.hibernate.loader.CollectionAliases; +import org.hibernate.loader.EntityAliases; +import org.hibernate.persister.collection.CollectionPersister; + +/** + * Represents a reference to an owned collection either as a return or as a fetch + * + * @author Steve Ebersole + */ +public interface CollectionReference { + /** + * Retrieve the alias associated with the persister (entity/collection). + * + * @return The alias + */ + public String getAlias(); + + /** + * Retrieve the lock mode associated with this return. + * + * @return The lock mode. + */ + public LockMode getLockMode(); + + /** + * Retrieves the CollectionPersister describing the collection associated with this Return. + * + * @return The CollectionPersister. + */ + public CollectionPersister getCollectionPersister(); + + /** + * Returns the description of the aliases in the JDBC ResultSet that identify values "belonging" to the + * this collection. + * + * @return The ResultSet alias descriptor for the collection + */ + public CollectionAliases getCollectionAliases(); + + /** + * If the elements of this collection are entities, this methods returns the JDBC ResultSet alias descriptions + * for that entity; {@code null} indicates a non-entity collection. + * + * @return The ResultSet alias descriptor for the collection's entity element, or {@code null} + */ + public EntityAliases getElementEntityAliases(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionReturn.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionReturn.java new file mode 100644 index 0000000000..0dacc39806 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionReturn.java @@ -0,0 +1,113 @@ +/* + * 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.plan.spi; + +import org.hibernate.LockMode; +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.CollectionAliases; +import org.hibernate.loader.EntityAliases; +import org.hibernate.loader.PropertyPath; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.collection.QueryableCollection; +import org.hibernate.persister.entity.EntityPersister; + +/** + * @author Steve Ebersole + */ +public class CollectionReturn extends AbstractFetchOwner implements Return, FetchOwner, CollectionReference { + private final String ownerEntityName; + private final String ownerProperty; + private final CollectionAliases collectionAliases; + private final EntityAliases elementEntityAliases; + + private final CollectionPersister persister; + + private final PropertyPath propertyPath = new PropertyPath(); // its a root + + public CollectionReturn( + SessionFactoryImplementor sessionFactory, + String alias, + LockMode lockMode, + String ownerEntityName, + String ownerProperty, + CollectionAliases collectionAliases, + EntityAliases elementEntityAliases) { + super( sessionFactory, alias, lockMode ); + this.ownerEntityName = ownerEntityName; + this.ownerProperty = ownerProperty; + this.collectionAliases = collectionAliases; + this.elementEntityAliases = elementEntityAliases; + + final String role = ownerEntityName + '.' + ownerProperty; + this.persister = sessionFactory.getCollectionPersister( role ); + } + + /** + * Returns the class owning the collection. + * + * @return The class owning the collection. + */ + public String getOwnerEntityName() { + return ownerEntityName; + } + + /** + * Returns the name of the property representing the collection from the {@link #getOwnerEntityName}. + * + * @return The name of the property representing the collection on the owner class. + */ + public String getOwnerProperty() { + return ownerProperty; + } + + @Override + public CollectionAliases getCollectionAliases() { + return collectionAliases; + } + + @Override + public EntityAliases getElementEntityAliases() { + return elementEntityAliases; + } + + @Override + public CollectionPersister getCollectionPersister() { + return persister; + } + + @Override + public void validateFetchPlan(FetchStrategy fetchStrategy) { + } + + @Override + public EntityPersister retrieveFetchSourcePersister() { + return ( (QueryableCollection) persister ).getElementPersister(); + } + + @Override + public PropertyPath getPropertyPath() { + return propertyPath; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java new file mode 100644 index 0000000000..48d2f4c415 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java @@ -0,0 +1,51 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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.FetchStrategy; +import org.hibernate.engine.FetchStyle; +import org.hibernate.engine.FetchTiming; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.persister.entity.EntityPersister; + +/** + * @author Steve Ebersole + */ +public class CompositeFetch extends AbstractFetch implements Fetch { + public static final FetchStrategy FETCH_PLAN = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN ); + + public CompositeFetch( + SessionFactoryImplementor sessionFactory, + String alias, + AbstractFetchOwner owner, + String ownerProperty) { + super( sessionFactory, alias, LockMode.NONE, owner, ownerProperty, FETCH_PLAN ); + } + + @Override + public EntityPersister retrieveFetchSourcePersister() { + return getOwner().retrieveFetchSourcePersister(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java new file mode 100644 index 0000000000..252e3e0288 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java @@ -0,0 +1,78 @@ +/* + * 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.plan.spi; + +import org.hibernate.LockMode; +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.EntityAliases; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.type.EntityType; + +/** + * @author Steve Ebersole + */ +public class EntityFetch extends AbstractFetch implements EntityReference { + private final String sqlTableAlias; + private final EntityAliases entityAliases; + + private final EntityPersister persister; + + public EntityFetch( + SessionFactoryImplementor sessionFactory, + String alias, + LockMode lockMode, + AbstractFetchOwner owner, + String ownerProperty, + FetchStrategy fetchStrategy, + String sqlTableAlias, + EntityAliases entityAliases) { + super( sessionFactory, alias, lockMode, owner, ownerProperty, fetchStrategy ); + this.sqlTableAlias = sqlTableAlias; + this.entityAliases = entityAliases; + + final EntityType type = (EntityType) owner.retrieveFetchSourcePersister().getPropertyType( ownerProperty ); + this.persister = sessionFactory.getEntityPersister( type.getAssociatedEntityName() ); + } + + @Override + public EntityPersister getEntityPersister() { + return persister; + } + + @Override + public EntityAliases getEntityAliases() { + return entityAliases; + } + + @Override + public String getSqlTableAlias() { + return sqlTableAlias; + } + + @Override + public EntityPersister retrieveFetchSourcePersister() { + return persister; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReference.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReference.java new file mode 100644 index 0000000000..d1102acc7b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReference.java @@ -0,0 +1,73 @@ +/* + * 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.plan.spi; + +import org.hibernate.LockMode; +import org.hibernate.loader.EntityAliases; +import org.hibernate.persister.entity.EntityPersister; + +/** + * Represents a reference to an entity either as a return or as a fetch + * + * @author Steve Ebersole + */ +public interface EntityReference { + /** + * Retrieve the alias associated with the persister (entity/collection). + * + * @return The alias + */ + public String getAlias(); + + /** + * Retrieve the lock mode associated with this return. + * + * @return The lock mode. + */ + public LockMode getLockMode(); + + /** + * Retrieves the EntityPersister describing the entity associated with this Return. + * + * @return The EntityPersister. + */ + public EntityPersister getEntityPersister(); + + /** + * Returns the description of the aliases in the JDBC ResultSet that identify values "belonging" to the this entity. + * + * @return The ResultSet alias descriptor. + */ + public EntityAliases getEntityAliases(); + + /** + * Obtain the SQL table alias associated with this entity. + * + * TODO : eventually this needs to not be a String, but a representation like I did for the Antlr3 branch + * (AliasRoot, I think it was called) + * + * @return The SQL table alias for this entity + */ + public String getSqlTableAlias(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java new file mode 100644 index 0000000000..eb14ecf297 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java @@ -0,0 +1,96 @@ +/* + * 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.plan.spi; + +import org.hibernate.LockMode; +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.EntityAliases; +import org.hibernate.loader.PropertyPath; +import org.hibernate.persister.entity.EntityPersister; + +/** + * @author Steve Ebersole + */ +public class EntityReturn extends AbstractFetchOwner implements Return, FetchOwner, EntityReference { + private final EntityAliases entityAliases; + private final String sqlTableAlias; + + private final EntityPersister persister; + + private final PropertyPath propertyPath = new PropertyPath(); // its a root + + public EntityReturn( + SessionFactoryImplementor sessionFactory, + String alias, + LockMode lockMode, + String entityName, + String sqlTableAlias, + EntityAliases entityAliases) { + super( sessionFactory, alias, lockMode ); + this.entityAliases = entityAliases; + this.sqlTableAlias = sqlTableAlias; + + this.persister = sessionFactory.getEntityPersister( entityName ); + } + + @Override + public String getAlias() { + return super.getAlias(); + } + + @Override + public LockMode getLockMode() { + return super.getLockMode(); + } + + @Override + public EntityPersister getEntityPersister() { + return persister; + } + + @Override + public EntityAliases getEntityAliases() { + return entityAliases; + } + + @Override + public String getSqlTableAlias() { + return sqlTableAlias; + } + + @Override + public void validateFetchPlan(FetchStrategy fetchStrategy) { + } + + @Override + public EntityPersister retrieveFetchSourcePersister() { + return getEntityPersister(); + } + + @Override + public PropertyPath getPropertyPath() { + return propertyPath; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java new file mode 100644 index 0000000000..4be5ca8bbc --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java @@ -0,0 +1,59 @@ +/* + * 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.plan.spi; + +import org.hibernate.engine.FetchStrategy; +import org.hibernate.loader.PropertyPath; + +/** + * Contract for associations that are being fetched. + *

+ * NOTE : can represent components/embeddables + * + * @author Steve Ebersole + */ +public interface Fetch extends FetchOwner { + /** + * Obtain the owner of this fetch. + * + * @return The fetch owner. + */ + public FetchOwner getOwner(); + + /** + * Obtain the name of the property, relative to the owner, being fetched. + * + * @return The fetched property name. + */ + public String getOwnerPropertyName(); + + public FetchStrategy getFetchStrategy(); + + /** + * Get the property path to this fetch + * + * @return The property path + */ + public PropertyPath getPropertyPath(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwner.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwner.java new file mode 100644 index 0000000000..e28daf1b24 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwner.java @@ -0,0 +1,68 @@ +/* + * 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.plan.spi; + +import org.hibernate.engine.FetchStrategy; +import org.hibernate.loader.PropertyPath; +import org.hibernate.persister.entity.EntityPersister; + +/** + * Contract for owners of fetches. Any non-scalar return could be a fetch owner. + * + * @author Steve Ebersole + */ +public interface FetchOwner { + /** + * Convenient constant for returning no fetches from {@link #getFetches()} + */ + public static final Fetch[] NO_FETCHES = new Fetch[0]; + + /** + * Retrieve the fetches owned by this return. + * + * @return The owned fetches. + */ + public Fetch[] getFetches(); + + /** + * Is the asserted plan valid from this owner to a fetch? + * + * @param fetchStrategy The pla to validate + */ + public void validateFetchPlan(FetchStrategy fetchStrategy); + + /** + * Retrieve the EntityPersister that is the base for any property references in the fetches it owns. + * + * @return The EntityPersister, for property name resolution. + */ + public EntityPersister retrieveFetchSourcePersister(); + + /** + * Get the property path to this fetch owner + * + * @return The property path + */ + public PropertyPath getPropertyPath(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlan.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlan.java new file mode 100644 index 0000000000..0173b39714 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlan.java @@ -0,0 +1,61 @@ +/* + * 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.plan.spi; + +import java.util.List; + +/** + * Describes a plan for performing a load of results. + * + * Generally speaking there are 3 forms of load plans:

    + *
  • + * An entity load plan for handling get/load handling. This form will typically have a single + * return (of type {@link EntityReturn}) defined by {@link #getReturns()}, possibly defining fetches. + *
  • + *
  • + * A collection initializer, used to load the contents of a collection. This form will typically have a + * single return (of type {@link CollectionReturn} defined by {@link #getReturns()}, possibly defining fetches + *
  • + *
  • + * A query load plan which can contain multiple returns of mixed type (though implementing {@link Return}). + * Again, may possibly define fetches. + *
  • + *
+ * + * @author Steve Ebersole + */ +public interface LoadPlan { + /** + * Convenient form of checking {@link #getReturns()} for scalar root returns. + * + * @return {@code true} if {@link #getReturns()} contained any scalar returns; {@code false} otherwise. + */ + public boolean hasAnyScalarReturns(); + + public List getReturns(); + + // todo : would also like to see "call back" style access for handling "subsequent actions" such as: + // 1) follow-on locking + // 2) join fetch conversions to subselect fetches +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlanBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlanBuilder.java new file mode 100644 index 0000000000..61b7b41ab1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlanBuilder.java @@ -0,0 +1,64 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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.persister.collection.CollectionPersister; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor; + +/** + * Coordinates building of a {@link LoadPlan} between the {@link org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor} and + * {@link LoadPlanBuilderStrategy} + * + * @author Steve Ebersole + */ +public class LoadPlanBuilder { + /** + * Coordinates building a LoadPlan that defines just a single root entity return (may have fetches). + *

+ * Typically this includes building load plans for entity loading or cascade loading. + * + * @param strategy The strategy defining the load plan shaping + * @param persister The persister for the entity forming the root of the load plan. + * + * @return The built load plan. + */ + public static LoadPlan buildRootEntityLoadPlan(LoadPlanBuilderStrategy strategy, EntityPersister persister) { + MetadataDrivenModelGraphVisitor.visitEntity( strategy, persister ); + return strategy.buildLoadPlan(); + } + + /** + * Coordinates building a LoadPlan that defines just a single root collection return (may have fetches). + * + * @param strategy The strategy defining the load plan shaping + * @param persister The persister for the collection forming the root of the load plan. + * + * @return The built load plan. + */ + public static LoadPlan buildRootCollectionLoadPlan(LoadPlanBuilderStrategy strategy, CollectionPersister persister) { + MetadataDrivenModelGraphVisitor.visitCollection( strategy, persister ); + return strategy.buildLoadPlan(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlanBuilderStrategy.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlanBuilderStrategy.java new file mode 100644 index 0000000000..7c40540fbf --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlanBuilderStrategy.java @@ -0,0 +1,40 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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.persister.walking.spi.AssociationVisitationStrategy; + +/** + * Specialized {@link org.hibernate.persister.walking.spi.AssociationVisitationStrategy} implementation for building {@link LoadPlan} instances. + * + * @author Steve Ebersole + */ +public interface LoadPlanBuilderStrategy extends AssociationVisitationStrategy { + /** + * After visitation is done, build the load plan. + * + * @return The load plan + */ + public LoadPlan buildLoadPlan(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Return.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Return.java new file mode 100644 index 0000000000..b94adfae9d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Return.java @@ -0,0 +1,40 @@ +/* + * 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.plan.spi; + +/** + * Represents a return value in the query results. Not the same as a result (column) in the JDBC ResultSet! + *

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

+ * Return is distinctly different from a {@link Fetch}. + * + * @see ScalarReturn + * @see EntityReturn + * @see CollectionReturn + * + * @author Steve Ebersole + */ +public interface Return { +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ReturnVisitationStrategy.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ReturnVisitationStrategy.java new file mode 100644 index 0000000000..4140249b3a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ReturnVisitationStrategy.java @@ -0,0 +1,138 @@ +/* + * 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.plan.spi; + +/** + * @author Steve Ebersole + */ +public interface ReturnVisitationStrategy { + /** + * Notification we are preparing to start visitation. + */ + public void start(); + + /** + * Notification we are finished visitation. + */ + public void finish(); + + /** + * Notification that a new root return branch is being started. Will be followed by calls to one of the following + * based on the type of return:

    + *
  • {@link #handleScalarReturn}
  • + *
  • {@link #handleEntityReturn}
  • + *
  • {@link #handleCollectionReturn}
  • + *
+ * + * @param rootReturn The root return at the root of the branch. + */ + public void startingRootReturn(Return rootReturn); + + /** + * Notification that we are finishing up processing a root return branch + * + * @param rootReturn The RootReturn we are finishing up processing. + */ + public void finishingRootReturn(Return rootReturn); + + /** + * Notification that a scalar return is being processed. Will be surrounded by calls to {@link #startingRootReturn} + * and {@link #finishingRootReturn} + * + * @param scalarReturn The scalar return + */ + public void handleScalarReturn(ScalarReturn scalarReturn); + + /** + * Notification that a root entity return is being processed. Will be surrounded by calls to + * {@link #startingRootReturn} and {@link #finishingRootReturn} + * + * @param rootEntityReturn The root entity return + */ + public void handleEntityReturn(EntityReturn rootEntityReturn); + + /** + * Notification that a root collection return is being processed. Will be surrounded by calls to + * {@link #startingRootReturn} and {@link #finishingRootReturn} + * + * @param rootCollectionReturn The root collection return + */ + public void handleCollectionReturn(CollectionReturn rootCollectionReturn); + + /** + * Notification that we are about to start processing the fetches for the given fetch owner. + * + * @param fetchOwner The fetch owner. + */ + public void startingFetches(FetchOwner fetchOwner); + + /** + * Notification that we are finishing up processing the fetches for the given fetch owner. + * + * @param fetchOwner The fetch owner. + */ + public void finishingFetches(FetchOwner fetchOwner); + + /** + * Notification we are starting the processing of an entity fetch + * + * @param entityFetch The entity fetch + */ + public void startingEntityFetch(EntityFetch entityFetch); + + /** + * Notification that we are finishing up the processing of an entity fetch + * + * @param entityFetch The entity fetch + */ + public void finishingEntityFetch(EntityFetch entityFetch); + + /** + * Notification we are starting the processing of a collection fetch + * + * @param collectionFetch The collection fetch + */ + public void startingCollectionFetch(CollectionFetch collectionFetch); + + /** + * Notification that we are finishing up the processing of a collection fetch + * + * @param collectionFetch The collection fetch + */ + public void finishingCollectionFetch(CollectionFetch collectionFetch); + + /** + * Notification we are starting the processing of a component fetch + * + * @param fetch The composite fetch + */ + public void startingCompositeFetch(CompositeFetch fetch); + + /** + * Notification that we are finishing up the processing of a composite fetch + * + * @param fetch The composite fetch + */ + public void finishingCompositeFetch(CompositeFetch fetch); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ReturnVisitor.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ReturnVisitor.java new file mode 100644 index 0000000000..cf4aaf6ef9 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ReturnVisitor.java @@ -0,0 +1,116 @@ +/* + * 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.plan.spi; + +/** + * Visitor for processing {@link Return} graphs + * + * @author Steve Ebersole + */ +public class ReturnVisitor { + public static void visit(Return[] rootReturns, ReturnVisitationStrategy strategy) { + new ReturnVisitor( strategy ).visitReturns( rootReturns ); + } + + private final ReturnVisitationStrategy strategy; + + public ReturnVisitor(ReturnVisitationStrategy strategy) { + this.strategy = strategy; + } + + private void visitReturns(Return[] rootReturns) { + strategy.start(); + + for ( Return rootReturn : rootReturns ) { + visitRootReturn( rootReturn ); + } + + strategy.finish(); + } + + private void visitRootReturn(Return rootReturn) { + strategy.startingRootReturn( rootReturn ); + + if ( org.hibernate.loader.plan.spi.ScalarReturn.class.isInstance( rootReturn ) ) { + strategy.handleScalarReturn( (ScalarReturn) rootReturn ); + } + else { + visitNonScalarRootReturn( rootReturn ); + } + + strategy.finishingRootReturn( rootReturn ); + } + + private void visitNonScalarRootReturn(Return rootReturn) { + if ( EntityReturn.class.isInstance( rootReturn ) ) { + strategy.handleEntityReturn( (EntityReturn) rootReturn ); + visitFetches( (EntityReturn) rootReturn ); + } + else if ( CollectionReturn.class.isInstance( rootReturn ) ) { + strategy.handleCollectionReturn( (CollectionReturn) rootReturn ); + visitFetches( (CollectionReturn) rootReturn ); + } + else { + throw new IllegalStateException( + "Unexpected return type encountered; expecting a non-scalar root return, but found " + + rootReturn.getClass().getName() + ); + } + } + + private void visitFetches(FetchOwner fetchOwner) { + strategy.startingFetches( fetchOwner ); + + for ( Fetch fetch : fetchOwner.getFetches() ) { + visitFetch( fetch ); + } + + strategy.finishingFetches( fetchOwner ); + } + + private void visitFetch(Fetch fetch) { + if ( EntityFetch.class.isInstance( fetch ) ) { + strategy.startingEntityFetch( (EntityFetch) fetch ); + visitFetches( fetch ); + strategy.finishingEntityFetch( (EntityFetch) fetch ); + } + else if ( CollectionFetch.class.isInstance( fetch ) ) { + strategy.startingCollectionFetch( (CollectionFetch) fetch ); + visitFetches( fetch ); + strategy.finishingCollectionFetch( (CollectionFetch) fetch ); + } + else if ( CompositeFetch.class.isInstance( fetch ) ) { + strategy.startingCompositeFetch( (CompositeFetch) fetch ); + visitFetches( fetch ); + strategy.finishingCompositeFetch( (CompositeFetch) fetch ); + } + else { + throw new IllegalStateException( + "Unexpected return type encountered; expecting a fetch return, but found " + + fetch.getClass().getName() + ); + } + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ScalarReturn.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ScalarReturn.java new file mode 100644 index 0000000000..9a3d434bbc --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/ScalarReturn.java @@ -0,0 +1,52 @@ +/* + * 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.plan.spi; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.type.Type; + +/** + * Represent a simple scalar return within a query result. Generally this would be values of basic (String, Integer, + * etc) or composite types. + * + * @author Steve Ebersole + */ +public class ScalarReturn extends AbstractPlanNode implements Return { + private final Type type; + private final String columnAlias; + + public ScalarReturn(SessionFactoryImplementor factory, Type type, String columnAlias) { + super( factory ); + this.type = type; + this.columnAlias = columnAlias; + } + + public Type getType() { + return type; + } + + public String getColumnAlias() { + return columnAlias; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java index 5236a08712..0db82ac72c 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java @@ -79,6 +79,11 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Loadable; import org.hibernate.persister.entity.PropertyMapping; import org.hibernate.persister.entity.Queryable; +import org.hibernate.persister.walking.spi.CollectionDefinition; +import org.hibernate.persister.walking.spi.CollectionElementDefinition; +import org.hibernate.persister.walking.spi.CollectionIndexDefinition; +import org.hibernate.persister.walking.spi.CompositeDefinition; +import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.pretty.MessageHelper; import org.hibernate.sql.Alias; import org.hibernate.sql.SelectFragment; @@ -90,6 +95,7 @@ import org.hibernate.sql.ordering.antlr.FormulaReference; import org.hibernate.sql.ordering.antlr.OrderByAliasResolver; import org.hibernate.sql.ordering.antlr.OrderByTranslation; import org.hibernate.sql.ordering.antlr.SqlValueReference; +import org.hibernate.type.AssociationType; import org.hibernate.type.CollectionType; import org.hibernate.type.CompositeType; import org.hibernate.type.EntityType; @@ -1934,4 +1940,79 @@ public abstract class AbstractCollectionPersister public abstract FilterAliasGenerator getFilterAliasGenerator(final String rootAlias); + + // ColectionDefinition impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Override + public CollectionPersister getCollectionPersister() { + return this; + } + + @Override + public CollectionIndexDefinition getIndexDefinition() { + if ( ! hasIndex() ) { + return null; + } + + return new CollectionIndexDefinition() { + @Override + public CollectionDefinition getCollectionDefinition() { + return AbstractCollectionPersister.this; + } + + @Override + public Type getType() { + return getIndexType(); + } + + @Override + public EntityDefinition toEntityDefinition() { + if ( getType().isComponentType() ) { + throw new IllegalStateException( "Cannot treat composite collection index type as entity" ); + } + return (EntityPersister) ( (AssociationType) getIndexType() ).getAssociatedJoinable( getFactory() ); + } + + @Override + public CompositeDefinition toCompositeDefinition() { + if ( ! getType().isComponentType() ) { + throw new IllegalStateException( "Cannot treat entity collection index type as composite" ); + } + // todo : implement + throw new NotYetImplementedException(); + } + }; + } + + @Override + public CollectionElementDefinition getElementDefinition() { + return new CollectionElementDefinition() { + @Override + public CollectionDefinition getCollectionDefinition() { + return AbstractCollectionPersister.this; + } + + @Override + public Type getType() { + return getElementType(); + } + + @Override + public EntityDefinition toEntityDefinition() { + if ( getType().isComponentType() ) { + throw new IllegalStateException( "Cannot treat composite collection element type as entity" ); + } + return getElementPersister(); + } + + @Override + public CompositeDefinition toCompositeDefinition() { + if ( ! getType().isComponentType() ) { + throw new IllegalStateException( "Cannot treat entity collection element type as composite" ); + } + // todo : implement + throw new NotYetImplementedException(); + } + }; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/CollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/CollectionPersister.java index 3b2e3788dd..ecfe83c7d8 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/CollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/CollectionPersister.java @@ -38,6 +38,7 @@ import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.id.IdentifierGenerator; import org.hibernate.metadata.CollectionMetadata; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.walking.spi.CollectionDefinition; import org.hibernate.type.CollectionType; import org.hibernate.type.Type; @@ -60,7 +61,7 @@ import org.hibernate.type.Type; * @see org.hibernate.collection.spi.PersistentCollection * @author Gavin King */ -public interface CollectionPersister { +public interface CollectionPersister extends CollectionDefinition { /** * Initialize the given collection with the given key */ diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 860f4b74b2..5456404f04 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -59,6 +59,7 @@ import org.hibernate.cache.spi.entry.ReferenceCacheEntryImpl; import org.hibernate.cache.spi.entry.StandardCacheEntryImpl; import org.hibernate.cache.spi.entry.StructuredCacheEntry; import org.hibernate.cache.spi.entry.UnstructuredCacheEntry; +import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.dialect.lock.LockingStrategy; import org.hibernate.engine.OptimisticLockStyle; import org.hibernate.engine.internal.StatefulPersistenceContext; @@ -91,7 +92,6 @@ import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.jdbc.Expectation; import org.hibernate.jdbc.Expectations; import org.hibernate.jdbc.TooManyRowsAffectedException; -import org.hibernate.loader.entity.BatchingEntityLoader; import org.hibernate.loader.entity.BatchingEntityLoaderBuilder; import org.hibernate.loader.entity.CascadeEntityLoader; import org.hibernate.loader.entity.EntityLoader; @@ -109,6 +109,7 @@ import org.hibernate.metamodel.binding.SimpleValueBinding; import org.hibernate.metamodel.binding.SingularAttributeBinding; import org.hibernate.metamodel.relational.DerivedValue; import org.hibernate.metamodel.relational.Value; +import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.pretty.MessageHelper; import org.hibernate.property.BackrefPropertyAccessor; import org.hibernate.sql.Alias; @@ -504,7 +505,7 @@ public abstract class AbstractEntityPersister this.naturalIdRegionAccessStrategy = naturalIdRegionAccessStrategy; isLazyPropertiesCacheable = persistentClass.isLazyPropertiesCacheable(); - this.entityMetamodel = new EntityMetamodel( persistentClass, factory ); + this.entityMetamodel = new EntityMetamodel( persistentClass, this, factory ); this.entityTuplizer = this.entityMetamodel.getTuplizer(); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -834,7 +835,7 @@ public abstract class AbstractEntityPersister entityBinding.getHierarchyDetails().getCaching() == null ? false : entityBinding.getHierarchyDetails().getCaching().isCacheLazyProperties(); - this.entityMetamodel = new EntityMetamodel( entityBinding, factory ); + this.entityMetamodel = new EntityMetamodel( entityBinding, this, factory ); this.entityTuplizer = this.entityMetamodel.getTuplizer(); int batch = entityBinding.getBatchSize(); if ( batch == -1 ) { @@ -3816,10 +3817,11 @@ public abstract class AbstractEntityPersister } public void postInstantiate() throws MappingException { + generateEntityDefinition(); + createLoaders(); createUniqueKeyLoaders(); createQueryLoader(); - } //needed by subclasses to override the createLoader strategy @@ -5070,4 +5072,110 @@ public abstract class AbstractEntityPersister throw new HibernateException( "Illegal attempt to build cache entry for non-cached entity" ); } } + + + // EntityDefinition impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + private Iterable embeddedCompositeIdentifierAttributes; + private Iterable attributeDefinitions; + + protected void generateEntityDefinition() { + collectEmbeddedCompositeIdentifierAttributeDefinitions(); + collectAttributeDefinitions(); + } + + @Override + public EntityPersister getEntityPersister() { + return this; + } + + @Override + public Iterable getEmbeddedCompositeIdentifierAttributes() { + return embeddedCompositeIdentifierAttributes; + } + + @Override + public Iterable getAttributes() { + return attributeDefinitions; + } + + private synchronized void collectEmbeddedCompositeIdentifierAttributeDefinitions() { + final Type idType = getIdentifierType(); + if ( !idType.isComponentType() ) { + return; + } + + final CompositeType cidType = (CompositeType) idType; + if ( !cidType.isEmbedded() ) { + return; + } + + // we have an embedded composite identifier. Most likely we need to process the composite + // properties separately, although there is an edge case where the identifier is really + // a simple identifier (single value) wrapped in a JPA @IdClass or even in the case of a + // a simple identifier (single value) wrapped in a Hibernate composite type. + // + // We really do not have a built-in method to determine that. However, generally the + // persister would report that there is single, physical identifier property which is + // explicitly at odds with the notion of "embedded composite". So we use that for now + if ( getEntityMetamodel().getIdentifierProperty().isEmbedded() ) { + this.embeddedCompositeIdentifierAttributes = new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + private final int numberOfAttributes = countSubclassProperties(); + private int currentAttributeNumber = 0; + + @Override + public boolean hasNext() { + return currentAttributeNumber < numberOfAttributes; + } + + @Override + public AttributeDefinition next() { + // todo : implement + throw new NotYetImplementedException(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException( "Remove operation not supported here" ); + } + }; + } + }; + } + } + + private void collectAttributeDefinitions() { + // todo : leverage the attribute definitions housed on EntityMetamodel + // for that to work, we'd have to be able to walk our super entity persister(s) + attributeDefinitions = new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { +// private final int numberOfAttributes = countSubclassProperties(); + private final int numberOfAttributes = entityMetamodel.getPropertySpan(); + private int currentAttributeNumber = 0; + + @Override + public boolean hasNext() { + return currentAttributeNumber < numberOfAttributes; + } + + @Override + public AttributeDefinition next() { + final int attributeNumber = currentAttributeNumber; + currentAttributeNumber++; + return entityMetamodel.getProperties()[ attributeNumber ]; + } + + @Override + public void remove() { + throw new UnsupportedOperationException( "Remove operation not supported here" ); + } + }; + } + }; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java index f95ea71970..46d3fd8bee 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java @@ -44,22 +44,25 @@ import org.hibernate.engine.spi.ValueInclusion; import org.hibernate.id.IdentifierGenerator; import org.hibernate.internal.FilterAliasGenerator; import org.hibernate.metadata.ClassMetadata; +import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.tuple.entity.EntityTuplizer; import org.hibernate.type.Type; import org.hibernate.type.VersionType; /** - * Implementors define mapping and persistence logic for a particular - * strategy of entity mapping. An instance of entity persisters corresponds - * to a given mapped entity. + * Contract describing mapping information and persistence logic for a particular strategy of entity mapping. A given + * persister instance corresponds to a given mapped entity class. *

- * Implementors must be threadsafe (preferrably immutable) and must provide a constructor - * matching the signature of: {@link org.hibernate.mapping.PersistentClass}, {@link org.hibernate.engine.spi.SessionFactoryImplementor} + * Implementations must be thread-safe (preferably immutable). * * @author Gavin King + * @author Steve Ebersole + * + * @see org.hibernate.persister.spi.PersisterFactory + * @see org.hibernate.persister.spi.PersisterClassResolver */ -public interface EntityPersister extends OptimisticCacheSource { +public interface EntityPersister extends OptimisticCacheSource, EntityDefinition { /** * The property name of the "special" identifier property in HQL diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/Helper.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/Helper.java new file mode 100644 index 0000000000..29eb9f5dbe --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/Helper.java @@ -0,0 +1,160 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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.internal; + +import java.util.Iterator; + +import org.hibernate.FetchMode; +import org.hibernate.engine.FetchStyle; +import org.hibernate.engine.FetchTiming; +import org.hibernate.engine.profile.Fetch; +import org.hibernate.engine.profile.FetchProfile; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.PropertyPath; +import org.hibernate.persister.collection.AbstractCollectionPersister; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.OuterJoinLoadable; +import org.hibernate.type.AssociationType; + +/** + * @author Steve Ebersole + */ +public class Helper { + /** + * Determine the fetch-style (if one) explicitly set for this association via fetch profiles. + *

+ * Note that currently fetch profiles only allow specifying join fetching, so this method currently + * returns either (a) FetchStyle.JOIN or (b) null + * + * @param loadQueryInfluencers + * @param persister + * @param path + * @param propertyNumber + * + * @return + */ + public static FetchStyle determineFetchStyleByProfile( + LoadQueryInfluencers loadQueryInfluencers, + EntityPersister persister, + PropertyPath path, + int propertyNumber) { + if ( !loadQueryInfluencers.hasEnabledFetchProfiles() ) { + // perf optimization + return null; + } + + // ugh, this stuff has to be made easier... + final String fullPath = path.getFullPath(); + final String rootPropertyName = ( (OuterJoinLoadable) persister ).getSubclassPropertyName( propertyNumber ); + int pos = fullPath.lastIndexOf( rootPropertyName ); + final String relativePropertyPath = pos >= 0 + ? fullPath.substring( pos ) + : rootPropertyName; + final String fetchRole = persister.getEntityName() + "." + relativePropertyPath; + + Iterator profiles = loadQueryInfluencers.getEnabledFetchProfileNames().iterator(); + while ( profiles.hasNext() ) { + final String profileName = ( String ) profiles.next(); + final FetchProfile profile = loadQueryInfluencers.getSessionFactory().getFetchProfile( profileName ); + final Fetch fetch = profile.getFetchByRole( fetchRole ); + if ( fetch != null && Fetch.Style.JOIN == fetch.getStyle() ) { + return FetchStyle.JOIN; + } + } + return null; + } + + /** + * + * @param mappingFetchMode The mapping defined fetch mode + * @param type The association type + * @param sessionFactory The session factory + * + * @return + */ + public static FetchStyle determineFetchStyleByMetadata( + FetchMode mappingFetchMode, + AssociationType type, + SessionFactoryImplementor sessionFactory) { + if ( !type.isEntityType() && !type.isCollectionType() ) { + return FetchStyle.SELECT; + } + + if ( mappingFetchMode == FetchMode.JOIN ) { + return FetchStyle.JOIN; + } + + if ( type.isEntityType() ) { + EntityPersister persister = (EntityPersister) type.getAssociatedJoinable( sessionFactory ); + if ( persister.isBatchLoadable() ) { + return FetchStyle.BATCH; + } + } + else { + CollectionPersister persister = (CollectionPersister) type.getAssociatedJoinable( sessionFactory ); + if ( persister instanceof AbstractCollectionPersister + && ( (AbstractCollectionPersister) persister ).isSubselectLoadable() ) { + return FetchStyle.SUBSELECT; + } + else if ( persister.getBatchSize() > 0 ) { + return FetchStyle.BATCH; + } + } + + return FetchStyle.SELECT; + } + + public static FetchTiming determineFetchTiming( + FetchStyle style, + AssociationType type, + SessionFactoryImplementor sessionFactory) { + switch ( style ) { + case JOIN: { + return FetchTiming.IMMEDIATE; + } + case BATCH: + case SUBSELECT: { + return FetchTiming.DELAYED; + } + default: { + // SELECT case, can be either + return isSubsequentSelectDelayed( type, sessionFactory ) + ? FetchTiming.DELAYED + : FetchTiming.IMMEDIATE; + } + } + } + + private static boolean isSubsequentSelectDelayed(AssociationType type, SessionFactoryImplementor sessionFactory) { + if ( type.isEntityType() ) { + return ( (EntityPersister) type.getAssociatedJoinable( sessionFactory ) ).hasProxy(); + } + else { + final CollectionPersister cp = ( (CollectionPersister) type.getAssociatedJoinable( sessionFactory ) ); + return cp.isLazy() || cp.isExtraLazy(); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationAttributeDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationAttributeDefinition.java new file mode 100644 index 0000000000..383b3d4e38 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationAttributeDefinition.java @@ -0,0 +1,46 @@ +/* + * 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.engine.FetchStrategy; +import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.loader.PropertyPath; + +/** + * @author Steve Ebersole + */ +public interface AssociationAttributeDefinition extends AttributeDefinition { + public AssociationKey getAssociationKey(); + + public boolean isCollection(); + + public EntityDefinition toEntityDefinition(); + + public CollectionDefinition toCollectionDefinition(); + + public FetchStrategy determineFetchPlan(LoadQueryInfluencers loadQueryInfluencers, PropertyPath propertyPath); + + public CascadeStyle determineCascadeStyle(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationKey.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationKey.java new file mode 100644 index 0000000000..352c4a548f --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationKey.java @@ -0,0 +1,53 @@ +/* + * 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 java.util.Arrays; + +/** + * Used to uniquely identify a foreign key, so that we don't join it more than once creating circularities. + *

+ * bit of a misnomer to call this an association attribute. But this follows the legacy use of AssociationKey + * from old JoinWalkers to denote circular join detection + */ +public class AssociationKey { + private final String table; + private final String[] columns; + + public AssociationKey(String table, String[] columns) { + this.table = table; + this.columns = columns; + } + + @Override + public boolean equals(Object other) { + AssociationKey that = (AssociationKey) other; + return that.table.equals(table) && Arrays.equals( columns, that.columns ); + } + + @Override + public int hashCode() { + return table.hashCode(); //TODO: inefficient + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java new file mode 100644 index 0000000000..1cb649b0d8 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java @@ -0,0 +1,51 @@ +/* + * 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 AssociationVisitationStrategy { + /** + * Notification we are preparing to start visitation. + */ + public void start(); + + /** + * Notification we are finished visitation. + */ + public void finish(); + + public void startingEntity(EntityDefinition entityDefinition); + public void finishingEntity(EntityDefinition entityDefinition); + + public void startingCollection(CollectionDefinition collectionDefinition); + public void finishingCollection(CollectionDefinition collectionDefinition); + + public void startingComposite(CompositeDefinition compositeDefinition); + public void finishingComposite(CompositeDefinition compositeDefinition); + + public boolean startingAttribute(AttributeDefinition attributeDefinition); + public void finishingAttribute(AttributeDefinition attributeDefinition); +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AttributeDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AttributeDefinition.java new file mode 100644 index 0000000000..ed58612671 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AttributeDefinition.java @@ -0,0 +1,35 @@ +/* + * 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.type.Type; + +/** + * @author Steve Ebersole + */ +public interface AttributeDefinition { + public String getName(); + public Type getType(); + public AttributeSource getSource(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AttributeSource.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AttributeSource.java new file mode 100644 index 0000000000..a94dfb1987 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AttributeSource.java @@ -0,0 +1,31 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.persister.walking.spi; + +/** +* @author Steve Ebersole +*/ +public interface AttributeSource { + public Iterable getAttributes(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionDefinition.java new file mode 100644 index 0000000000..f7e2fb6ad7 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionDefinition.java @@ -0,0 +1,38 @@ +/* + * 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.persister.collection.CollectionPersister; +import org.hibernate.type.CollectionType; + +/** + * @author Steve Ebersole + */ +public interface CollectionDefinition { + public CollectionPersister getCollectionPersister(); + public CollectionType getCollectionType(); + + public CollectionIndexDefinition getIndexDefinition(); + public CollectionElementDefinition getElementDefinition(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionElementDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionElementDefinition.java new file mode 100644 index 0000000000..fd48366c3a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionElementDefinition.java @@ -0,0 +1,39 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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.type.Type; + +/** + * @author Steve Ebersole + */ +public interface CollectionElementDefinition { + public CollectionDefinition getCollectionDefinition(); + + public Type getType(); + + public EntityDefinition toEntityDefinition(); + + public CompositeDefinition toCompositeDefinition(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionIndexDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionIndexDefinition.java new file mode 100644 index 0000000000..700e2b5070 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionIndexDefinition.java @@ -0,0 +1,39 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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.type.Type; + +/** + * @author Steve Ebersole + */ +public interface CollectionIndexDefinition { + public CollectionDefinition getCollectionDefinition(); + + public Type getType(); + + public EntityDefinition toEntityDefinition(); + + public CompositeDefinition toCompositeDefinition(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositeDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositeDefinition.java new file mode 100644 index 0000000000..51c88333f4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositeDefinition.java @@ -0,0 +1,30 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.persister.walking.spi; + +/** + * @author Steve Ebersole + */ +public interface CompositeDefinition extends AttributeDefinition, AttributeSource { +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/EntityDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/EntityDefinition.java new file mode 100644 index 0000000000..1e02b42106 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/EntityDefinition.java @@ -0,0 +1,36 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.persister.walking.spi; + +import org.hibernate.persister.entity.EntityPersister; + +/** + * Defines the contract for walking the attributes defined by an entity + * + * @author Steve Ebersole + */ +public interface EntityDefinition extends AttributeSource { + public EntityPersister getEntityPersister(); + public Iterable getEmbeddedCompositeIdentifierAttributes(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java new file mode 100644 index 0000000000..ccbbceb6bb --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java @@ -0,0 +1,208 @@ +/* + * 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 java.util.HashSet; +import java.util.Set; + +import org.jboss.logging.Logger; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.PropertyPath; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.type.Type; + +/** + * Provides model graph visitation based on the defined metadata (as opposed to based on the incoming graph + * as we see in cascade processing). In layman terms, we are walking the graph of the users model as defined by + * mapped associations. + *

+ * Re-implementation of the legacy {@link org.hibernate.loader.JoinWalker} contract to leverage load plans. + * + * @author Steve Ebersole + */ +public class MetadataDrivenModelGraphVisitor { + private static final Logger log = Logger.getLogger( MetadataDrivenModelGraphVisitor.class ); + + public static void visitEntity(AssociationVisitationStrategy strategy, EntityPersister persister) { + strategy.start(); + try { + new MetadataDrivenModelGraphVisitor( strategy, persister.getFactory() ) + .visitEntityDefinition( persister ); + } + finally { + strategy.finish(); + } + } + + public static void visitCollection(AssociationVisitationStrategy strategy, CollectionPersister persister) { + strategy.start(); + try { + new MetadataDrivenModelGraphVisitor( strategy, persister.getFactory() ) + .visitCollectionDefinition( persister ); + } + finally { + strategy.finish(); + } + } + + private final AssociationVisitationStrategy strategy; + private final SessionFactoryImplementor factory; + + // todo : add a getDepth() method to PropertyPath + private PropertyPath currentPropertyPath = new PropertyPath(); + + public MetadataDrivenModelGraphVisitor(AssociationVisitationStrategy strategy, SessionFactoryImplementor factory) { + this.strategy = strategy; + this.factory = factory; + } + + private void visitEntityDefinition(EntityDefinition entityDefinition) { + strategy.startingEntity( entityDefinition ); + try { + visitAttributes( entityDefinition ); + optionallyVisitEmbeddedCompositeIdentifier( entityDefinition ); + } + finally { + strategy.finishingEntity( entityDefinition ); + } + } + + private void optionallyVisitEmbeddedCompositeIdentifier(EntityDefinition entityDefinition) { + // if the entity has a composite identifier, see if we need to handle its sub-properties separately + final Iterable embeddedCompositeIdentifierAttributes = + entityDefinition.getEmbeddedCompositeIdentifierAttributes(); + if ( embeddedCompositeIdentifierAttributes == null ) { + return; + } + + for ( AttributeDefinition attributeDefinition : embeddedCompositeIdentifierAttributes ) { + visitAttributeDefinition( attributeDefinition ); + } + } + + private void visitAttributes(AttributeSource attributeSource) { + for ( AttributeDefinition attributeDefinition : attributeSource.getAttributes() ) { + visitAttributeDefinition( attributeDefinition ); + } + } + + private void visitAttributeDefinition(AttributeDefinition attributeDefinition) { + final PropertyPath subPath = currentPropertyPath.append( attributeDefinition.getName() ); + log.debug( "Visiting attribute path : " + subPath.getFullPath() ); + + final boolean continueWalk = strategy.startingAttribute( attributeDefinition ); + if ( continueWalk ) { + final PropertyPath old = currentPropertyPath; + currentPropertyPath = subPath; + try { + if ( attributeDefinition.getType().isAssociationType() ) { + visitAssociation( (AssociationAttributeDefinition) attributeDefinition ); + } + else if ( attributeDefinition.getType().isComponentType() ) { + visitCompositeDefinition( (CompositeDefinition) attributeDefinition ); + } + } + finally { + currentPropertyPath = old; + } + } + strategy.finishingAttribute( attributeDefinition ); + } + + private void visitAssociation(AssociationAttributeDefinition attribute) { + // todo : do "too deep" checks; but see note about adding depth to PropertyPath + + if ( isDuplicateAssociation( attribute.getAssociationKey() ) ) { + log.debug( "Property path deemed to be circular : " + currentPropertyPath.getFullPath() ); + return; + } + + if ( attribute.isCollection() ) { + visitCollectionDefinition( attribute.toCollectionDefinition() ); + } + else { + visitEntityDefinition( attribute.toEntityDefinition() ); + } + } + + private void visitCompositeDefinition(CompositeDefinition compositeDefinition) { + strategy.startingComposite( compositeDefinition ); + try { + visitAttributes( compositeDefinition ); + } + finally { + strategy.finishingComposite( compositeDefinition ); + } + } + + private void visitCollectionDefinition(CollectionDefinition collectionDefinition) { + strategy.startingCollection( collectionDefinition ); + + try { + visitCollectionIndex( collectionDefinition.getIndexDefinition() ); + + final CollectionElementDefinition elementDefinition = collectionDefinition.getElementDefinition(); + if ( elementDefinition.getType().isComponentType() ) { + visitCompositeDefinition( elementDefinition.toCompositeDefinition() ); + } + else { + visitEntityDefinition( elementDefinition.toEntityDefinition() ); + } + } + finally { + strategy.finishingCollection( collectionDefinition ); + } + } + + private void visitCollectionIndex(CollectionIndexDefinition collectionIndexDefinition) { + if ( collectionIndexDefinition == null ) { + return; + } + + log.debug( "Visiting collection index : " + currentPropertyPath.getFullPath() ); + currentPropertyPath = currentPropertyPath.append( "" ); + try { + final Type collectionIndexType = collectionIndexDefinition.getType(); + if ( collectionIndexType.isComponentType() ) { + visitCompositeDefinition( collectionIndexDefinition.toCompositeDefinition() ); + } + else if ( collectionIndexType.isAssociationType() ) { + visitEntityDefinition( collectionIndexDefinition.toEntityDefinition() ); + } + } + finally { + currentPropertyPath = currentPropertyPath.getParent(); + } + } + + + private final Set visitedAssociationKeys = new HashSet(); + + protected boolean isDuplicateAssociation(AssociationKey associationKey) { + return !visitedAssociationKeys.add( associationKey ); + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/package-info.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/package-info.java new file mode 100644 index 0000000000..90d0cd8faf --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/package-info.java @@ -0,0 +1,6 @@ +package org.hibernate.persister.walking.spi; + +/** + * Package for "walking" associations through metadata definition. Ultimately want {@link org.hibernate.persister.walking.spi.AttributeDefinition} and + * {@link AttributeSource} etc to become part of the persister model. + */ \ No newline at end of file diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/AbstractAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/AbstractAttribute.java new file mode 100644 index 0000000000..4818c33c22 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tuple/AbstractAttribute.java @@ -0,0 +1,55 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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.tuple; + +import org.hibernate.type.Type; + +/** + * @author Steve Ebersole + */ +public abstract class AbstractAttribute implements Attribute, Property { + private final String attributeName; + private final Type attributeType; + + protected AbstractAttribute(String attributeName, Type attributeType) { + this.attributeName = attributeName; + this.attributeType = attributeType; + } + + @Override + @Deprecated + public String getNode() { + return null; + } + + @Override + public String getName() { + return attributeName; + } + + @Override + public Type getType() { + return attributeType; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/AbstractNonIdentifierAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/AbstractNonIdentifierAttribute.java new file mode 100644 index 0000000000..b01458fe64 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tuple/AbstractNonIdentifierAttribute.java @@ -0,0 +1,133 @@ +/* + * 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.tuple; + +import org.hibernate.FetchMode; +import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.persister.walking.spi.AttributeSource; +import org.hibernate.type.Type; + +/** + * @author Steve Ebersole + */ +public abstract class AbstractNonIdentifierAttribute extends AbstractAttribute implements NonIdentifierAttribute { + private final AttributeSource source; + private final SessionFactoryImplementor sessionFactory; + + private final int attributeNumber; + + private final BaselineAttributeInformation attributeInformation; + + protected AbstractNonIdentifierAttribute( + AttributeSource source, + SessionFactoryImplementor sessionFactory, + int attributeNumber, + String attributeName, + Type attributeType, + BaselineAttributeInformation attributeInformation) { + super( attributeName, attributeType ); + this.source = source; + this.sessionFactory = sessionFactory; + this.attributeNumber = attributeNumber; + this.attributeInformation = attributeInformation; + } + + @Override + public AttributeSource getSource() { + return source(); + } + + protected AttributeSource source() { + return source; + } + + protected SessionFactoryImplementor sessionFactory() { + return sessionFactory; + } + + protected int attributeNumber() { + return attributeNumber; + } + + @Override + public boolean isLazy() { + return attributeInformation.isLazy(); + } + + @Override + public boolean isInsertable() { + return attributeInformation.isInsertable(); + } + + @Override + public boolean isUpdateable() { + return attributeInformation.isUpdateable(); + } + + @Override + public boolean isInsertGenerated() { + return attributeInformation.isInsertGenerated(); + } + + @Override + public boolean isUpdateGenerated() { + return attributeInformation.isUpdateGenerated(); + } + + @Override + public boolean isNullable() { + return attributeInformation.isNullable(); + } + + @Override + public boolean isDirtyCheckable() { + return attributeInformation.isDirtyCheckable(); + } + + @Override + public boolean isDirtyCheckable(boolean hasUninitializedProperties) { + return isDirtyCheckable() && ( !hasUninitializedProperties || !isLazy() ); + } + + @Override + public boolean isVersionable() { + return attributeInformation.isVersionable(); + } + + @Override + public CascadeStyle getCascadeStyle() { + return attributeInformation.getCascadeStyle(); + } + + @Override + public FetchMode getFetchMode() { + return attributeInformation.getFetchMode(); + } + + @Override + public String toString() { + return "Attribute[non-identifier]( " + getName() + ")"; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/Attribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/Attribute.java new file mode 100644 index 0000000000..08e7496204 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tuple/Attribute.java @@ -0,0 +1,37 @@ +/* + * 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.tuple; + +import org.hibernate.persister.walking.spi.AttributeDefinition; +import org.hibernate.type.Type; + +/** + * Contract for attributes + * + * @author Steve Ebersole + */ +public interface Attribute { + public String getName(); + public Type getType(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/BaselineAttributeInformation.java b/hibernate-core/src/main/java/org/hibernate/tuple/BaselineAttributeInformation.java new file mode 100644 index 0000000000..920231b0d9 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tuple/BaselineAttributeInformation.java @@ -0,0 +1,166 @@ +package org.hibernate.tuple; + +import org.hibernate.FetchMode; +import org.hibernate.engine.spi.CascadeStyle; + +/** +* @author Steve Ebersole +*/ +public class BaselineAttributeInformation { + private final boolean lazy; + private final boolean insertable; + private final boolean updateable; + private final boolean insertGenerated; + private final boolean updateGenerated; + private final boolean nullable; + private final boolean dirtyCheckable; + private final boolean versionable; + private final CascadeStyle cascadeStyle; + private final FetchMode fetchMode; + private boolean checkable; + + public BaselineAttributeInformation( + boolean lazy, + boolean insertable, + boolean updateable, + boolean insertGenerated, + boolean updateGenerated, + boolean nullable, + boolean dirtyCheckable, + boolean versionable, + CascadeStyle cascadeStyle, + FetchMode fetchMode) { + this.lazy = lazy; + this.insertable = insertable; + this.updateable = updateable; + this.insertGenerated = insertGenerated; + this.updateGenerated = updateGenerated; + this.nullable = nullable; + this.dirtyCheckable = dirtyCheckable; + this.versionable = versionable; + this.cascadeStyle = cascadeStyle; + this.fetchMode = fetchMode; + } + + public boolean isLazy() { + return lazy; + } + + public boolean isInsertable() { + return insertable; + } + + public boolean isUpdateable() { + return updateable; + } + + public boolean isInsertGenerated() { + return insertGenerated; + } + + public boolean isUpdateGenerated() { + return updateGenerated; + } + + public boolean isNullable() { + return nullable; + } + + public boolean isDirtyCheckable() { + return dirtyCheckable; + } + + public boolean isVersionable() { + return versionable; + } + + public CascadeStyle getCascadeStyle() { + return cascadeStyle; + } + + public FetchMode getFetchMode() { + return fetchMode; + } + + public boolean isCheckable() { + return checkable; + } + + public static class Builder { + private boolean lazy; + private boolean insertable; + private boolean updateable; + private boolean insertGenerated; + private boolean updateGenerated; + private boolean nullable; + private boolean dirtyCheckable; + private boolean versionable; + private CascadeStyle cascadeStyle; + private FetchMode fetchMode; + + public Builder setLazy(boolean lazy) { + this.lazy = lazy; + return this; + } + + public Builder setInsertable(boolean insertable) { + this.insertable = insertable; + return this; + } + + public Builder setUpdateable(boolean updateable) { + this.updateable = updateable; + return this; + } + + public Builder setInsertGenerated(boolean insertGenerated) { + this.insertGenerated = insertGenerated; + return this; + } + + public Builder setUpdateGenerated(boolean updateGenerated) { + this.updateGenerated = updateGenerated; + return this; + } + + public Builder setNullable(boolean nullable) { + this.nullable = nullable; + return this; + } + + public Builder setDirtyCheckable(boolean dirtyCheckable) { + this.dirtyCheckable = dirtyCheckable; + return this; + } + + public Builder setVersionable(boolean versionable) { + this.versionable = versionable; + return this; + } + + public Builder setCascadeStyle(CascadeStyle cascadeStyle) { + this.cascadeStyle = cascadeStyle; + return this; + } + + public Builder setFetchMode(FetchMode fetchMode) { + this.fetchMode = fetchMode; + return this; + } + + public BaselineAttributeInformation createInformation() { + return new BaselineAttributeInformation( + lazy, + insertable, + updateable, + insertGenerated, + updateGenerated, + nullable, + dirtyCheckable, + versionable, + cascadeStyle, + fetchMode + ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/IdentifierAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/IdentifierAttribute.java new file mode 100644 index 0000000000..345ef8f45c --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tuple/IdentifierAttribute.java @@ -0,0 +1,21 @@ +package org.hibernate.tuple; + +import org.hibernate.engine.spi.IdentifierValue; +import org.hibernate.id.IdentifierGenerator; + +/** + * @author Steve Ebersole + */ +public interface IdentifierAttribute extends Attribute, Property { + boolean isVirtual(); + + boolean isEmbedded(); + + IdentifierValue getUnsavedValue(); + + IdentifierGenerator getIdentifierGenerator(); + + boolean isIdentifierAssignedByInsert(); + + boolean hasIdentifierMapper(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/IdentifierProperty.java b/hibernate-core/src/main/java/org/hibernate/tuple/IdentifierProperty.java index ccd05be6ec..54ad0ad32d 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/IdentifierProperty.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/IdentifierProperty.java @@ -1,10 +1,10 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * Copyright (c) 2008, 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 Middleware LLC. + * 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 @@ -20,9 +20,9 @@ * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA - * */ package org.hibernate.tuple; + import org.hibernate.engine.spi.IdentifierValue; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.PostInsertIdentifierGenerator; @@ -34,7 +34,7 @@ import org.hibernate.type.Type; * * @author Steve Ebersole */ -public class IdentifierProperty extends Property { +public class IdentifierProperty extends AbstractAttribute implements IdentifierAttribute { private boolean virtual; private boolean embedded; @@ -63,7 +63,7 @@ public class IdentifierProperty extends Property { boolean embedded, IdentifierValue unsavedValue, IdentifierGenerator identifierGenerator) { - super(name, node, type); + super( name, type ); this.virtual = false; this.embedded = embedded; this.hasIdentifierMapper = false; @@ -87,7 +87,7 @@ public class IdentifierProperty extends Property { boolean hasIdentifierMapper, IdentifierValue unsavedValue, IdentifierGenerator identifierGenerator) { - super(null, null, type); + super( null, type ); this.virtual = true; this.embedded = embedded; this.hasIdentifierMapper = hasIdentifierMapper; @@ -96,27 +96,38 @@ public class IdentifierProperty extends Property { this.identifierAssignedByInsert = identifierGenerator instanceof PostInsertIdentifierGenerator; } + @Override public boolean isVirtual() { return virtual; } + @Override public boolean isEmbedded() { return embedded; } + @Override public IdentifierValue getUnsavedValue() { return unsavedValue; } + @Override public IdentifierGenerator getIdentifierGenerator() { return identifierGenerator; } + @Override public boolean isIdentifierAssignedByInsert() { return identifierAssignedByInsert; } + @Override public boolean hasIdentifierMapper() { return hasIdentifierMapper; } + + @Override + public String toString() { + return "IdentifierAttribute(" + getName() + ")"; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/NonIdentifierAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/NonIdentifierAttribute.java new file mode 100644 index 0000000000..508572de1d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tuple/NonIdentifierAttribute.java @@ -0,0 +1,55 @@ +/* + * 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.tuple; + +import org.hibernate.FetchMode; +import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.persister.walking.spi.AttributeDefinition; + +/** + * @author Steve Ebersole + */ +public interface NonIdentifierAttribute extends Attribute, AttributeDefinition { + public boolean isLazy(); + + public boolean isInsertable(); + + public boolean isUpdateable(); + + public boolean isInsertGenerated(); + + public boolean isUpdateGenerated(); + + public boolean isNullable(); + + public boolean isDirtyCheckable(boolean hasUninitializedProperties); + + public boolean isDirtyCheckable(); + + public boolean isVersionable(); + + public CascadeStyle getCascadeStyle(); + + public FetchMode getFetchMode(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/Property.java b/hibernate-core/src/main/java/org/hibernate/tuple/Property.java index b9f04fd924..69ea51cd20 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/Property.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/Property.java @@ -1,10 +1,10 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * Copyright (c) 2008, 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 Middleware LLC. + * 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 @@ -20,52 +20,16 @@ * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA - * */ package org.hibernate.tuple; -import java.io.Serializable; - -import org.hibernate.type.Type; /** * Defines the basic contract of a Property within the runtime metamodel. * * @author Steve Ebersole */ -public abstract class Property implements Serializable { - private String name; - private String node; - private Type type; - - /** - * Constructor for Property instances. - * - * @param name The name by which the property can be referenced within - * its owner. - * @param node The node name to use for XML-based representation of this - * property. - * @param type The Hibernate Type of this property. - */ - protected Property(String name, String node, Type type) { - this.name = name; - this.node = node; - this.type = type; - } - - public String getName() { - return name; - } - - public String getNode() { - return node; - } - - public Type getType() { - return type; - } - - public String toString() { - return "Property(" + name + ':' + type.getName() + ')'; - } - +@Deprecated +public interface Property extends Attribute { + @Deprecated + public String getNode(); } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java b/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java index 8c1a51f931..8db4f2048e 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java @@ -1,10 +1,10 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * Copyright (c) 2008, 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 Middleware LLC. + * 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 @@ -20,17 +20,20 @@ * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA - * */ package org.hibernate.tuple; + import java.lang.reflect.Constructor; import org.hibernate.EntityMode; import org.hibernate.FetchMode; +import org.hibernate.HibernateException; +import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.engine.internal.UnsavedValueFactory; import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.CascadeStyles; import org.hibernate.engine.spi.IdentifierValue; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.VersionValue; import org.hibernate.id.IdentifierGenerator; import org.hibernate.internal.util.ReflectHelper; @@ -45,10 +48,16 @@ import org.hibernate.metamodel.binding.BasicAttributeBinding; import org.hibernate.metamodel.binding.EntityBinding; import org.hibernate.metamodel.binding.SimpleValueBinding; import org.hibernate.metamodel.binding.SingularAttributeBinding; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.property.Getter; import org.hibernate.property.PropertyAccessor; import org.hibernate.property.PropertyAccessorFactory; +import org.hibernate.tuple.entity.EntityBasedAssociationAttribute; +import org.hibernate.tuple.entity.EntityBasedBasicAttribute; +import org.hibernate.tuple.entity.EntityBasedCompositeAttribute; +import org.hibernate.tuple.entity.VersionProperty; import org.hibernate.type.AssociationType; +import org.hibernate.type.CompositeType; import org.hibernate.type.Type; import org.hibernate.type.VersionType; @@ -59,16 +68,16 @@ import org.hibernate.type.VersionType; * @author Steve Ebersole */ public class PropertyFactory { - /** - * Generates an IdentifierProperty representation of the for a given entity mapping. + * Generates the attribute representation of the identifier for a given entity mapping. * * @param mappedEntity The mapping definition of the entity. * @param generator The identifier value generator to use for this identifier. * @return The appropriate IdentifierProperty definition. */ - public static IdentifierProperty buildIdentifierProperty(PersistentClass mappedEntity, IdentifierGenerator generator) { - + public static IdentifierProperty buildIdentifierAttribute( + PersistentClass mappedEntity, + IdentifierGenerator generator) { String mappedUnsavedValue = mappedEntity.getIdentifier().getNullValue(); Type type = mappedEntity.getIdentifier().getType(); Property property = mappedEntity.getIdentifierProperty(); @@ -109,7 +118,9 @@ public class PropertyFactory { * @param generator The identifier value generator to use for this identifier. * @return The appropriate IdentifierProperty definition. */ - public static IdentifierProperty buildIdentifierProperty(EntityBinding mappedEntity, IdentifierGenerator generator) { + public static IdentifierProperty buildIdentifierProperty( + EntityBinding mappedEntity, + IdentifierGenerator generator) { final BasicAttributeBinding property = mappedEntity.getHierarchyDetails().getEntityIdentifier().getValueBinding(); @@ -157,7 +168,12 @@ public class PropertyFactory { * @param lazyAvailable Is property lazy loading currently available. * @return The appropriate VersionProperty definition. */ - public static VersionProperty buildVersionProperty(Property property, boolean lazyAvailable) { + public static VersionProperty buildVersionProperty( + EntityPersister persister, + SessionFactoryImplementor sessionFactory, + int attributeNumber, + Property property, + boolean lazyAvailable) { String mappedUnsavedValue = ( (KeyValue) property.getValue() ).getNullValue(); VersionValue unsavedValue = UnsavedValueFactory.getUnsavedVersionValue( @@ -165,23 +181,27 @@ public class PropertyFactory { getGetter( property ), (VersionType) property.getType(), getConstructor( property.getPersistentClass() ) - ); + ); boolean lazy = lazyAvailable && property.isLazy(); return new VersionProperty( + persister, + sessionFactory, + attributeNumber, property.getName(), - property.getNodeName(), property.getValue().getType(), - lazy, - property.isInsertable(), - property.isUpdateable(), - property.getGeneration() == PropertyGeneration.INSERT || property.getGeneration() == PropertyGeneration.ALWAYS, - property.getGeneration() == PropertyGeneration.ALWAYS, - property.isOptional(), - property.isUpdateable() && !lazy, - property.isOptimisticLocked(), - property.getCascadeStyle(), + new BaselineAttributeInformation.Builder() + .setLazy( lazy ) + .setInsertable( property.isInsertable() ) + .setUpdateable( property.isUpdateable() ) + .setInsertGenerated( property.getGeneration() == PropertyGeneration.INSERT || property.getGeneration() == PropertyGeneration.ALWAYS ) + .setUpdateGenerated( property.getGeneration() == PropertyGeneration.ALWAYS ) + .setNullable( property.isOptional() ) + .setDirtyCheckable( property.isUpdateable() && !lazy ) + .setVersionable( property.isOptimisticLocked() ) + .setCascadeStyle( property.getCascadeStyle() ) + .createInformation(), unsavedValue ); } @@ -194,52 +214,38 @@ public class PropertyFactory { * @param lazyAvailable Is property lazy loading currently available. * @return The appropriate VersionProperty definition. */ - public static VersionProperty buildVersionProperty(BasicAttributeBinding property, boolean lazyAvailable) { - String mappedUnsavedValue = ( (KeyValue) property.getValue() ).getNullValue(); + public static VersionProperty buildVersionProperty( + EntityPersister persister, + BasicAttributeBinding property, + boolean lazyAvailable) { + throw new NotYetImplementedException(); + } - VersionValue unsavedValue = UnsavedValueFactory.getUnsavedVersionValue( - mappedUnsavedValue, - getGetter( property ), - (VersionType) property.getHibernateTypeDescriptor().getResolvedTypeMapping(), - getConstructor( (EntityBinding) property.getContainer() ) - ); - - boolean lazy = lazyAvailable && property.isLazy(); - - final CascadeStyle cascadeStyle = property.isAssociation() - ? ( (AssociationAttributeBinding) property ).getCascadeStyle() - : CascadeStyles.NONE; - - return new VersionProperty( - property.getAttribute().getName(), - null, - property.getHibernateTypeDescriptor().getResolvedTypeMapping(), - lazy, - true, // insertable - true, // updatable - property.getGeneration() == PropertyGeneration.INSERT - || property.getGeneration() == PropertyGeneration.ALWAYS, - property.getGeneration() == PropertyGeneration.ALWAYS, - property.isNullable(), - !lazy, - property.isIncludedInOptimisticLocking(), - cascadeStyle, - unsavedValue - ); + public static enum NonIdentifierAttributeNature { + BASIC, + COMPOSITE, + ANY, + ENTITY, + COLLECTION } /** - * Generate a "standard" (i.e., non-identifier and non-version) based on the given - * mapped property. + * Generate a non-identifier (and non-version) attribute based on the given mapped property from the given entity * * @param property The mapped property. * @param lazyAvailable Is property lazy loading currently available. - * @return The appropriate StandardProperty definition. + * @return The appropriate NonIdentifierProperty definition. */ - public static StandardProperty buildStandardProperty(Property property, boolean lazyAvailable) { - + public static NonIdentifierAttribute buildEntityBasedAttribute( + EntityPersister persister, + SessionFactoryImplementor sessionFactory, + int attributeNumber, + Property property, + boolean lazyAvailable) { final Type type = property.getValue().getType(); - + + final NonIdentifierAttributeNature nature = decode( type ); + // we need to dirty check collections, since they can cause an owner // version number increment @@ -250,30 +256,147 @@ public class PropertyFactory { boolean alwaysDirtyCheck = type.isAssociationType() && ( (AssociationType) type ).isAlwaysDirtyChecked(); + switch ( nature ) { + case BASIC: { + return new EntityBasedBasicAttribute( + persister, + sessionFactory, + attributeNumber, + property.getName(), + type, + new BaselineAttributeInformation.Builder() + .setLazy( lazyAvailable && property.isLazy() ) + .setInsertable( property.isInsertable() ) + .setUpdateable( property.isUpdateable() ) + .setInsertGenerated( + property.getGeneration() == PropertyGeneration.INSERT + || property.getGeneration() == PropertyGeneration.ALWAYS + ) + .setUpdateGenerated( property.getGeneration() == PropertyGeneration.ALWAYS ) + .setNullable( property.isOptional() ) + .setDirtyCheckable( alwaysDirtyCheck || property.isUpdateable() ) + .setVersionable( property.isOptimisticLocked() ) + .setCascadeStyle( property.getCascadeStyle() ) + .setFetchMode( property.getValue().getFetchMode() ) + .createInformation() + ); + } + case COMPOSITE: { + return new EntityBasedCompositeAttribute( + persister, + sessionFactory, + attributeNumber, + property.getName(), + (CompositeType) type, + new BaselineAttributeInformation.Builder() + .setLazy( lazyAvailable && property.isLazy() ) + .setInsertable( property.isInsertable() ) + .setUpdateable( property.isUpdateable() ) + .setInsertGenerated( + property.getGeneration() == PropertyGeneration.INSERT + || property.getGeneration() == PropertyGeneration.ALWAYS + ) + .setUpdateGenerated( property.getGeneration() == PropertyGeneration.ALWAYS ) + .setNullable( property.isOptional() ) + .setDirtyCheckable( alwaysDirtyCheck || property.isUpdateable() ) + .setVersionable( property.isOptimisticLocked() ) + .setCascadeStyle( property.getCascadeStyle() ) + .setFetchMode( property.getValue().getFetchMode() ) + .createInformation() + ); + } + case ENTITY: + case ANY: + case COLLECTION: { + return new EntityBasedAssociationAttribute( + persister, + sessionFactory, + attributeNumber, + property.getName(), + (AssociationType) type, + new BaselineAttributeInformation.Builder() + .setLazy( lazyAvailable && property.isLazy() ) + .setInsertable( property.isInsertable() ) + .setUpdateable( property.isUpdateable() ) + .setInsertGenerated( + property.getGeneration() == PropertyGeneration.INSERT + || property.getGeneration() == PropertyGeneration.ALWAYS + ) + .setUpdateGenerated( property.getGeneration() == PropertyGeneration.ALWAYS ) + .setNullable( property.isOptional() ) + .setDirtyCheckable( alwaysDirtyCheck || property.isUpdateable() ) + .setVersionable( property.isOptimisticLocked() ) + .setCascadeStyle( property.getCascadeStyle() ) + .setFetchMode( property.getValue().getFetchMode() ) + .createInformation() + ); + } + default: { + throw new HibernateException( "Internal error" ); + } + } + } + + private static NonIdentifierAttributeNature decode(Type type) { + if ( type.isAssociationType() ) { + AssociationType associationType = (AssociationType) type; + + if ( type.isComponentType() ) { + // an any type is both an association and a composite... + return NonIdentifierAttributeNature.ANY; + } + + return type.isCollectionType() + ? NonIdentifierAttributeNature.COLLECTION + : NonIdentifierAttributeNature.ENTITY; + } + else { + if ( type.isComponentType() ) { + return NonIdentifierAttributeNature.COMPOSITE; + } + + return NonIdentifierAttributeNature.BASIC; + } + } + + @Deprecated + public static StandardProperty buildStandardProperty(Property property, boolean lazyAvailable) { + final Type type = property.getValue().getType(); + + // we need to dirty check collections, since they can cause an owner + // version number increment + + // we need to dirty check many-to-ones with not-found="ignore" in order + // to update the cache (not the database), since in this case a null + // entity reference can lose information + + boolean alwaysDirtyCheck = type.isAssociationType() && + ( (AssociationType) type ).isAlwaysDirtyChecked(); + return new StandardProperty( property.getName(), - property.getNodeName(), type, lazyAvailable && property.isLazy(), property.isInsertable(), property.isUpdateable(), - property.getGeneration() == PropertyGeneration.INSERT || property.getGeneration() == PropertyGeneration.ALWAYS, + property.getGeneration() == PropertyGeneration.INSERT || property.getGeneration() == PropertyGeneration.ALWAYS, property.getGeneration() == PropertyGeneration.ALWAYS, property.isOptional(), alwaysDirtyCheck || property.isUpdateable(), property.isOptimisticLocked(), property.getCascadeStyle(), - property.getValue().getFetchMode() - ); + property.getValue().getFetchMode() + ); } + /** * Generate a "standard" (i.e., non-identifier and non-version) based on the given * mapped property. * * @param property The mapped property. * @param lazyAvailable Is property lazy loading currently available. - * @return The appropriate StandardProperty definition. + * @return The appropriate NonIdentifierProperty definition. */ public static StandardProperty buildStandardProperty(AttributeBinding property, boolean lazyAvailable) { @@ -299,7 +422,6 @@ public class PropertyFactory { return new StandardProperty( singularAttributeBinding.getAttribute().getName(), - null, type, lazyAvailable && singularAttributeBinding.isLazy(), true, // insertable @@ -325,7 +447,6 @@ public class PropertyFactory { return new StandardProperty( pluralAttributeBinding.getAttribute().getName(), - null, type, lazyAvailable && pluralAttributeBinding.isLazy(), // TODO: fix this when HHH-6356 is fixed; for now assume AbstractPluralAttributeBinding is updatable and insertable diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/StandardProperty.java b/hibernate-core/src/main/java/org/hibernate/tuple/StandardProperty.java index 30eb8da053..a4c1adb8c2 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/StandardProperty.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/StandardProperty.java @@ -1,10 +1,10 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * Copyright (c) 2008, 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 Middleware LLC. + * 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 @@ -20,118 +20,69 @@ * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA - * */ package org.hibernate.tuple; + import org.hibernate.FetchMode; import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.type.Type; /** - * Represents a basic property within the Hibernate runtime-metamodel. + * Represents a non-identifier property within the Hibernate runtime-metamodel. * * @author Steve Ebersole */ -public class StandardProperty extends Property { +@Deprecated +public class StandardProperty extends AbstractNonIdentifierAttribute implements NonIdentifierAttribute { - private final boolean lazy; - private final boolean insertable; - private final boolean updateable; - private final boolean insertGenerated; - private final boolean updateGenerated; - private final boolean nullable; - private final boolean dirtyCheckable; - private final boolean versionable; - private final CascadeStyle cascadeStyle; - private final FetchMode fetchMode; - - /** - * Constructs StandardProperty instances. - * - * @param name The name by which the property can be referenced within - * its owner. - * @param node The node name to use for XML-based representation of this - * property. - * @param type The Hibernate Type of this property. - * @param lazy Should this property be handled lazily? - * @param insertable Is this property an insertable value? - * @param updateable Is this property an updateable value? - * @param insertGenerated Is this property generated in the database on insert? - * @param updateGenerated Is this property generated in the database on update? - * @param nullable Is this property a nullable value? - * @param checkable Is this property a checkable value? - * @param versionable Is this property a versionable value? - * @param cascadeStyle The cascade style for this property's value. - * @param fetchMode Any fetch mode defined for this property - */ - public StandardProperty( - String name, - String node, - Type type, - boolean lazy, - boolean insertable, - boolean updateable, - boolean insertGenerated, - boolean updateGenerated, - boolean nullable, - boolean checkable, - boolean versionable, - CascadeStyle cascadeStyle, - FetchMode fetchMode) { - super(name, node, type); - this.lazy = lazy; - this.insertable = insertable; - this.updateable = updateable; - this.insertGenerated = insertGenerated; - this.updateGenerated = updateGenerated; - this.nullable = nullable; - this.dirtyCheckable = checkable; - this.versionable = versionable; - this.cascadeStyle = cascadeStyle; - this.fetchMode = fetchMode; - } - - public boolean isLazy() { - return lazy; - } - - public boolean isInsertable() { - return insertable; - } - - public boolean isUpdateable() { - return updateable; - } - - public boolean isInsertGenerated() { - return insertGenerated; - } - - public boolean isUpdateGenerated() { - return updateGenerated; - } - - public boolean isNullable() { - return nullable; - } - - public boolean isDirtyCheckable(boolean hasUninitializedProperties) { - return isDirtyCheckable() && ( !hasUninitializedProperties || !isLazy() ); - } - - public boolean isDirtyCheckable() { - return dirtyCheckable; - } - - public boolean isVersionable() { - return versionable; - } - - public CascadeStyle getCascadeStyle() { - return cascadeStyle; - } - - public FetchMode getFetchMode() { - return fetchMode; + /** + * Constructs NonIdentifierProperty instances. + * + * @param name The name by which the property can be referenced within + * its owner. + * @param type The Hibernate Type of this property. + * @param lazy Should this property be handled lazily? + * @param insertable Is this property an insertable value? + * @param updateable Is this property an updateable value? + * @param insertGenerated Is this property generated in the database on insert? + * @param updateGenerated Is this property generated in the database on update? + * @param nullable Is this property a nullable value? + * @param checkable Is this property a checkable value? + * @param versionable Is this property a versionable value? + * @param cascadeStyle The cascade style for this property's value. + * @param fetchMode Any fetch mode defined for this property + */ + public StandardProperty( + String name, + Type type, + boolean lazy, + boolean insertable, + boolean updateable, + boolean insertGenerated, + boolean updateGenerated, + boolean nullable, + boolean checkable, + boolean versionable, + CascadeStyle cascadeStyle, + FetchMode fetchMode) { + super( + null, + null, + -1, + name, + type, + new BaselineAttributeInformation.Builder() + .setLazy( lazy ) + .setInsertable( insertable ) + .setUpdateable( updateable ) + .setInsertGenerated( insertGenerated ) + .setUpdateGenerated( updateGenerated ) + .setNullable( nullable ) + .setDirtyCheckable( checkable ) + .setVersionable( versionable ) + .setCascadeStyle( cascadeStyle ) + .setFetchMode( fetchMode ) + .createInformation() + ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/VersionProperty.java b/hibernate-core/src/main/java/org/hibernate/tuple/VersionProperty.java deleted file mode 100644 index 639939eb4c..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/tuple/VersionProperty.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC. - * - * 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.tuple; -import org.hibernate.engine.spi.CascadeStyle; -import org.hibernate.engine.spi.VersionValue; -import org.hibernate.type.Type; - -/** - * Represents a version property within the Hibernate runtime-metamodel. - * - * @author Steve Ebersole - */ -public class VersionProperty extends StandardProperty { - - private final VersionValue unsavedValue; - - /** - * Constructs VersionProperty instances. - * - * @param name The name by which the property can be referenced within - * its owner. - * @param node The node name to use for XML-based representation of this - * property. - * @param type The Hibernate Type of this property. - * @param lazy Should this property be handled lazily? - * @param insertable Is this property an insertable value? - * @param updateable Is this property an updateable value? - * @param insertGenerated Is this property generated in the database on insert? - * @param updateGenerated Is this property generated in the database on update? - * @param nullable Is this property a nullable value? - * @param checkable Is this property a checkable value? - * @param versionable Is this property a versionable value? - * @param cascadeStyle The cascade style for this property's value. - * @param unsavedValue The value which, if found as the value of - * this (i.e., the version) property, represents new (i.e., un-saved) - * instances of the owning entity. - */ - public VersionProperty( - String name, - String node, - Type type, - boolean lazy, - boolean insertable, - boolean updateable, - boolean insertGenerated, - boolean updateGenerated, - boolean nullable, - boolean checkable, - boolean versionable, - CascadeStyle cascadeStyle, - VersionValue unsavedValue) { - super( name, node, type, lazy, insertable, updateable, insertGenerated, updateGenerated, nullable, checkable, versionable, cascadeStyle, null ); - this.unsavedValue = unsavedValue; - } - - public VersionValue getUnsavedValue() { - return unsavedValue; - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeBasedAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeBasedAttribute.java new file mode 100644 index 0000000000..9dd83c8885 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeBasedAttribute.java @@ -0,0 +1,61 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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.tuple.component; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.tuple.AbstractNonIdentifierAttribute; +import org.hibernate.tuple.BaselineAttributeInformation; +import org.hibernate.tuple.NonIdentifierAttribute; +import org.hibernate.type.Type; + +/** + * @author Steve Ebersole + */ +public abstract class AbstractCompositeBasedAttribute + extends AbstractNonIdentifierAttribute + implements NonIdentifierAttribute { + + private final int ownerAttributeNumber; + + public AbstractCompositeBasedAttribute( + AbstractCompositeDefinition source, + SessionFactoryImplementor sessionFactory, + int attributeNumber, + String attributeName, + Type attributeType, + BaselineAttributeInformation baselineInfo, + int ownerAttributeNumber) { + super( source, sessionFactory, attributeNumber, attributeName, attributeType, baselineInfo ); + this.ownerAttributeNumber = ownerAttributeNumber; + } + + protected int ownerAttributeNumber() { + return ownerAttributeNumber; + } + + @Override + public AbstractCompositeDefinition getSource() { + return (AbstractCompositeDefinition) super.getSource(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeDefinition.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeDefinition.java new file mode 100644 index 0000000000..87ca927f9d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeDefinition.java @@ -0,0 +1,202 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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.tuple.component; + +import java.util.Iterator; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.Joinable; +import org.hibernate.persister.entity.OuterJoinLoadable; +import org.hibernate.persister.walking.spi.AssociationKey; +import org.hibernate.persister.walking.spi.AttributeDefinition; +import org.hibernate.persister.walking.spi.AttributeSource; +import org.hibernate.persister.walking.spi.CompositeDefinition; +import org.hibernate.persister.walking.spi.EntityDefinition; +import org.hibernate.tuple.AbstractNonIdentifierAttribute; +import org.hibernate.tuple.BaselineAttributeInformation; +import org.hibernate.type.AssociationType; +import org.hibernate.type.CompositeType; +import org.hibernate.type.ForeignKeyDirection; +import org.hibernate.type.Type; + +import static org.hibernate.engine.internal.JoinHelper.getLHSColumnNames; +import static org.hibernate.engine.internal.JoinHelper.getLHSTableName; +import static org.hibernate.engine.internal.JoinHelper.getRHSColumnNames; + +/** + * @author Steve Ebersole + */ +public abstract class AbstractCompositeDefinition extends AbstractNonIdentifierAttribute implements CompositeDefinition { + protected AbstractCompositeDefinition( + AttributeSource source, + SessionFactoryImplementor sessionFactory, + int attributeNumber, + String attributeName, + CompositeType attributeType, + BaselineAttributeInformation baselineInfo) { + super( source, sessionFactory, attributeNumber, attributeName, attributeType, baselineInfo ); + } + + @Override + public CompositeType getType() { + return (CompositeType) super.getType(); + } + + @Override + public Iterable getAttributes() { + return new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + private final int numberOfAttributes = getType().getSubtypes().length; + private int currentAttributeNumber = 0; + private int currentColumnPosition = 0; + + @Override + public boolean hasNext() { + return currentAttributeNumber < numberOfAttributes; + } + + @Override + public AttributeDefinition next() { + final int attributeNumber = currentAttributeNumber; + currentAttributeNumber++; + + final String name = getType().getPropertyNames()[attributeNumber]; + final Type type = getType().getSubtypes()[attributeNumber]; + + int columnPosition = currentColumnPosition; + currentColumnPosition += type.getColumnSpan( sessionFactory() ); + + if ( type.isAssociationType() ) { + // we build the association-key here because of the "goofiness" with 'currentColumnPosition' + final AssociationKey associationKey; + final AssociationType aType = (AssociationType) type; + final Joinable joinable = aType.getAssociatedJoinable( sessionFactory() ); + if ( aType.getForeignKeyDirection() == ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT ) { + associationKey = new AssociationKey( + getLHSTableName( + aType, + attributeNumber(), + (OuterJoinLoadable) joinable + ), + getLHSColumnNames( + aType, + attributeNumber(), + columnPosition, + (OuterJoinLoadable) joinable, + sessionFactory() + ) + ); + } + else { + associationKey = new AssociationKey( + joinable.getTableName(), + getRHSColumnNames( aType, sessionFactory() ) + ); + } + + return new CompositeBasedAssociationAttribute( + AbstractCompositeDefinition.this, + sessionFactory(), + currentAttributeNumber, + name, + (AssociationType) type, + new BaselineAttributeInformation.Builder() + .setInsertable( AbstractCompositeDefinition.this.isInsertable() ) + .setUpdateable( AbstractCompositeDefinition.this.isUpdateable() ) + .setInsertGenerated( AbstractCompositeDefinition.this.isInsertGenerated() ) + .setUpdateGenerated( AbstractCompositeDefinition.this.isUpdateGenerated() ) + .setNullable( getType().getPropertyNullability()[currentAttributeNumber] ) + .setDirtyCheckable( true ) + .setVersionable( AbstractCompositeDefinition.this.isVersionable() ) + .setCascadeStyle( getType().getCascadeStyle( currentAttributeNumber ) ) + .setFetchMode( getType().getFetchMode( currentAttributeNumber ) ) + .createInformation(), + AbstractCompositeDefinition.this.attributeNumber(), + associationKey + ); + } + else if ( type.isComponentType() ) { + return new CompositeBasedCompositeAttribute( + AbstractCompositeDefinition.this, + sessionFactory(), + currentAttributeNumber, + name, + (CompositeType) type, + new BaselineAttributeInformation.Builder() + .setInsertable( AbstractCompositeDefinition.this.isInsertable() ) + .setUpdateable( AbstractCompositeDefinition.this.isUpdateable() ) + .setInsertGenerated( AbstractCompositeDefinition.this.isInsertGenerated() ) + .setUpdateGenerated( AbstractCompositeDefinition.this.isUpdateGenerated() ) + .setNullable( getType().getPropertyNullability()[currentAttributeNumber] ) + .setDirtyCheckable( true ) + .setVersionable( AbstractCompositeDefinition.this.isVersionable() ) + .setCascadeStyle( getType().getCascadeStyle( currentAttributeNumber ) ) + .setFetchMode( getType().getFetchMode( currentAttributeNumber ) ) + .createInformation() + ); + } + else { + return new CompositeBasedBasicAttribute( + AbstractCompositeDefinition.this, + sessionFactory(), + currentAttributeNumber, + name, + type, + new BaselineAttributeInformation.Builder() + .setInsertable( AbstractCompositeDefinition.this.isInsertable() ) + .setUpdateable( AbstractCompositeDefinition.this.isUpdateable() ) + .setInsertGenerated( AbstractCompositeDefinition.this.isInsertGenerated() ) + .setUpdateGenerated( AbstractCompositeDefinition.this.isUpdateGenerated() ) + .setNullable( getType().getPropertyNullability()[currentAttributeNumber] ) + .setDirtyCheckable( true ) + .setVersionable( AbstractCompositeDefinition.this.isVersionable() ) + .setCascadeStyle( getType().getCascadeStyle( currentAttributeNumber ) ) + .setFetchMode( getType().getFetchMode( currentAttributeNumber ) ) + .createInformation() + ); + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException( "Remove operation not supported here" ); + } + }; + } + }; + } + + public EntityPersister locateOwningPersister() { + if ( EntityDefinition.class.isInstance( getSource() ) ) { + return ( (EntityDefinition) getSource() ).getEntityPersister(); + } + else { + return ( (AbstractCompositeDefinition) getSource() ).locateOwningPersister(); + } + } +} + diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/ComponentMetamodel.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/ComponentMetamodel.java index 2de23be05a..2ccd7ffebe 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/component/ComponentMetamodel.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/ComponentMetamodel.java @@ -32,8 +32,8 @@ import org.hibernate.EntityMode; import org.hibernate.HibernateException; import org.hibernate.mapping.Component; import org.hibernate.mapping.Property; -import org.hibernate.tuple.PropertyFactory; import org.hibernate.tuple.StandardProperty; +import org.hibernate.tuple.PropertyFactory; /** * Centralizes metamodel information about a component. diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java new file mode 100644 index 0000000000..c064c6e457 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java @@ -0,0 +1,157 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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.tuple.component; + +import org.hibernate.FetchMode; +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.FetchStyle; +import org.hibernate.engine.FetchTiming; +import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.PropertyPath; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.Joinable; +import org.hibernate.persister.walking.internal.Helper; +import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; +import org.hibernate.persister.walking.spi.AssociationKey; +import org.hibernate.persister.walking.spi.CollectionDefinition; +import org.hibernate.persister.walking.spi.EntityDefinition; +import org.hibernate.tuple.BaselineAttributeInformation; +import org.hibernate.type.AssociationType; +import org.hibernate.type.CompositeType; + +/** + * @author Steve Ebersole + */ +public class CompositeBasedAssociationAttribute + extends AbstractCompositeBasedAttribute + implements AssociationAttributeDefinition { + + private final AssociationKey associationKey; + private Joinable joinable; + + public CompositeBasedAssociationAttribute( + AbstractCompositeDefinition source, + SessionFactoryImplementor factory, + int attributeNumber, + String attributeName, + AssociationType attributeType, + BaselineAttributeInformation baselineInfo, + int ownerAttributeNumber, + AssociationKey associationKey) { + super( source, factory, attributeNumber, attributeName, attributeType, baselineInfo, ownerAttributeNumber ); + this.associationKey = associationKey; + } + + @Override + public AssociationType getType() { + return (AssociationType) super.getType(); + } + + protected Joinable getJoinable() { + if ( joinable == null ) { + joinable = getType().getAssociatedJoinable( sessionFactory() ); + } + return joinable; + } + + @Override + public AssociationKey getAssociationKey() { + return associationKey; + } + + @Override + public boolean isCollection() { + return getJoinable().isCollection(); + } + + @Override + public EntityDefinition toEntityDefinition() { + if ( isCollection() ) { + throw new IllegalStateException( "Cannot treat collection attribute as entity type" ); + } + return (EntityPersister) getJoinable(); + } + + @Override + public CollectionDefinition toCollectionDefinition() { + if ( isCollection() ) { + throw new IllegalStateException( "Cannot treat entity attribute as collection type" ); + } + return (CollectionPersister) getJoinable(); + } + + @Override + public FetchStrategy determineFetchPlan(LoadQueryInfluencers loadQueryInfluencers, PropertyPath propertyPath) { + final EntityPersister owningPersister = locateOwningPersister(); + + FetchStyle style = determineFetchStyleByProfile( + loadQueryInfluencers, + owningPersister, + propertyPath, + ownerAttributeNumber() + ); + if ( style == null ) { + style = determineFetchStyleByMetadata( + getSource().getType().getFetchMode( attributeNumber() ), + getType() + ); + } + + return new FetchStrategy( determineFetchTiming( style ), style ); + } + + protected FetchStyle determineFetchStyleByProfile( + LoadQueryInfluencers loadQueryInfluencers, + EntityPersister owningPersister, + PropertyPath propertyPath, + int ownerAttributeNumber) { + return Helper.determineFetchStyleByProfile( + loadQueryInfluencers, + owningPersister, + propertyPath, + ownerAttributeNumber + ); + } + + protected FetchStyle determineFetchStyleByMetadata(FetchMode fetchMode, AssociationType type) { + return Helper.determineFetchStyleByMetadata( fetchMode, type, sessionFactory() ); + } + + private FetchTiming determineFetchTiming(FetchStyle style) { + return Helper.determineFetchTiming( style, getType(), sessionFactory() ); + } + + private EntityPersister locateOwningPersister() { + return getSource().locateOwningPersister(); + } + + @Override + public CascadeStyle determineCascadeStyle() { + final CompositeType compositeType = (CompositeType) locateOwningPersister().getPropertyType( getName() ); + return compositeType.getCascadeStyle( attributeNumber() ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedBasicAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedBasicAttribute.java new file mode 100644 index 0000000000..a975635599 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedBasicAttribute.java @@ -0,0 +1,45 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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.tuple.component; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.persister.walking.spi.AttributeSource; +import org.hibernate.tuple.AbstractNonIdentifierAttribute; +import org.hibernate.tuple.BaselineAttributeInformation; +import org.hibernate.type.Type; + +/** + * @author Steve Ebersole + */ +public class CompositeBasedBasicAttribute extends AbstractNonIdentifierAttribute { + protected CompositeBasedBasicAttribute( + AttributeSource source, + SessionFactoryImplementor sessionFactory, + int attributeNumber, + String attributeName, + Type attributeType, + BaselineAttributeInformation baselineInfo) { + super( source, sessionFactory, attributeNumber, attributeName, attributeType, baselineInfo ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedCompositeAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedCompositeAttribute.java new file mode 100644 index 0000000000..1cca99c828 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedCompositeAttribute.java @@ -0,0 +1,46 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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.tuple.component; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.persister.walking.spi.CompositeDefinition; +import org.hibernate.tuple.BaselineAttributeInformation; +import org.hibernate.type.CompositeType; + +/** + * @author Steve Ebersole + */ +public class CompositeBasedCompositeAttribute + extends AbstractCompositeDefinition + implements CompositeDefinition { + public CompositeBasedCompositeAttribute( + CompositeDefinition source, + SessionFactoryImplementor sessionFactory, + int attributeNumber, + String attributeName, + CompositeType attributeType, + BaselineAttributeInformation baselineInfo) { + super( source, sessionFactory, attributeNumber, attributeName, attributeType, baselineInfo ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/AbstractEntityBasedAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/AbstractEntityBasedAttribute.java new file mode 100644 index 0000000000..64db5884b3 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/AbstractEntityBasedAttribute.java @@ -0,0 +1,50 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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.tuple.entity; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.tuple.AbstractNonIdentifierAttribute; +import org.hibernate.tuple.BaselineAttributeInformation; +import org.hibernate.type.Type; + +/** + * @author Steve Ebersole + */ +public abstract class AbstractEntityBasedAttribute extends AbstractNonIdentifierAttribute { + protected AbstractEntityBasedAttribute( + EntityPersister source, + SessionFactoryImplementor sessionFactory, + int attributeNumber, + String attributeName, + Type attributeType, + BaselineAttributeInformation attributeInformation) { + super( source, sessionFactory, attributeNumber, attributeName, attributeType, attributeInformation ); + } + + @Override + public EntityPersister getSource() { + return (EntityPersister) super.getSource(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/AbstractEntityTuplizer.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/AbstractEntityTuplizer.java index d82567ac21..6c1c08a6f2 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/entity/AbstractEntityTuplizer.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/AbstractEntityTuplizer.java @@ -56,8 +56,9 @@ import org.hibernate.property.Setter; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.ProxyFactory; import org.hibernate.tuple.Instantiator; +import org.hibernate.tuple.NonIdentifierAttribute; import org.hibernate.tuple.StandardProperty; -import org.hibernate.tuple.VersionProperty; +import org.hibernate.tuple.entity.VersionProperty; import org.hibernate.type.ComponentType; import org.hibernate.type.CompositeType; import org.hibernate.type.EntityType; @@ -604,7 +605,7 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer { final Object[] result = new Object[span]; for ( int j = 0; j < span; j++ ) { - StandardProperty property = entityMetamodel.getProperties()[j]; + NonIdentifierAttribute property = entityMetamodel.getProperties()[j]; if ( getAll || !property.isLazy() ) { result[j] = getters[j].get( entity ); } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedAssociationAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedAssociationAttribute.java new file mode 100644 index 0000000000..f19f1d8439 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedAssociationAttribute.java @@ -0,0 +1,132 @@ +package org.hibernate.tuple.entity; + +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.FetchStyle; +import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.PropertyPath; +import org.hibernate.persister.collection.QueryableCollection; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.Joinable; +import org.hibernate.persister.entity.OuterJoinLoadable; +import org.hibernate.persister.walking.internal.Helper; +import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; +import org.hibernate.persister.walking.spi.AssociationKey; +import org.hibernate.persister.walking.spi.CollectionDefinition; +import org.hibernate.persister.walking.spi.EntityDefinition; +import org.hibernate.tuple.BaselineAttributeInformation; +import org.hibernate.type.AssociationType; +import org.hibernate.type.ForeignKeyDirection; + +import static org.hibernate.engine.internal.JoinHelper.getLHSColumnNames; +import static org.hibernate.engine.internal.JoinHelper.getLHSTableName; +import static org.hibernate.engine.internal.JoinHelper.getRHSColumnNames; + +/** +* @author Steve Ebersole +*/ +public class EntityBasedAssociationAttribute + extends AbstractEntityBasedAttribute + implements AssociationAttributeDefinition { + + private Joinable joinable; + + public EntityBasedAssociationAttribute( + EntityPersister source, + SessionFactoryImplementor sessionFactory, + int attributeNumber, + String attributeName, + AssociationType attributeType, + BaselineAttributeInformation baselineInfo) { + super( source, sessionFactory, attributeNumber, attributeName, attributeType, baselineInfo ); + } + + @Override + public AssociationType getType() { + return (AssociationType) super.getType(); + } + + protected Joinable getJoinable() { + if ( joinable == null ) { + joinable = getType().getAssociatedJoinable( sessionFactory() ); + } + return joinable; + } + + @Override + public AssociationKey getAssociationKey() { + final AssociationType type = getType(); + final Joinable joinable = type.getAssociatedJoinable( sessionFactory() ); + + if ( type.getForeignKeyDirection() == ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT ) { + final String lhsTableName; + final String[] lhsColumnNames; + + if ( joinable.isCollection() ) { + final QueryableCollection collectionPersister = (QueryableCollection) joinable; + lhsTableName = collectionPersister.getTableName(); + lhsColumnNames = collectionPersister.getElementColumnNames(); + } + else { + final OuterJoinLoadable entityPersister = (OuterJoinLoadable) joinable; + lhsTableName = getLHSTableName( type, attributeNumber(), entityPersister ); + lhsColumnNames = getLHSColumnNames( type, attributeNumber(), entityPersister, sessionFactory() ); + } + return new AssociationKey( lhsTableName, lhsColumnNames ); + } + else { + return new AssociationKey( joinable.getTableName(), getRHSColumnNames( type, sessionFactory() ) ); + } + } + + @Override + public boolean isCollection() { + return getJoinable().isCollection(); + } + + @Override + public EntityDefinition toEntityDefinition() { + if ( isCollection() ) { + throw new IllegalStateException( "Cannot treat collection-valued attribute as entity type" ); + } + return (EntityPersister) getJoinable(); + } + + @Override + public CollectionDefinition toCollectionDefinition() { + if ( ! isCollection() ) { + throw new IllegalStateException( "Cannot treat entity-valued attribute as collection type" ); + } + return (QueryableCollection) getJoinable(); + } + + @Override + public FetchStrategy determineFetchPlan(LoadQueryInfluencers loadQueryInfluencers, PropertyPath propertyPath) { + final EntityPersister owningPersister = getSource().getEntityPersister(); + + FetchStyle style = Helper.determineFetchStyleByProfile( + loadQueryInfluencers, + owningPersister, + propertyPath, + attributeNumber() + ); + if ( style == null ) { + style = Helper.determineFetchStyleByMetadata( + ((OuterJoinLoadable) getSource().getEntityPersister()).getFetchMode( attributeNumber() ), + getType(), + sessionFactory() + ); + } + + return new FetchStrategy( + Helper.determineFetchTiming( style, getType(), sessionFactory() ), + style + ); + } + + @Override + public CascadeStyle determineCascadeStyle() { + return getSource().getEntityPersister().getPropertyCascadeStyles()[attributeNumber()]; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedBasicAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedBasicAttribute.java new file mode 100644 index 0000000000..ed08bfca29 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedBasicAttribute.java @@ -0,0 +1,44 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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.tuple.entity; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.tuple.BaselineAttributeInformation; +import org.hibernate.type.Type; + +/** + * @author Steve Ebersole + */ +public class EntityBasedBasicAttribute extends AbstractEntityBasedAttribute { + public EntityBasedBasicAttribute( + EntityPersister source, + SessionFactoryImplementor factory, + int attributeNumber, + String attributeName, + Type attributeType, + BaselineAttributeInformation baselineInfo) { + super( source, factory, attributeNumber, attributeName, attributeType, baselineInfo ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedCompositeAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedCompositeAttribute.java new file mode 100644 index 0000000000..514bdd5dd5 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedCompositeAttribute.java @@ -0,0 +1,49 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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.tuple.entity; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.tuple.component.AbstractCompositeDefinition; +import org.hibernate.persister.walking.spi.CompositeDefinition; +import org.hibernate.tuple.BaselineAttributeInformation; +import org.hibernate.type.CompositeType; + +/** + * @author Steve Ebersole + */ +public class EntityBasedCompositeAttribute + extends AbstractCompositeDefinition + implements CompositeDefinition { + + public EntityBasedCompositeAttribute( + EntityPersister source, + SessionFactoryImplementor factory, + int attributeNumber, + String attributeName, + CompositeType attributeType, + BaselineAttributeInformation baselineInfo) { + super( source, factory, attributeNumber, attributeName, attributeType, baselineInfo ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java index ac2d02a4d0..7d0b2717fe 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java @@ -57,10 +57,12 @@ import org.hibernate.metamodel.binding.BasicAttributeBinding; import org.hibernate.metamodel.binding.EntityBinding; import org.hibernate.metamodel.domain.Attribute; import org.hibernate.metamodel.domain.SingularAttribute; +import org.hibernate.persister.entity.AbstractEntityPersister; import org.hibernate.tuple.IdentifierProperty; +import org.hibernate.tuple.NonIdentifierAttribute; import org.hibernate.tuple.PropertyFactory; import org.hibernate.tuple.StandardProperty; -import org.hibernate.tuple.VersionProperty; +import org.hibernate.tuple.entity.VersionProperty; import org.hibernate.type.AssociationType; import org.hibernate.type.CompositeType; import org.hibernate.type.EntityType; @@ -78,17 +80,18 @@ public class EntityMetamodel implements Serializable { private static final int NO_VERSION_INDX = -66; private final SessionFactoryImplementor sessionFactory; + private final AbstractEntityPersister persister; private final String name; private final String rootName; private final EntityType entityType; - private final IdentifierProperty identifierProperty; + private final IdentifierProperty identifierAttribute; private final boolean versioned; private final int propertySpan; private final int versionPropertyIndex; - private final StandardProperty[] properties; + private final NonIdentifierAttribute[] properties; // temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private final String[] propertyNames; private final Type[] propertyTypes; @@ -136,17 +139,21 @@ public class EntityMetamodel implements Serializable { private final EntityTuplizer entityTuplizer; private final EntityInstrumentationMetadata instrumentationMetadata; - public EntityMetamodel(PersistentClass persistentClass, SessionFactoryImplementor sessionFactory) { + public EntityMetamodel( + PersistentClass persistentClass, + AbstractEntityPersister persister, + SessionFactoryImplementor sessionFactory) { this.sessionFactory = sessionFactory; + this.persister = persister; name = persistentClass.getEntityName(); rootName = persistentClass.getRootClass().getEntityName(); entityType = sessionFactory.getTypeResolver().getTypeFactory().manyToOne( name ); - identifierProperty = PropertyFactory.buildIdentifierProperty( - persistentClass, - sessionFactory.getIdentifierGenerator( rootName ) - ); + identifierAttribute = PropertyFactory.buildIdentifierAttribute( + persistentClass, + sessionFactory.getIdentifierGenerator( rootName ) + ); versioned = persistentClass.isVersioned(); @@ -157,7 +164,7 @@ public class EntityMetamodel implements Serializable { boolean hasLazy = false; propertySpan = persistentClass.getPropertyClosureSpan(); - properties = new StandardProperty[propertySpan]; + properties = new NonIdentifierAttribute[propertySpan]; List naturalIdNumbers = new ArrayList(); // temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ propertyNames = new String[propertySpan]; @@ -191,10 +198,22 @@ public class EntityMetamodel implements Serializable { if ( prop == persistentClass.getVersion() ) { tempVersionProperty = i; - properties[i] = PropertyFactory.buildVersionProperty( prop, instrumentationMetadata.isInstrumented() ); + properties[i] = PropertyFactory.buildVersionProperty( + persister, + sessionFactory, + i, + prop, + instrumentationMetadata.isInstrumented() + ); } else { - properties[i] = PropertyFactory.buildStandardProperty( prop, instrumentationMetadata.isInstrumented() ); + properties[i] = PropertyFactory.buildEntityBasedAttribute( + persister, + sessionFactory, + i, + prop, + instrumentationMetadata.isInstrumented() + ); } if ( prop.isNaturalIdentifier() ) { @@ -363,15 +382,19 @@ public class EntityMetamodel implements Serializable { } } - public EntityMetamodel(EntityBinding entityBinding, SessionFactoryImplementor sessionFactory) { + public EntityMetamodel( + EntityBinding entityBinding, + AbstractEntityPersister persister, + SessionFactoryImplementor sessionFactory) { this.sessionFactory = sessionFactory; + this.persister = persister; name = entityBinding.getEntity().getName(); rootName = entityBinding.getHierarchyDetails().getRootEntityBinding().getEntity().getName(); entityType = sessionFactory.getTypeResolver().getTypeFactory().manyToOne( name ); - identifierProperty = PropertyFactory.buildIdentifierProperty( + identifierAttribute = PropertyFactory.buildIdentifierProperty( entityBinding, sessionFactory.getIdentifierGenerator( rootName ) ); @@ -398,7 +421,7 @@ public class EntityMetamodel implements Serializable { entityBinding.getAttributeBindingClosureSpan() : entityBinding.getAttributeBindingClosureSpan() - 1; - properties = new StandardProperty[propertySpan]; + properties = new NonIdentifierAttribute[propertySpan]; List naturalIdNumbers = new ArrayList(); // temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ propertyNames = new String[propertySpan]; @@ -435,6 +458,7 @@ public class EntityMetamodel implements Serializable { if ( attributeBinding == entityBinding.getHierarchyDetails().getVersioningAttributeBinding() ) { tempVersionProperty = i; properties[i] = PropertyFactory.buildVersionProperty( + persister, entityBinding.getHierarchyDetails().getVersioningAttributeBinding(), instrumentationMetadata.isInstrumented() ); @@ -595,7 +619,7 @@ public class EntityMetamodel implements Serializable { } } - private ValueInclusion determineInsertValueGenerationType(Property mappingProperty, StandardProperty runtimeProperty) { + private ValueInclusion determineInsertValueGenerationType(Property mappingProperty, NonIdentifierAttribute runtimeProperty) { if ( runtimeProperty.isInsertGenerated() ) { return ValueInclusion.FULL; } @@ -607,7 +631,7 @@ public class EntityMetamodel implements Serializable { return ValueInclusion.NONE; } - private ValueInclusion determineInsertValueGenerationType(AttributeBinding mappingProperty, StandardProperty runtimeProperty) { + private ValueInclusion determineInsertValueGenerationType(AttributeBinding mappingProperty, NonIdentifierAttribute runtimeProperty) { if ( runtimeProperty.isInsertGenerated() ) { return ValueInclusion.FULL; } @@ -636,7 +660,7 @@ public class EntityMetamodel implements Serializable { return false; } - private ValueInclusion determineUpdateValueGenerationType(Property mappingProperty, StandardProperty runtimeProperty) { + private ValueInclusion determineUpdateValueGenerationType(Property mappingProperty, NonIdentifierAttribute runtimeProperty) { if ( runtimeProperty.isUpdateGenerated() ) { return ValueInclusion.FULL; } @@ -648,7 +672,7 @@ public class EntityMetamodel implements Serializable { return ValueInclusion.NONE; } - private ValueInclusion determineUpdateValueGenerationType(AttributeBinding mappingProperty, StandardProperty runtimeProperty) { + private ValueInclusion determineUpdateValueGenerationType(AttributeBinding mappingProperty, NonIdentifierAttribute runtimeProperty) { if ( runtimeProperty.isUpdateGenerated() ) { return ValueInclusion.FULL; } @@ -762,7 +786,7 @@ public class EntityMetamodel implements Serializable { } public IdentifierProperty getIdentifierProperty() { - return identifierProperty; + return identifierAttribute; } public int getPropertySpan() { @@ -782,7 +806,7 @@ public class EntityMetamodel implements Serializable { } } - public StandardProperty[] getProperties() { + public NonIdentifierAttribute[] getProperties() { return properties; } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/VersionProperty.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/VersionProperty.java new file mode 100644 index 0000000000..51a8e67d4e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/VersionProperty.java @@ -0,0 +1,70 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2008, 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.tuple.entity; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.VersionValue; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.tuple.AbstractNonIdentifierAttribute; +import org.hibernate.tuple.BaselineAttributeInformation; +import org.hibernate.type.Type; + +/** + * Represents a version property within the Hibernate runtime-metamodel. + * + * @author Steve Ebersole + */ +public class VersionProperty extends AbstractNonIdentifierAttribute { + + private final VersionValue unsavedValue; + + /** + * Constructs VersionProperty instances. + * + * @param source Reference back to the source of this attribute (the persister) + * @param sessionFactory The session factory this is part of. + * @param attributeNumber The attribute number within thje + * @param attributeName The name by which the property can be referenced within + * its owner. + * @param attributeType The Hibernate Type of this property. + * @param attributeInformation The basic attribute information. + * @param unsavedValue The value which, if found as the value of + * this (i.e., the version) property, represents new (i.e., un-saved) + * instances of the owning entity. + */ + public VersionProperty( + EntityPersister source, + SessionFactoryImplementor sessionFactory, + int attributeNumber, + String attributeName, + Type attributeType, + BaselineAttributeInformation attributeInformation, VersionValue unsavedValue) { + super( source, sessionFactory, attributeNumber, attributeName, attributeType, attributeInformation ); + this.unsavedValue = unsavedValue; + } + + public VersionValue getUnsavedValue() { + return unsavedValue; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/type/TypeHelper.java b/hibernate-core/src/main/java/org/hibernate/type/TypeHelper.java index df23ae8603..126e792d2a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/TypeHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/type/TypeHelper.java @@ -28,7 +28,7 @@ import java.util.Map; import org.hibernate.bytecode.instrumentation.spi.LazyPropertyInitializer; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.property.BackrefPropertyAccessor; -import org.hibernate.tuple.StandardProperty; +import org.hibernate.tuple.NonIdentifierAttribute; /** * Collection of convenience methods relating to operations across arrays of types... @@ -280,7 +280,7 @@ public class TypeHelper { * @return Array containing indices of the dirty properties, or null if no properties considered dirty. */ public static int[] findDirty( - final StandardProperty[] properties, + final NonIdentifierAttribute[] properties, final Object[] currentState, final Object[] previousState, final boolean[][] includeColumns, @@ -328,7 +328,7 @@ public class TypeHelper { * @return Array containing indices of the modified properties, or null if no properties considered modified. */ public static int[] findModified( - final StandardProperty[] properties, + final NonIdentifierAttribute[] properties, final Object[] currentState, final Object[] previousState, final boolean[][] includeColumns, diff --git a/hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanBuilderTest.java b/hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanBuilderTest.java new file mode 100644 index 0000000000..597b900344 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanBuilderTest.java @@ -0,0 +1,149 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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 javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import java.util.List; + +import org.hibernate.engine.spi.CascadingActions; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.loader.plan.internal.CascadeLoadPlanBuilderStrategy; +import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.entity.EntityPersister; + +import org.junit.Test; + +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.junit4.ExtraAssertions; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +/** + * @author Steve Ebersole + */ +public class LoadPlanBuilderTest extends BaseCoreFunctionalTestCase { + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Message.class, Poster.class }; + } + + @Test + public void testSimpleBuild() { + EntityPersister ep = (EntityPersister) sessionFactory().getClassMetadata(Message.class); + SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy( + sessionFactory(), + LoadQueryInfluencers.NONE, + "abc", + 0 + ); + LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, ep ); + assertFalse( plan.hasAnyScalarReturns() ); + assertEquals( 1, plan.getReturns().size() ); + Return rtn = plan.getReturns().get( 0 ); + EntityReturn entityReturn = ExtraAssertions.assertTyping( EntityReturn.class, rtn ); + assertEquals( "abc", entityReturn.getAlias() ); + assertNotNull( entityReturn.getFetches() ); + assertEquals( 1, entityReturn.getFetches().length ); + Fetch fetch = entityReturn.getFetches()[0]; + EntityFetch entityFetch = ExtraAssertions.assertTyping( EntityFetch.class, fetch ); + assertNotNull( entityFetch.getFetches() ); + assertEquals( 0, entityFetch.getFetches().length ); + } + + @Test + public void testCascadeBasedBuild() { + EntityPersister ep = (EntityPersister) sessionFactory().getClassMetadata(Message.class); + CascadeLoadPlanBuilderStrategy strategy = new CascadeLoadPlanBuilderStrategy( + CascadingActions.MERGE, + sessionFactory(), + LoadQueryInfluencers.NONE, + "abc", + 0 + ); + LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, ep ); + assertFalse( plan.hasAnyScalarReturns() ); + assertEquals( 1, plan.getReturns().size() ); + Return rtn = plan.getReturns().get( 0 ); + EntityReturn entityReturn = ExtraAssertions.assertTyping( EntityReturn.class, rtn ); + assertEquals( "abc", entityReturn.getAlias() ); + assertNotNull( entityReturn.getFetches() ); + assertEquals( 1, entityReturn.getFetches().length ); + Fetch fetch = entityReturn.getFetches()[0]; + EntityFetch entityFetch = ExtraAssertions.assertTyping( EntityFetch.class, fetch ); + assertNotNull( entityFetch.getFetches() ); + assertEquals( 0, entityFetch.getFetches().length ); + } + + @Test + public void testCollectionInitializerCase() { + CollectionPersister cp = sessionFactory().getCollectionPersister( Poster.class.getName() + ".messages" ); + SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy( + sessionFactory(), + LoadQueryInfluencers.NONE, + "abc", + 0 + ); + LoadPlan plan = LoadPlanBuilder.buildRootCollectionLoadPlan( strategy, cp ); + assertFalse( plan.hasAnyScalarReturns() ); + assertEquals( 1, plan.getReturns().size() ); + Return rtn = plan.getReturns().get( 0 ); + CollectionReturn collectionReturn = ExtraAssertions.assertTyping( CollectionReturn.class, rtn ); + assertEquals( "abc", collectionReturn.getAlias() ); + + assertNotNull( collectionReturn.getFetches() ); + assertEquals( 1, collectionReturn.getFetches().length ); // the collection elements are fetched + Fetch fetch = collectionReturn.getFetches()[0]; + EntityFetch entityFetch = ExtraAssertions.assertTyping( EntityFetch.class, fetch ); + assertNotNull( entityFetch.getFetches() ); + assertEquals( 0, entityFetch.getFetches().length ); + } + + @Entity( name = "Message" ) + public static class Message { + @Id + private Integer id; + private String name; + @ManyToOne( cascade = CascadeType.MERGE ) + @JoinColumn + private Poster poster; + } + + @Entity( name = "Poster" ) + public static class Poster { + @Id + private Integer id; + private String name; + @OneToMany(mappedBy = "poster") + private List messages; + } + +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java b/hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java new file mode 100644 index 0000000000..07c193bb13 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java @@ -0,0 +1,177 @@ +/* + * jDocBook, processing of DocBook sources + * + * 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; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import java.util.List; + +import org.hibernate.annotations.common.util.StringHelper; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.walking.spi.AssociationVisitationStrategy; +import org.hibernate.persister.walking.spi.AttributeDefinition; +import org.hibernate.persister.walking.spi.CollectionDefinition; +import org.hibernate.persister.walking.spi.CompositeDefinition; +import org.hibernate.persister.walking.spi.EntityDefinition; +import org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor; + +import org.junit.Test; + +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +/** + * @author Steve Ebersole + */ +public class BasicWalkingTest extends BaseCoreFunctionalTestCase { + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Message.class, Poster.class }; + } + + @Test + public void testIt() { + EntityPersister ep = (EntityPersister) sessionFactory().getClassMetadata(Message.class); + MetadataDrivenModelGraphVisitor.visitEntity( + new AssociationVisitationStrategy() { + private int depth = 0; + + @Override + public void start() { + System.out.println( ">> Start" ); + } + + @Override + public void finish() { + System.out.println( "<< Finish" ); + } + + @Override + public void startingEntity(EntityDefinition entityDefinition) { + System.out.println( + String.format( + "%s Starting entity (%s)", + StringHelper.repeat( ">>", ++depth ), + entityDefinition.toString() + ) + ); + } + + @Override + public void finishingEntity(EntityDefinition entityDefinition) { + System.out.println( + String.format( + "%s Finishing entity (%s)", + StringHelper.repeat( "<<", depth-- ), + entityDefinition.toString() + ) + ); + } + + @Override + public void startingCollection(CollectionDefinition collectionDefinition) { + System.out.println( + String.format( + "%s Starting collection (%s)", + StringHelper.repeat( ">>", ++depth ), + collectionDefinition.toString() + ) + ); + } + + @Override + public void finishingCollection(CollectionDefinition collectionDefinition) { + System.out.println( + String.format( + "%s Finishing collection (%s)", + StringHelper.repeat( ">>", depth-- ), + collectionDefinition.toString() + ) + ); + } + + @Override + public void startingComposite(CompositeDefinition compositeDefinition) { + System.out.println( + String.format( + "%s Starting composite (%s)", + StringHelper.repeat( ">>", ++depth ), + compositeDefinition.toString() + ) + ); + } + + @Override + public void finishingComposite(CompositeDefinition compositeDefinition) { + System.out.println( + String.format( + "%s Finishing composite (%s)", + StringHelper.repeat( ">>", depth-- ), + compositeDefinition.toString() + ) + ); + } + + @Override + public boolean startingAttribute(AttributeDefinition attributeDefinition) { + System.out.println( + String.format( + "%s Handling attribute (%s)", + StringHelper.repeat( ">>", depth + 1 ), + attributeDefinition.toString() + ) + ); + return true; + } + + @Override + public void finishingAttribute(AttributeDefinition attributeDefinition) { + // nothing to do + } + }, + ep + ); + } + + @Entity( name = "Message" ) + public static class Message { + @Id + private Integer id; + private String name; + @ManyToOne + @JoinColumn + private Poster poster; + } + + @Entity( name = "Poster" ) + public static class Poster { + @Id + private Integer id; + private String name; + @OneToMany(mappedBy = "poster") + private List messages; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java index 3e32129981..6af678c8a5 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java +++ b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java @@ -37,6 +37,7 @@ import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy; import org.hibernate.cache.spi.entry.CacheEntry; import org.hibernate.cache.spi.entry.CacheEntryStructure; +import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.Mapping; @@ -54,6 +55,9 @@ import org.hibernate.metamodel.binding.PluralAttributeBinding; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.spi.PersisterClassResolver; +import org.hibernate.persister.walking.spi.AttributeDefinition; +import org.hibernate.persister.walking.spi.CollectionElementDefinition; +import org.hibernate.persister.walking.spi.CollectionIndexDefinition; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.tuple.entity.EntityTuplizer; import org.hibernate.tuple.entity.NonPojoInstrumentationMetadata; @@ -579,6 +583,21 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver { // TODO Auto-generated method stub return null; } + + @Override + public EntityPersister getEntityPersister() { + return this; + } + + @Override + public Iterable getEmbeddedCompositeIdentifierAttributes() { + throw new NotYetImplementedException(); + } + + @Override + public Iterable getAttributes() { + throw new NotYetImplementedException(); + } } public static class NoopCollectionPersister implements CollectionPersister { @@ -606,8 +625,23 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver { return null; //To change body of implemented methods use File | Settings | File Templates. } + @Override + public CollectionPersister getCollectionPersister() { + return this; + } + public CollectionType getCollectionType() { - return null; //To change body of implemented methods use File | Settings | File Templates. + throw new NotYetImplementedException(); + } + + @Override + public CollectionIndexDefinition getIndexDefinition() { + throw new NotYetImplementedException(); + } + + @Override + public CollectionElementDefinition getElementDefinition() { + throw new NotYetImplementedException(); } public Type getKeyType() { diff --git a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java index 62258c377b..55b96323ab 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java +++ b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java @@ -17,6 +17,7 @@ import org.hibernate.cache.spi.entry.CacheEntry; import org.hibernate.cache.spi.entry.CacheEntryStructure; import org.hibernate.cache.spi.entry.StandardCacheEntryImpl; import org.hibernate.cache.spi.entry.UnstructuredCacheEntry; +import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.engine.internal.TwoPhaseLoad; import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.Mapping; @@ -34,6 +35,7 @@ import org.hibernate.internal.util.compare.EqualsHelper; import org.hibernate.mapping.PersistentClass; import org.hibernate.metadata.ClassMetadata; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.tuple.entity.EntityTuplizer; import org.hibernate.tuple.entity.NonPojoInstrumentationMetadata; @@ -671,4 +673,19 @@ public class CustomPersister implements EntityPersister { public FilterAliasGenerator getFilterAliasGenerator(String rootAlias) { return new StaticFilterAliasGenerator(rootAlias); } + + @Override + public EntityPersister getEntityPersister() { + return this; + } + + @Override + public Iterable getEmbeddedCompositeIdentifierAttributes() { + throw new NotYetImplementedException(); + } + + @Override + public Iterable getAttributes() { + throw new NotYetImplementedException(); + } }