All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.persister.walking.spi.MetamodelGraphWalker Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha1
Show newest version
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.persister.walking.spi;

import java.util.HashSet;
import java.util.Set;

import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.PropertyPath;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.Type;

import org.jboss.logging.Logger;

/**
 * Implements metamodel graph walking.  In layman terms, we are walking the graph of the users domain model as
 * defined/understood by mapped associations.
 * 

* Initially grew as a part of the re-implementation of the legacy JoinWalker functionality to instead build LoadPlans. * But this is really quite simple walking. Interesting events are handled by calling out to * implementations of {@link AssociationVisitationStrategy} which really provide the real functionality of what we do * as we walk. *

* The visitor will walk the entire metamodel graph (the parts reachable from the given root)!!! It is up to the * provided AssociationVisitationStrategy to tell it when to stop. The walker provides the walking; the strategy * provides the semantics of what happens at certain points. Its really very similar to parsers and how parsing is * generally split between syntax and semantics. Walker walks the syntax (associations, identifiers, etc) and when it * calls out to the strategy the strategy then decides the semantics (literally, the meaning). *

* The visitor will, however, stop if it sees a "duplicate" AssociationKey. In such a case, the walker would call * {@link AssociationVisitationStrategy#foundCircularAssociation} and stop walking any further down that graph any * further. * * @author Steve Ebersole */ public class MetamodelGraphWalker { private static final Logger log = Logger.getLogger( MetamodelGraphWalker.class ); /** * Entry point into walking the model graph of an entity according to its defined metamodel. * * @param strategy The semantics strategy * @param persister The persister describing the entity to start walking from */ public static void visitEntity(AssociationVisitationStrategy strategy, EntityPersister persister) { strategy.start(); try { new MetamodelGraphWalker( strategy, persister.getFactory() ) .visitEntityDefinition( persister ); } finally { strategy.finish(); } } /** * Entry point into walking the model graph of a collection according to its defined metamodel. * * @param strategy The semantics strategy * @param persister The persister describing the collection to start walking from */ public static void visitCollection(AssociationVisitationStrategy strategy, CollectionPersister persister) { strategy.start(); try { new MetamodelGraphWalker( strategy, persister.getFactory() ) .visitCollectionDefinition( persister ); } finally { strategy.finish(); } } private final AssociationVisitationStrategy strategy; private final SessionFactoryImplementor factory; // todo : add a getDepth() method to PropertyPath private PropertyPath currentPropertyPath = new PropertyPath(); public MetamodelGraphWalker(AssociationVisitationStrategy strategy, SessionFactoryImplementor factory) { this.strategy = strategy; this.factory = factory; } private void visitEntityDefinition(EntityDefinition entityDefinition) { strategy.startingEntity( entityDefinition ); try { AbstractEntityPersister persister = (AbstractEntityPersister) entityDefinition.getEntityPersister(); visitIdentifierDefinition( entityDefinition.getEntityKeyDefinition() ); visitAttributes( entityDefinition, persister ); } finally { strategy.finishingEntity( entityDefinition ); } } private void visitIdentifierDefinition(EntityIdentifierDefinition identifierDefinition) { strategy.startingEntityIdentifier( identifierDefinition ); try { // to make encapsulated and non-encapsulated composite identifiers work the same here, we "cheat" here a // little bit and simply walk the attributes of the composite id in both cases. // this works because the LoadPlans already build the top-level composite for composite ids if ( identifierDefinition.isEncapsulated() ) { // in the encapsulated composite id case that means we have a little bit of duplication between here and // visitCompositeDefinition, but in the spirit of consistently handling composite ids, that is much better // solution... final EncapsulatedEntityIdentifierDefinition idAsEncapsulated = (EncapsulatedEntityIdentifierDefinition) identifierDefinition; final AttributeDefinition idAttr = idAsEncapsulated.getAttributeDefinition(); if ( CompositionDefinition.class.isInstance( idAttr ) ) { visitCompositeDefinition( (CompositionDefinition) idAttr ); } } else { // NonEncapsulatedEntityIdentifierDefinition itself is defined as a CompositionDefinition visitCompositeDefinition( (NonEncapsulatedEntityIdentifierDefinition) identifierDefinition ); } } finally { strategy.finishingEntityIdentifier( identifierDefinition ); } } private void visitAttributes(AttributeSource attributeSource, AbstractEntityPersister sourcePersister) { final Iterable attributeDefinitions = attributeSource.getAttributes(); if ( attributeDefinitions == null ) { return; } for ( AttributeDefinition attributeDefinition : attributeDefinitions ) { visitAttributeDefinition( attributeDefinition, sourcePersister); } } private void visitAttributeDefinition(AttributeDefinition attributeDefinition, AbstractEntityPersister sourcePersister) { final PropertyPath subPath = currentPropertyPath.append( attributeDefinition.getName() ); log.debug( "Visiting attribute path : " + subPath.getFullPath() ); if ( attributeDefinition.getType().isAssociationType() ) { final AssociationAttributeDefinition associationAttributeDefinition = (AssociationAttributeDefinition) attributeDefinition; final AssociationKey associationKey = associationAttributeDefinition.getAssociationKey(); if ( isDuplicateAssociationKey( associationKey ) ) { log.debug( "Property path deemed to be circular : " + subPath.getFullPath() ); strategy.foundCircularAssociation( associationAttributeDefinition ); // EARLY EXIT!!! return; } if ( sourcePersister != null ) { String[] columns = sourcePersister.toColumns(attributeDefinition.getName()); // Empty columns means that the attribute is not resolvable on this persister if ( columns.length == 0 ) { return; } } } boolean continueWalk = strategy.startingAttribute( attributeDefinition ); try { if ( continueWalk ) { final PropertyPath old = currentPropertyPath; currentPropertyPath = subPath; try { final Type attributeType = attributeDefinition.getType(); if ( attributeType.isAssociationType() ) { visitAssociation( (AssociationAttributeDefinition) attributeDefinition ); } else if ( attributeType.isComponentType() ) { visitCompositeDefinition( (CompositionDefinition) attributeDefinition ); } } finally { currentPropertyPath = old; } } } finally { strategy.finishingAttribute( attributeDefinition ); } } private void visitAssociation(AssociationAttributeDefinition attribute) { // todo : do "too deep" checks; but see note about adding depth to PropertyPath // // may also need to better account for "composite fetches" in terms of "depth". addAssociationKey( attribute.getAssociationKey() ); final AssociationAttributeDefinition.AssociationNature nature = attribute.getAssociationNature(); if ( nature == AssociationAttributeDefinition.AssociationNature.ANY ) { visitAnyDefinition( attribute.toAnyDefinition() ); } else if ( nature == AssociationAttributeDefinition.AssociationNature.COLLECTION ) { visitCollectionDefinition( attribute.toCollectionDefinition() ); } else { visitEntityDefinition( attribute.toEntityDefinition() ); } } private void visitAnyDefinition(AnyMappingDefinition anyDefinition) { strategy.foundAny( anyDefinition ); } private void visitCompositeDefinition(CompositionDefinition compositionDefinition) { strategy.startingComposite( compositionDefinition ); try { visitAttributes( compositionDefinition, null ); } finally { strategy.finishingComposite( compositionDefinition ); } } private void visitCollectionDefinition(CollectionDefinition collectionDefinition) { strategy.startingCollection( collectionDefinition ); try { visitCollectionIndex( collectionDefinition ); visitCollectionElements( collectionDefinition ); } finally { strategy.finishingCollection( collectionDefinition ); } } private void visitCollectionIndex(CollectionDefinition collectionDefinition) { final CollectionIndexDefinition collectionIndexDefinition = collectionDefinition.getIndexDefinition(); if ( collectionIndexDefinition == null ) { return; } strategy.startingCollectionIndex( collectionIndexDefinition ); try { log.debug( "Visiting index for collection : " + currentPropertyPath.getFullPath() ); currentPropertyPath = currentPropertyPath.append( "" ); try { final Type collectionIndexType = collectionIndexDefinition.getType(); if ( collectionIndexType.isAnyType() ) { visitAnyDefinition( collectionIndexDefinition.toAnyMappingDefinition() ); } else if ( collectionIndexType.isComponentType() ) { visitCompositeDefinition( collectionIndexDefinition.toCompositeDefinition() ); } else if ( collectionIndexType.isAssociationType() ) { visitEntityDefinition( collectionIndexDefinition.toEntityDefinition() ); } } finally { currentPropertyPath = currentPropertyPath.getParent(); } } finally { strategy.finishingCollectionIndex( collectionIndexDefinition ); } } private void visitCollectionElements(CollectionDefinition collectionDefinition) { final CollectionElementDefinition elementDefinition = collectionDefinition.getElementDefinition(); strategy.startingCollectionElements( elementDefinition ); try { final Type collectionElementType = elementDefinition.getType(); if ( collectionElementType.isAnyType() ) { visitAnyDefinition( elementDefinition.toAnyMappingDefinition() ); } else if ( collectionElementType.isComponentType() ) { visitCompositeDefinition( elementDefinition.toCompositeElementDefinition() ); } else if ( collectionElementType.isEntityType() ) { if ( !collectionDefinition.getCollectionPersister().isOneToMany() ) { final QueryableCollection queryableCollection = (QueryableCollection) collectionDefinition.getCollectionPersister(); addAssociationKey( new AssociationKey( queryableCollection.getTableName(), queryableCollection.getElementColumnNames() ) ); } visitEntityDefinition( elementDefinition.toEntityDefinition() ); } } finally { strategy.finishingCollectionElements( elementDefinition ); } } private final Set visitedAssociationKeys = new HashSet(); /** * Add association key to indicate the association is being visited. * @param associationKey - the association key. * @throws WalkingException if the association with the specified association key * has already been visited. */ protected void addAssociationKey(AssociationKey associationKey) { if ( ! visitedAssociationKeys.add( associationKey ) ) { throw new WalkingException( String.format( "Association has already been visited: %s", associationKey ) ); } strategy.associationKeyRegistered( associationKey ); } /** * Has an association with the specified key been visited already? * @param associationKey - the association key. * @return true, if the association with the specified association key has already been visited; * false, otherwise. */ protected boolean isDuplicateAssociationKey(AssociationKey associationKey) { return visitedAssociationKeys.contains( associationKey ) || strategy.isDuplicateAssociationKey( associationKey ); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy