HHH-7841 - Redesign Loader
This commit is contained in:
parent
dfe33ffa1a
commit
4620ff4b4f
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ public class CollectionJoinableAssociationImpl extends AbstractJoinableAssociati
|
|||
currentEntityReference,
|
||||
collectionFetch,
|
||||
withClause,
|
||||
true,
|
||||
hasRestriction,
|
||||
enabledFilters
|
||||
);
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() );
|
||||
}
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 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) {
|
||||
// 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();
|
||||
// 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,14 +718,14 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
|||
final Object ownerIdentifierHydratedState = ownerIdentifierResolutionContext.getHydratedForm();
|
||||
|
||||
if ( ownerIdentifierHydratedState != null ) {
|
||||
for ( EntityFetch fetch : identifierFetches ) {
|
||||
for ( AbstractSingularAttributeFetch fetch : identifierFetches ) {
|
||||
if ( fetch instanceof EntityFetch ) {
|
||||
final IdentifierResolutionContext identifierResolutionContext =
|
||||
context.getIdentifierResolutionContext( fetch );
|
||||
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 )
|
||||
|
@ -675,6 +737,10 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
|||
// 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" );
|
||||
}
|
||||
}
|
||||
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>();
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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" );
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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/>
|
|
@ -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 );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
)
|
||||
);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,14 +129,14 @@ 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(
|
||||
style = FetchStrategyHelper.determineFetchStyleByMetadata(
|
||||
( (OuterJoinLoadable) getSource().getEntityPersister() ).getFetchMode( attributeNumber() ),
|
||||
getType(),
|
||||
sessionFactory()
|
||||
|
@ -144,7 +144,7 @@ public class EntityBasedAssociationAttribute
|
|||
}
|
||||
|
||||
return new FetchStrategy(
|
||||
Helper.determineFetchTiming( style, getType(), sessionFactory() ),
|
||||
FetchStrategyHelper.determineFetchTiming( style, getType(), sessionFactory() ),
|
||||
style
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 )
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue