HHH-7841 - Redesign Loader

This commit is contained in:
Gail Badner 2013-05-13 11:54:13 -07:00
parent dfe33ffa1a
commit 4620ff4b4f
18 changed files with 964 additions and 193 deletions

View File

@ -56,12 +56,10 @@ public abstract class AbstractJoinableAssociationImpl implements JoinableAssocia
EntityReference currentEntityReference,
CollectionReference currentCollectionReference,
String withClause,
boolean isNullable,
boolean hasRestriction,
Map<String, Filter> enabledFilters) throws MappingException {
this.propertyPath = currentFetch.getPropertyPath();
final OuterJoinLoadable ownerPersister = (OuterJoinLoadable) currentFetch.getOwner().retrieveFetchSourcePersister();
final int propertyNumber = ownerPersister.getEntityMetamodel().getPropertyIndex( currentFetch.getOwnerPropertyName() );
final boolean isNullable = ownerPersister.isSubclassPropertyNullable( propertyNumber );
if ( currentFetch.getFetchStrategy().getStyle() == FetchStyle.JOIN ) {
joinType = isNullable ? JoinType.LEFT_OUTER_JOIN : JoinType.INNER_JOIN;
}

View File

@ -56,6 +56,7 @@ public class CollectionJoinableAssociationImpl extends AbstractJoinableAssociati
currentEntityReference,
collectionFetch,
withClause,
true,
hasRestriction,
enabledFilters
);

View File

@ -48,6 +48,7 @@ public class EntityJoinableAssociationImpl extends AbstractJoinableAssociationIm
EntityFetch entityFetch,
CollectionReference currentCollectionReference,
String withClause,
boolean isNullable,
boolean hasRestriction,
Map<String, Filter> enabledFilters) throws MappingException {
super(
@ -55,6 +56,7 @@ public class EntityJoinableAssociationImpl extends AbstractJoinableAssociationIm
entityFetch,
currentCollectionReference,
withClause,
isNullable,
hasRestriction,
enabledFilters
);

View File

@ -149,10 +149,14 @@ public class EntityLoadQueryBuilderImpl implements LoadQueryBuilder {
@Override
public void startingEntityFetch(EntityFetch entityFetch) {
final OuterJoinLoadable ownerPersister = (OuterJoinLoadable) entityFetch.getOwner().retrieveFetchSourcePersister();
final int propertyNumber = ownerPersister.getEntityMetamodel().getPropertyIndex( entityFetch.getOwnerPropertyName() );
final boolean isNullable = ownerPersister.isSubclassPropertyNullable( propertyNumber );
EntityJoinableAssociationImpl assoc = new EntityJoinableAssociationImpl(
entityFetch,
getCurrentCollectionReference(),
"", // getWithClause( entityFetch.getPropertyPath() )
isNullable,
false, // hasRestriction( entityFetch.getPropertyPath() )
loadQueryInfluencers.getEnabledFilters()
);

View File

@ -33,6 +33,7 @@ import org.hibernate.loader.plan.spi.FetchOwner;
import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.type.EntityType;
/**
* @author Steve Ebersole
@ -63,6 +64,7 @@ public class LoadPlanBuildingHelper {
LockMode.NONE, // todo : for now
fetchOwner,
attributeDefinition.getName(),
(EntityType) attributeDefinition.getType(),
fetchStrategy
);
}
@ -73,7 +75,7 @@ public class LoadPlanBuildingHelper {
LoadPlanBuildingContext loadPlanBuildingContext) {
return new CompositeFetch(
loadPlanBuildingContext.getSessionFactory(),
(AbstractFetchOwner) fetchOwner,
fetchOwner,
attributeDefinition.getName()
);
}

View File

@ -73,7 +73,12 @@ public class CompositeFetch extends AbstractSingularAttributeFetch {
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
LoadPlanBuildingContext loadPlanBuildingContext) {
return null; //To change body of implemented methods use File | Settings | File Templates.
return LoadPlanBuildingHelper.buildStandardEntityFetch(
this,
attributeDefinition,
fetchStrategy,
loadPlanBuildingContext
);
}
@Override

View File

@ -55,10 +55,14 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
LockMode lockMode,
FetchOwner owner,
String ownerProperty,
EntityType entityType,
FetchStrategy fetchStrategy) {
super( sessionFactory, lockMode, owner, ownerProperty, fetchStrategy );
this.associationType = (EntityType) owner.retrieveFetchSourcePersister().getPropertyType( ownerProperty );
this.associationType = entityType;
// (EntityType) owner.retrieveFetchSourcePersister().getPropertyType( ownerProperty );
//this.associationType =
// (EntityType) owner.retrieveFetchSourcePersister().getPropertyType( getPropertyPath().getFullPath() );
this.persister = sessionFactory.getEntityPersister( associationType.getAssociatedEntityName() );
}

View File

@ -134,7 +134,7 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe
@Override
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
EntityKey entityKey = context.getDictatedRootEntityKey();
EntityKey entityKey = getEntityKeyFromContext( context );
if ( entityKey != null ) {
context.getIdentifierResolutionContext( this ).registerEntityKey( entityKey );
return;
@ -147,6 +147,19 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe
}
}
private EntityKey getEntityKeyFromContext(ResultSetProcessingContext context) {
if ( context.getDictatedRootEntityKey() != null ) {
return context.getDictatedRootEntityKey();
}
else if ( context.getQueryParameters().getOptionalId() != null ) {
return context.getSession().generateEntityKey(
context.getQueryParameters().getOptionalId(),
getEntityPersister()
);
}
return null;
}
@Override
public void resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
final IdentifierResolutionContext identifierResolutionContext = context.getIdentifierResolutionContext( this );

View File

@ -37,6 +37,7 @@ import org.jboss.logging.MDC;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.EntityKey;
@ -44,6 +45,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper;
import org.hibernate.loader.plan.spi.AbstractSingularAttributeFetch;
import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CollectionReference;
import org.hibernate.loader.plan.spi.CollectionReturn;
@ -83,6 +85,8 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
private ArrayDeque<FetchOwner> fetchOwnerStack = new ArrayDeque<FetchOwner>();
private ArrayDeque<CollectionReference> collectionReferenceStack = new ArrayDeque<CollectionReference>();
//private AbstractIdentifierAttributeCollector currentIdentifierAttributeCollector = null;
protected AbstractLoadPlanBuilderStrategy(SessionFactoryImplementor sessionFactory) {
this.sessionFactory = sessionFactory;
}
@ -117,6 +121,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
MDC.remove( MDC_KEY );
fetchOwnerStack.clear();
collectionReferenceStack.clear();
// currentIdentifierAttributeCollector = null;
}
@Override
@ -413,10 +418,21 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
);
}
if ( FetchOwner.class.isInstance( associationFetch ) ) {
// If we are collecting fetches for the identifier then
// currentIdentifierAttributeCollector will be non-null.
// In that case, we do not want to continue walking the association, so
// don't push associationFetch to the stack.
//final boolean continueWalk = currentIdentifierAttributeCollector == null;
//if ( continueWalk && FetchOwner.class.isInstance( associationFetch) ) {
if ( FetchOwner.class.isInstance( associationFetch) ) {
pushToStack( (FetchOwner) associationFetch );
}
//if ( ! continueWalk ) {
// popFromStack();
//}
//return continueWalk;
return true;
}
@ -431,7 +447,21 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
}
private void pushToStack(FetchOwner fetchOwner) {
log.trace( "Pushing fetch owner to stack : " + fetchOwner );
// if ( fetchOwner instanceof AbstractIdentifierAttributeCollector ) {
// if ( currentIdentifierAttributeCollector != null ) {
// throw new WalkingException(
// String.format(
// "An AbstractIdentifierAttributeCollector is already being processed: %s",
// currentIdentifierAttributeCollector
// )
// );
// }
// currentIdentifierAttributeCollector = (AbstractIdentifierAttributeCollector) fetchOwner;
// log.trace( "Pushing AbstractIdentifierAttributeCollector fetch owner to stack : " + fetchOwner );
// }
// else {
log.trace( "Pushing fetch owner to stack : " + fetchOwner );
// }
mdcStack().push( fetchOwner.getPropertyPath() );
fetchOwnerStack.addFirst( fetchOwner );
}
@ -442,7 +472,30 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
private FetchOwner popFromStack() {
final FetchOwner last = fetchOwnerStack.removeFirst();
log.trace( "Popped fetch owner from stack : " + last );
// if ( last instanceof AbstractIdentifierAttributeCollector ) {
// if ( currentIdentifierAttributeCollector == null ) {
// throw new WalkingException(
// String.format(
// "Popped fetch owner was an AbstractIdentifierAttributeCollector [%s], but none in process (currentIdentifierAttributeCollector == null)",
// last
// )
// );
// }
// else if ( currentIdentifierAttributeCollector != last ) {
// throw new WalkingException(
// String.format(
// "Expected popped fetch owner to be [%s], but instead it was [%s])",
// currentIdentifierAttributeCollector,
// last
// )
// );
// }
// currentIdentifierAttributeCollector = null;
// log.trace( "Popped AbstractIdentifierAttributeCollector fetch owner from stack : " + last );
// }
// else {
log.trace( "Popped fetch owner from stack : " + last );
// }
mdcStack().pop();
if ( FetchStackAware.class.isInstance( last ) ) {
( (FetchStackAware) last ).poppedFromStack();
@ -487,15 +540,13 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
implements FetchOwner, EntityReference, FetchStackAware {
protected final EntityReference entityReference;
private final PropertyPath propertyPath;
protected final List<EntityFetch> identifierFetches = new ArrayList<EntityFetch>();
protected final Map<EntityFetch,HydratedCompoundValueHandler> fetchToHydratedStateExtractorMap
= new HashMap<EntityFetch, HydratedCompoundValueHandler>();
protected final List<AbstractSingularAttributeFetch> identifierFetches = new ArrayList<AbstractSingularAttributeFetch>();
protected final Map<AbstractSingularAttributeFetch,HydratedCompoundValueHandler> fetchToHydratedStateExtractorMap
= new HashMap<AbstractSingularAttributeFetch, HydratedCompoundValueHandler>();
public AbstractIdentifierAttributeCollector(EntityReference entityReference) {
this.entityReference = entityReference;
this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath().append( "<id>" );
}
@Override
@ -533,7 +584,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
LoadPlanBuildingContext loadPlanBuildingContext) {
// we have a key-many-to-one
//
// IMPL NOTE: we pass ourselves as the FetchOwner which will route the fetch back throw our #addFetch
// IMPL NOTE: we pass ourselves as the FetchOwner which will route the fetch back through our #addFetch
// impl. We collect them there and later build the IdentifierDescription
final EntityFetch fetch = LoadPlanBuildingHelper.buildStandardEntityFetch(
this,
@ -551,7 +602,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
CompositionDefinition attributeDefinition, LoadPlanBuildingContext loadPlanBuildingContext) {
// nested composition. Unusual, but not disallowed.
//
// IMPL NOTE: we pass ourselves as the FetchOwner which will route the fetch back throw our #addFetch
// IMPL NOTE: we pass ourselves as the FetchOwner which will route the fetch back through our #addFetch
// impl. We collect them there and later build the IdentifierDescription
return LoadPlanBuildingHelper.buildStandardCompositeFetch(
this,
@ -570,7 +621,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
@Override
public void addFetch(Fetch fetch) {
identifierFetches.add( (EntityFetch) fetch );
identifierFetches.add( (AbstractSingularAttributeFetch) fetch );
}
@Override
@ -588,11 +639,6 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
return ( (FetchOwner) entityReference ).retrieveFetchSourcePersister();
}
@Override
public PropertyPath getPropertyPath() {
return propertyPath;
}
@Override
public void injectIdentifierDescription(IdentifierDescription identifierDescription) {
throw new WalkingException(
@ -602,43 +648,59 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
}
protected static class EncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector {
private final PropertyPath propertyPath;
public EncapsulatedIdentifierAttributeCollector(EntityReference entityReference) {
super( entityReference );
this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath();
}
@Override
protected IdentifierDescription buildIdentifierDescription() {
return new IdentifierDescriptionImpl(
entityReference,
identifierFetches.toArray( new EntityFetch[ identifierFetches.size() ] ),
identifierFetches.toArray( new AbstractSingularAttributeFetch[ identifierFetches.size() ] ),
null
);
}
@Override
public PropertyPath getPropertyPath() {
return propertyPath;
}
}
protected static class NonEncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector {
private final PropertyPath propertyPath;
public NonEncapsulatedIdentifierAttributeCollector(EntityReference entityReference) {
super( entityReference );
this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath().append( "<id>" );
}
@Override
protected IdentifierDescription buildIdentifierDescription() {
return new IdentifierDescriptionImpl(
entityReference,
identifierFetches.toArray( new EntityFetch[ identifierFetches.size() ] ),
identifierFetches.toArray( new AbstractSingularAttributeFetch[ identifierFetches.size() ] ),
fetchToHydratedStateExtractorMap
);
}
@Override
public PropertyPath getPropertyPath() {
return propertyPath;
}
}
private static class IdentifierDescriptionImpl implements IdentifierDescription {
private final EntityReference entityReference;
private final EntityFetch[] identifierFetches;
private final Map<EntityFetch,HydratedCompoundValueHandler> fetchToHydratedStateExtractorMap;
private final AbstractSingularAttributeFetch[] identifierFetches;
private final Map<AbstractSingularAttributeFetch,HydratedCompoundValueHandler> fetchToHydratedStateExtractorMap;
private IdentifierDescriptionImpl(
EntityReference entityReference, EntityFetch[] identifierFetches,
Map<EntityFetch, HydratedCompoundValueHandler> fetchToHydratedStateExtractorMap) {
EntityReference entityReference,
AbstractSingularAttributeFetch[] identifierFetches,
Map<AbstractSingularAttributeFetch, HydratedCompoundValueHandler> fetchToHydratedStateExtractorMap) {
this.entityReference = entityReference;
this.identifierFetches = identifierFetches;
this.fetchToHydratedStateExtractorMap = fetchToHydratedStateExtractorMap;
@ -656,24 +718,28 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
final Object ownerIdentifierHydratedState = ownerIdentifierResolutionContext.getHydratedForm();
if ( ownerIdentifierHydratedState != null ) {
for ( EntityFetch fetch : identifierFetches ) {
final IdentifierResolutionContext identifierResolutionContext =
context.getIdentifierResolutionContext( fetch );
// if the identifier was already hydrated, nothing to do
if ( identifierResolutionContext.getHydratedForm() != null ) {
continue;
}
for ( AbstractSingularAttributeFetch fetch : identifierFetches ) {
if ( fetch instanceof EntityFetch ) {
final IdentifierResolutionContext identifierResolutionContext =
context.getIdentifierResolutionContext( (EntityFetch) fetch );
// if the identifier was already hydrated, nothing to do
if ( identifierResolutionContext.getHydratedForm() != null ) {
continue;
}
// try to extract the sub-hydrated value from the owners tuple array
if ( fetchToHydratedStateExtractorMap != null && ownerIdentifierHydratedState != null ) {
Serializable extracted = (Serializable) fetchToHydratedStateExtractorMap.get( fetch )
.extract( ownerIdentifierHydratedState );
identifierResolutionContext.registerHydratedForm( extracted );
continue;
}
// try to extract the sub-hydrated value from the owners tuple array
if ( fetchToHydratedStateExtractorMap != null && ownerIdentifierHydratedState != null ) {
Serializable extracted = (Serializable) fetchToHydratedStateExtractorMap.get( fetch )
.extract( ownerIdentifierHydratedState );
identifierResolutionContext.registerHydratedForm( extracted );
continue;
// if we can't, then read from result set
fetch.hydrate( resultSet, context );
}
else {
throw new NotYetImplementedException( "Cannot hydrate identifier Fetch that is not an EntityFetch" );
}
// if we can't, then read from result set
fetch.hydrate( resultSet, context );
}
return;
}
@ -689,15 +755,8 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
@Override
public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
for ( EntityFetch fetch : identifierFetches ) {
final IdentifierResolutionContext identifierResolutionContext =
context.getIdentifierResolutionContext( fetch );
if ( identifierResolutionContext.getEntityKey() != null ) {
continue;
}
EntityKey fetchKey = fetch.resolveInIdentifier( resultSet, context );
identifierResolutionContext.registerEntityKey( fetchKey );
for ( AbstractSingularAttributeFetch fetch : identifierFetches ) {
resolveIdentifierFetch( resultSet, context, fetch );
}
final IdentifierResolutionContext ownerIdentifierResolutionContext =
@ -710,6 +769,28 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
}
}
private static void resolveIdentifierFetch(
ResultSet resultSet,
ResultSetProcessingContext context,
AbstractSingularAttributeFetch fetch) throws SQLException {
if ( fetch instanceof EntityFetch ) {
EntityFetch entityFetch = (EntityFetch) fetch;
final IdentifierResolutionContext identifierResolutionContext =
context.getIdentifierResolutionContext( entityFetch );
if ( identifierResolutionContext.getEntityKey() != null ) {
return;
}
EntityKey fetchKey = entityFetch.resolveInIdentifier( resultSet, context );
identifierResolutionContext.registerEntityKey( fetchKey );
}
else if ( fetch instanceof CompositeFetch ) {
for ( Fetch subFetch : fetch.getFetches() ) {
resolveIdentifierFetch( resultSet, context, (AbstractSingularAttributeFetch) subFetch );
}
}
}
public static class MDCStack {
private ArrayDeque<PropertyPath> pathStack = new ArrayDeque<PropertyPath>();

View File

@ -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.internal.EntityIdentifierDefinitionHelper;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeSource;
import org.hibernate.persister.walking.spi.CompositionDefinition;
@ -5122,129 +5123,20 @@ public abstract class AbstractEntityPersister
final Type idType = getIdentifierType();
if ( !idType.isComponentType() ) {
entityIdentifierDefinition = buildSimpleEncapsulatedIdentifierDefinition();
entityIdentifierDefinition =
EntityIdentifierDefinitionHelper.buildSimpleEncapsulatedIdentifierDefinition( this );
return;
}
final CompositeType cidType = (CompositeType) idType;
if ( !cidType.isEmbedded() ) {
entityIdentifierDefinition = buildEncapsulatedCompositeIdentifierDefinition();
entityIdentifierDefinition =
EntityIdentifierDefinitionHelper.buildEncapsulatedCompositeIdentifierDefinition( this );
return;
}
entityIdentifierDefinition = new NonEncapsulatedEntityIdentifierDefinition() {
@Override
public Iterable<AttributeDefinition> getAttributes() {
// todo : implement
throw new NotYetImplementedException();
}
@Override
public Class getSeparateIdentifierMappingClass() {
// todo : implement
throw new NotYetImplementedException();
}
@Override
public boolean isEncapsulated() {
return false;
}
@Override
public EntityDefinition getEntityDefinition() {
return AbstractEntityPersister.this;
}
};
}
private EntityIdentifierDefinition buildSimpleEncapsulatedIdentifierDefinition() {
final AttributeDefinition simpleIdentifierAttributeAdapter = new AttributeDefinition() {
@Override
public String getName() {
return entityMetamodel.getIdentifierProperty().getName();
}
@Override
public Type getType() {
return entityMetamodel.getIdentifierProperty().getType();
}
@Override
public AttributeSource getSource() {
return AbstractEntityPersister.this;
}
@Override
public String toString() {
return "<identifier-property:" + getName() + ">";
}
};
return new EncapsulatedEntityIdentifierDefinition() {
@Override
public AttributeDefinition getAttributeDefinition() {
return simpleIdentifierAttributeAdapter;
}
@Override
public boolean isEncapsulated() {
return true;
}
@Override
public EntityDefinition getEntityDefinition() {
return AbstractEntityPersister.this;
}
};
}
private EntityIdentifierDefinition buildEncapsulatedCompositeIdentifierDefinition() {
final CompositionDefinition compositeIdentifierAttributeAdapter = new CompositionDefinition() {
@Override
public String getName() {
return entityMetamodel.getIdentifierProperty().getName();
}
@Override
public Type getType() {
return entityMetamodel.getIdentifierProperty().getType();
}
@Override
public AttributeSource getSource() {
return AbstractEntityPersister.this;
}
@Override
public String toString() {
return "<identifier-property:" + getName() + ">";
}
@Override
public Iterable<AttributeDefinition> getAttributes() {
ComponentType componentType = (ComponentType) getType();
//for ( Type type : componentType.getSubtypes() ) {
throw new NotYetImplementedException( "cannot create sub-attribute definitions for a ComponentType yet." );
//}
}
};
return new EncapsulatedEntityIdentifierDefinition() {
@Override
public AttributeDefinition getAttributeDefinition() {
return compositeIdentifierAttributeAdapter;
}
@Override
public boolean isEncapsulated() {
return true;
}
@Override
public EntityDefinition getEntityDefinition() {
return AbstractEntityPersister.this;
}
};
entityIdentifierDefinition =
EntityIdentifierDefinitionHelper.buildNonEncapsulatedCompositeIdentifierDefinition( this );
}
private void collectAttributeDefinitions() {

View File

@ -0,0 +1,313 @@
/*
* 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.internal;
import java.util.Iterator;
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.CascadeStyles;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.loader.PropertyPath;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.persister.spi.HydratedCompoundValueHandler;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
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.CollectionDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.persister.walking.spi.EncapsulatedEntityIdentifierDefinition;
import org.hibernate.persister.walking.spi.EntityDefinition;
import org.hibernate.persister.walking.spi.EntityIdentifierDefinition;
import org.hibernate.persister.walking.spi.NonEncapsulatedEntityIdentifierDefinition;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.type.AssociationType;
import org.hibernate.type.ComponentType;
import org.hibernate.type.Type;
import static org.hibernate.engine.internal.JoinHelper.getLHSColumnNames;
import static org.hibernate.engine.internal.JoinHelper.getLHSTableName;
/**
* @author Gail Badner
*/
public class EntityIdentifierDefinitionHelper {
public static EntityIdentifierDefinition buildSimpleEncapsulatedIdentifierDefinition(final AbstractEntityPersister entityPersister) {
return new EncapsulatedEntityIdentifierDefinition() {
@Override
public AttributeDefinition getAttributeDefinition() {
return new AttributeDefinitionAdapter( entityPersister);
}
@Override
public boolean isEncapsulated() {
return true;
}
@Override
public EntityDefinition getEntityDefinition() {
return entityPersister;
}
};
}
public static EntityIdentifierDefinition buildEncapsulatedCompositeIdentifierDefinition(final AbstractEntityPersister entityPersister) {
return new EncapsulatedEntityIdentifierDefinition() {
@Override
public AttributeDefinition getAttributeDefinition() {
return new CompositeAttributeDefinitionAdapter( entityPersister );
}
@Override
public boolean isEncapsulated() {
return true;
}
@Override
public EntityDefinition getEntityDefinition() {
return entityPersister;
}
};
}
public static EntityIdentifierDefinition buildNonEncapsulatedCompositeIdentifierDefinition(final AbstractEntityPersister entityPersister) {
return new NonEncapsulatedEntityIdentifierDefinition() {
@Override
public Iterable<AttributeDefinition> getAttributes() {
return new CompositeAttributeDefinitionAdapter( entityPersister ).getAttributes();
}
@Override
public Class getSeparateIdentifierMappingClass() {
return entityPersister.getEntityMetamodel().getIdentifierProperty().getType().getReturnedClass();
}
@Override
public boolean isEncapsulated() {
return false;
}
@Override
public EntityDefinition getEntityDefinition() {
return entityPersister;
}
};
}
private static class AttributeDefinitionAdapter implements AttributeDefinition {
private final AbstractEntityPersister entityPersister;
AttributeDefinitionAdapter(AbstractEntityPersister entityPersister) {
this.entityPersister = entityPersister;
}
@Override
public String getName() {
return entityPersister.getEntityMetamodel().getIdentifierProperty().getName();
}
@Override
public Type getType() {
return entityPersister.getEntityMetamodel().getIdentifierProperty().getType();
}
@Override
public AttributeSource getSource() {
return entityPersister;
}
@Override
public String toString() {
return "<identifier-property:" + getName() + ">";
}
protected AbstractEntityPersister getEntityPersister() {
return entityPersister;
}
}
private static class CompositeAttributeDefinitionAdapter extends AttributeDefinitionAdapter implements CompositionDefinition {
CompositeAttributeDefinitionAdapter(AbstractEntityPersister entityPersister) {
super( entityPersister );
}
@Override
public Iterable<AttributeDefinition> getAttributes() {
return new Iterable<AttributeDefinition>() {
@Override
public Iterator<AttributeDefinition> iterator() {
final ComponentType componentType = (ComponentType) getType();
return new Iterator<AttributeDefinition>() {
private final int numberOfAttributes = componentType.getSubtypes().length;
private int currentSubAttributeNumber = 0;
private int currentColumnPosition = 0;
@Override
public boolean hasNext() {
return currentSubAttributeNumber < numberOfAttributes;
}
@Override
public AttributeDefinition next() {
final int subAttributeNumber = currentSubAttributeNumber;
currentSubAttributeNumber++;
final AttributeSource source = getSource();
final String name = componentType.getPropertyNames()[subAttributeNumber];
final Type type = componentType.getSubtypes()[subAttributeNumber];
final int columnPosition = currentColumnPosition;
currentColumnPosition += type.getColumnSpan( getEntityPersister().getFactory() );
if ( type.isAssociationType() ) {
final AssociationType aType = (AssociationType) type;
final Joinable joinable = aType.getAssociatedJoinable( getEntityPersister().getFactory() );
return new AssociationAttributeDefinition() {
@Override
public AssociationKey getAssociationKey() {
/* TODO: is this always correct? */
//return new AssociationKey(
// joinable.getTableName(),
// JoinHelper.getRHSColumnNames( aType, getEntityPersister().getFactory() )
//);
return new AssociationKey(
getEntityPersister().getTableName(),
getLHSColumnNames(
aType,
-1,
columnPosition,
getEntityPersister(),
getEntityPersister().getFactory()
)
);
}
@Override
public boolean isCollection() {
return false;
}
@Override
public EntityDefinition toEntityDefinition() {
return (EntityPersister) joinable;
}
@Override
public CollectionDefinition toCollectionDefinition() {
throw new WalkingException( "A collection cannot be mapped to a composite ID sub-attribute." );
}
@Override
public FetchStrategy determineFetchPlan(LoadQueryInfluencers loadQueryInfluencers, PropertyPath propertyPath) {
return new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN );
}
@Override
public CascadeStyle determineCascadeStyle() {
return CascadeStyles.NONE;
}
@Override
public HydratedCompoundValueHandler getHydratedCompoundValueExtractor() {
return null;
}
@Override
public String getName() {
return name;
}
@Override
public Type getType() {
return type;
}
@Override
public AttributeSource getSource() {
return source;
}
};
}
else if ( type.isComponentType() ) {
return new CompositionDefinition() {
@Override
public String getName() {
return name;
}
@Override
public Type getType() {
return type;
}
@Override
public AttributeSource getSource() {
return source;
}
@Override
public Iterable<AttributeDefinition> getAttributes() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
};
}
else {
return new AttributeDefinition() {
@Override
public String getName() {
return name;
}
@Override
public Type getType() {
return type;
}
@Override
public AttributeSource getSource() {
return source;
}
};
}
}
@Override
public void remove() {
throw new UnsupportedOperationException( "Remove operation not supported here" );
}
};
}
};
}
}
}

View File

@ -42,7 +42,7 @@ import org.hibernate.type.AssociationType;
/**
* @author Steve Ebersole
*/
public class Helper {
public class FetchStrategyHelper {
/**
* Determine the fetch-style (if one) explicitly set for this association via fetch profiles.
* <p/>

View File

@ -28,12 +28,21 @@ import java.util.Set;
import org.jboss.logging.Logger;
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.spi.HydratedCompoundValueHandler;
import org.hibernate.type.Type;
import static org.hibernate.engine.internal.JoinHelper.getRHSColumnNames;
/**
* 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
@ -116,7 +125,7 @@ public class MetadataDrivenModelGraphVisitor {
final boolean continueWalk;
if ( attributeDefinition.getType().isAssociationType() ) {
continueWalk =
! isDuplicateAssociation( ( (AssociationAttributeDefinition) attributeDefinition ).getAssociationKey() ) &&
! isDuplicateAssociationKey( ( (AssociationAttributeDefinition) attributeDefinition ).getAssociationKey() ) &&
strategy.startingAttribute( attributeDefinition );
}
else {
@ -143,6 +152,11 @@ public class MetadataDrivenModelGraphVisitor {
private void visitAssociation(AssociationAttributeDefinition attribute) {
// todo : do "too deep" checks; but see note about adding depth to PropertyPath
if ( !addAssociationKey( attribute.getAssociationKey() ) ) {
log.debug( "Property path deemed to be circular : " + currentPropertyPath.getFullPath() );
return;
}
if ( attribute.isCollection() ) {
visitCollectionDefinition( attribute.toCollectionDefinition() );
}
@ -212,15 +226,18 @@ public class MetadataDrivenModelGraphVisitor {
private final Set<AssociationKey> visitedAssociationKeys = new HashSet<AssociationKey>();
protected boolean isDuplicateAssociation(AssociationKey associationKey) {
boolean isDuplicate = !visitedAssociationKeys.add( associationKey );
if ( isDuplicate ) {
log.debug( "Property path deemed to be circular : " + currentPropertyPath.getFullPath() );
return true;
}
else {
return false;
}
/**
* Add association key.
* @param associationKey - the association key.
* @return true, if the association key was added;
* false, otherwise (indicating the association key was already visited).
*/
protected boolean addAssociationKey(AssociationKey associationKey) {
return visitedAssociationKeys.add( associationKey );
}
protected boolean isDuplicateAssociationKey(AssociationKey associationKey) {
return visitedAssociationKeys.contains( associationKey );
}
}

View File

@ -101,13 +101,13 @@ public abstract class AbstractCompositionDefinition extends AbstractNonIdentifie
getLHSTableName(
aType,
attributeNumber(),
(OuterJoinLoadable) joinable
(OuterJoinLoadable) locateOwningPersister()
),
getLHSColumnNames(
aType,
attributeNumber(),
columnPosition,
(OuterJoinLoadable) joinable,
(OuterJoinLoadable) locateOwningPersister(),
sessionFactory()
)
);

View File

@ -23,8 +23,6 @@
*/
package org.hibernate.tuple.component;
import java.io.Serializable;
import org.hibernate.FetchMode;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle;
@ -37,7 +35,7 @@ import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.persister.spi.HydratedCompoundValueHandler;
import org.hibernate.persister.walking.internal.Helper;
import org.hibernate.persister.walking.internal.FetchStrategyHelper;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AssociationKey;
import org.hibernate.persister.walking.spi.CollectionDefinition;
@ -132,7 +130,7 @@ public class CompositeBasedAssociationAttribute
EntityPersister owningPersister,
PropertyPath propertyPath,
int ownerAttributeNumber) {
return Helper.determineFetchStyleByProfile(
return FetchStrategyHelper.determineFetchStyleByProfile(
loadQueryInfluencers,
owningPersister,
propertyPath,
@ -141,11 +139,11 @@ public class CompositeBasedAssociationAttribute
}
protected FetchStyle determineFetchStyleByMetadata(FetchMode fetchMode, AssociationType type) {
return Helper.determineFetchStyleByMetadata( fetchMode, type, sessionFactory() );
return FetchStrategyHelper.determineFetchStyleByMetadata( fetchMode, type, sessionFactory() );
}
private FetchTiming determineFetchTiming(FetchStyle style) {
return Helper.determineFetchTiming( style, getType(), sessionFactory() );
return FetchStrategyHelper.determineFetchTiming( style, getType(), sessionFactory() );
}
private EntityPersister locateOwningPersister() {

View File

@ -34,7 +34,7 @@ import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.persister.spi.HydratedCompoundValueHandler;
import org.hibernate.persister.walking.internal.Helper;
import org.hibernate.persister.walking.internal.FetchStrategyHelper;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AssociationKey;
import org.hibernate.persister.walking.spi.CollectionDefinition;
@ -129,22 +129,22 @@ public class EntityBasedAssociationAttribute
public FetchStrategy determineFetchPlan(LoadQueryInfluencers loadQueryInfluencers, PropertyPath propertyPath) {
final EntityPersister owningPersister = getSource().getEntityPersister();
FetchStyle style = Helper.determineFetchStyleByProfile(
FetchStyle style = FetchStrategyHelper.determineFetchStyleByProfile(
loadQueryInfluencers,
owningPersister,
propertyPath,
attributeNumber()
);
if ( style == null ) {
style = Helper.determineFetchStyleByMetadata(
((OuterJoinLoadable) getSource().getEntityPersister()).getFetchMode( attributeNumber() ),
style = FetchStrategyHelper.determineFetchStyleByMetadata(
( (OuterJoinLoadable) getSource().getEntityPersister() ).getFetchMode( attributeNumber() ),
getType(),
sessionFactory()
);
}
return new FetchStrategy(
Helper.determineFetchTiming( style, getType(), sessionFactory() ),
FetchStrategyHelper.determineFetchTiming( style, getType(), sessionFactory() ),
style
);
}

View File

@ -0,0 +1,434 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import org.junit.Test;
import org.hibernate.LockOptions;
import org.hibernate.Session;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.jdbc.Work;
import org.hibernate.loader.internal.EntityLoadQueryBuilderImpl;
import org.hibernate.loader.internal.LoadQueryAliasResolutionContextImpl;
import org.hibernate.loader.internal.ResultSetProcessorImpl;
import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.build.LoadPlanBuilder;
import org.hibernate.loader.spi.LoadQueryAliasResolutionContext;
import org.hibernate.loader.spi.NamedParameterContext;
import org.hibernate.loader.spi.NoOpLoadPlanAdvisor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.testing.junit4.ExtraAssertions;
import org.hibernate.type.Type;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
/**
* @author Gail Badner
*/
public class EncapsulatedCompositeIdResultSetProcessorTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Parent.class, CardField.class, Card.class };
}
@Test
public void testSimpleCompositeId() throws Exception {
// create some test data
Session session = openSession();
session.beginTransaction();
Parent parent = new Parent();
parent.id = new ParentPK();
parent.id.firstName = "Joe";
parent.id.lastName = "Blow";
session.save( parent );
session.getTransaction().commit();
session.close();
session = openSession();
session.beginTransaction();
Parent parentGotten = (Parent) session.get( Parent.class, parent.id );
assertEquals( parent, parentGotten );
session.getTransaction().commit();
session.close();
final List results = getResults(
sessionFactory().getEntityPersister( Parent.class.getName() ),
new Callback() {
@Override
public void bind(PreparedStatement ps) throws SQLException {
ps.setString( 1, "Joe" );
ps.setString( 2, "Blow" );
}
@Override
public QueryParameters getQueryParameters() {
return new QueryParameters();
}
}
);
assertEquals( 1, results.size() );
Object result = results.get( 0 );
assertNotNull( result );
Parent parentWork = ExtraAssertions.assertTyping( Parent.class, result );
assertEquals( parent, parentWork );
// clean up test data
session = openSession();
session.beginTransaction();
session.createQuery( "delete Parent" ).executeUpdate();
session.getTransaction().commit();
session.close();
}
@Test
public void testCompositeIdWithKeyManyToOne() throws Exception {
final String cardId = "ace-of-spades";
// create some test data
Session session = openSession();
session.beginTransaction();
Card card = new Card( cardId );
final CardField cardField = new CardField( card, 1 );
session.persist( card );
session.persist( cardField );
session.getTransaction().commit();
session.close();
session = openSession();
session.beginTransaction();
Card cardProxy = (Card) session.load( Card.class, cardId );
final CardFieldPK cardFieldPK = new CardFieldPK( cardProxy, 1 );
CardField cardFieldGotten = (CardField) session.get( CardField.class, cardFieldPK );
//assertEquals( card, cardGotten );
session.getTransaction().commit();
session.close();
final EntityPersister entityPersister = sessionFactory().getEntityPersister( CardField.class.getName() );
final List results = getResults(
entityPersister,
new Callback() {
@Override
public void bind(PreparedStatement ps) throws SQLException {
ps.setString( 1, cardField.primaryKey.card.id );
ps.setInt( 2, cardField.primaryKey.fieldNumber );
}
@Override
public QueryParameters getQueryParameters() {
QueryParameters qp = new QueryParameters();
qp.setPositionalParameterTypes( new Type[] { entityPersister.getIdentifierType() } );
qp.setPositionalParameterValues( new Object[] { cardFieldPK } );
qp.setOptionalObject( null );
qp.setOptionalEntityName( entityPersister.getEntityName() );
qp.setOptionalId( cardFieldPK );
qp.setLockOptions( LockOptions.NONE );
return qp;
}
}
);
assertEquals( 1, results.size() );
Object result = results.get( 0 );
assertNotNull( result );
CardField cardFieldWork = ExtraAssertions.assertTyping( CardField.class, result );
assertEquals( cardFieldGotten, cardFieldWork );
// clean up test data
session = openSession();
session.beginTransaction();
session.createQuery( "delete CardField" ).executeUpdate();
session.createQuery( "delete Card" ).executeUpdate();
session.getTransaction().commit();
session.close();
}
private List getResults(final EntityPersister entityPersister, final Callback callback) {
final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy(
sessionFactory(),
LoadQueryInfluencers.NONE
);
final LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister );
final LoadQueryAliasResolutionContext aliasResolutionContext =
new LoadQueryAliasResolutionContextImpl(
sessionFactory(),
0,
Collections.singletonMap( plan.getReturns().get( 0 ), new String[] { "abc" } )
);
final EntityLoadQueryBuilderImpl queryBuilder = new EntityLoadQueryBuilderImpl(
LoadQueryInfluencers.NONE,
plan
);
final String sql = queryBuilder.generateSql( 1, sessionFactory(), aliasResolutionContext );
final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan );
final List results = new ArrayList();
final Session workSession = openSession();
workSession.beginTransaction();
workSession.doWork(
new Work() {
@Override
public void execute(Connection connection) throws SQLException {
PreparedStatement ps = connection.prepareStatement( sql );
callback.bind( ps );
ResultSet resultSet = ps.executeQuery();
//callback.beforeExtractResults( workSession );
results.addAll(
resultSetProcessor.extractResults(
NoOpLoadPlanAdvisor.INSTANCE,
resultSet,
(SessionImplementor) workSession,
callback.getQueryParameters(),
new NamedParameterContext() {
@Override
public int[] getNamedParameterLocations(String name) {
return new int[0];
}
},
aliasResolutionContext,
true,
false,
null,
null
)
);
resultSet.close();
ps.close();
}
}
);
workSession.getTransaction().commit();
workSession.close();
return results;
}
private interface Callback {
void bind(PreparedStatement ps) throws SQLException;
QueryParameters getQueryParameters ();
}
@Entity ( name = "Parent" )
public static class Parent {
@EmbeddedId
public ParentPK id;
public boolean equals(Object o) {
if ( this == o ) return true;
if ( !( o instanceof Parent ) ) return false;
final Parent parent = (Parent) o;
if ( !id.equals( parent.id ) ) return false;
return true;
}
public int hashCode() {
return id.hashCode();
}
}
@Embeddable
public static class ParentPK implements Serializable {
private String firstName;
private String lastName;
public boolean equals(Object o) {
if ( this == o ) return true;
if ( !( o instanceof ParentPK ) ) return false;
final ParentPK parentPk = (ParentPK) o;
if ( !firstName.equals( parentPk.firstName ) ) return false;
if ( !lastName.equals( parentPk.lastName ) ) return false;
return true;
}
public int hashCode() {
int result;
result = firstName.hashCode();
result = 29 * result + lastName.hashCode();
return result;
}
}
@Entity ( name = "CardField" )
public static class CardField implements Serializable {
@EmbeddedId
private CardFieldPK primaryKey;
CardField(Card card, int fieldNumber) {
this.primaryKey = new CardFieldPK(card, fieldNumber);
}
CardField() {
}
public CardFieldPK getPrimaryKey() {
return primaryKey;
}
public void setPrimaryKey(CardFieldPK primaryKey) {
this.primaryKey = primaryKey;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
CardField cardField = (CardField) o;
if ( primaryKey != null ? !primaryKey.equals( cardField.primaryKey ) : cardField.primaryKey != null ) {
return false;
}
return true;
}
@Override
public int hashCode() {
return primaryKey != null ? primaryKey.hashCode() : 0;
}
}
@Embeddable
public static class CardFieldPK implements Serializable {
@ManyToOne(optional = false)
private Card card;
private int fieldNumber;
public CardFieldPK(Card card, int fieldNumber) {
this.card = card;
this.fieldNumber = fieldNumber;
}
CardFieldPK() {
}
public Card getCard() {
return card;
}
public void setCard(Card card) {
this.card = card;
}
public int getFieldNumber() {
return fieldNumber;
}
public void setFieldNumber(int fieldNumber) {
this.fieldNumber = fieldNumber;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
CardFieldPK that = (CardFieldPK) o;
if ( fieldNumber != that.fieldNumber ) {
return false;
}
if ( card != null ? !card.equals( that.card ) : that.card != null ) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = card != null ? card.hashCode() : 0;
result = 31 * result + fieldNumber;
return result;
}
}
@Entity ( name = "Card" )
public static class Card implements Serializable {
@Id
private String id;
public Card(String id) {
this();
this.id = id;
}
Card() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
}

View File

@ -29,11 +29,13 @@ import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.jpa.graph.spi.AttributeNodeImplementor;
import org.hibernate.jpa.internal.metamodel.Helper;
import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CompositeFetch;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.Fetch;
import org.hibernate.loader.plan.spi.FetchOwner;
import org.hibernate.type.EntityType;
/**
* @author Steve Ebersole
@ -54,11 +56,16 @@ public class AdviceHelper {
);
}
else {
EntityType entityType = (EntityType) Helper.resolveType(
(SessionFactoryImplementor) attributeNode.entityManagerFactory().getSessionFactory(),
attributeNode.getAttribute()
);
return new EntityFetch(
(SessionFactoryImplementor) attributeNode.entityManagerFactory().getSessionFactory(),
LockMode.NONE,
fetchOwner,
attributeNode.getAttributeName(),
entityType,
new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.SELECT )
);
}