📄 dotnode.java
字号:
// $Id: DotNode.java 11378 2007-03-30 11:23:48Z steve.ebersole@jboss.com $package org.hibernate.hql.ast.tree;import org.hibernate.QueryException;import org.hibernate.engine.JoinSequence;import org.hibernate.hql.CollectionProperties;import org.hibernate.hql.antlr.SqlTokenTypes;import org.hibernate.hql.ast.util.ASTPrinter;import org.hibernate.hql.ast.util.ASTUtil;import org.hibernate.hql.ast.util.ColumnHelper;import org.hibernate.persister.collection.QueryableCollection;import org.hibernate.persister.entity.EntityPersister;import org.hibernate.sql.JoinFragment;import org.hibernate.type.CollectionType;import org.hibernate.type.EntityType;import org.hibernate.type.Type;import org.hibernate.util.StringHelper;import antlr.SemanticException;import antlr.collections.AST;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * Represents a reference to a property or alias expression. This should duplicate the relevant behaviors in * PathExpressionParser. * * @author Joshua Davis */public class DotNode extends FromReferenceNode implements DisplayableNode, SelectExpression { /////////////////////////////////////////////////////////////////////////// // USED ONLY FOR REGRESSION TESTING!!!! // // todo : obviously get rid of all this junk ;) /////////////////////////////////////////////////////////////////////////// public static boolean useThetaStyleImplicitJoins = false; public static boolean REGRESSION_STYLE_JOIN_SUPPRESSION = false; public static interface IllegalCollectionDereferenceExceptionBuilder { public QueryException buildIllegalCollectionDereferenceException(String collectionPropertyName, FromReferenceNode lhs); } public static final IllegalCollectionDereferenceExceptionBuilder DEF_ILLEGAL_COLL_DEREF_EXCP_BUILDER = new IllegalCollectionDereferenceExceptionBuilder() { public QueryException buildIllegalCollectionDereferenceException(String propertyName, FromReferenceNode lhs) { String lhsPath = ASTUtil.getPathText( lhs ); return new QueryException( "illegal attempt to dereference collection [" + lhsPath + "] with element property reference [" + propertyName + "]" ); } }; public static IllegalCollectionDereferenceExceptionBuilder ILLEGAL_COLL_DEREF_EXCP_BUILDER = DEF_ILLEGAL_COLL_DEREF_EXCP_BUILDER; /////////////////////////////////////////////////////////////////////////// private static final Logger log = LoggerFactory.getLogger( DotNode.class ); private static final int DEREF_UNKNOWN = 0; private static final int DEREF_ENTITY = 1; private static final int DEREF_COMPONENT = 2; private static final int DEREF_COLLECTION = 3; private static final int DEREF_PRIMITIVE = 4; private static final int DEREF_IDENTIFIER = 5; private static final int DEREF_JAVA_CONSTANT = 6; /** * The identifier that is the name of the property. */ private String propertyName; /** * The full path, to the root alias of this dot node. */ private String path; /** * The unresolved property path relative to this dot node. */ private String propertyPath; /** * The column names that this resolves to. */ private String[] columns; /** * The type of join to create. Default is an inner join. */ private int joinType = JoinFragment.INNER_JOIN; /** * Fetch join or not. */ private boolean fetch = false; /** * The type of dereference that hapened (DEREF_xxx). */ private int dereferenceType = DEREF_UNKNOWN; private FromElement impliedJoin; /** * Sets the join type for this '.' node structure. * * @param joinType The type of join to use. * @see JoinFragment */ public void setJoinType(int joinType) { this.joinType = joinType; } private String[] getColumns() throws QueryException { if ( columns == null ) { // Use the table fromElement and the property name to get the array of column names. String tableAlias = getLhs().getFromElement().getTableAlias(); columns = getFromElement().toColumns( tableAlias, propertyPath, false ); } return columns; } public String getDisplayText() { StringBuffer buf = new StringBuffer(); FromElement fromElement = getFromElement(); buf.append( "{propertyName=" ).append( propertyName ); buf.append( ",dereferenceType=" ).append( ASTPrinter.getConstantName( getClass(), dereferenceType ) ); buf.append( ",propertyPath=" ).append( propertyPath ); buf.append( ",path=" ).append( getPath() ); if ( fromElement != null ) { buf.append( ",tableAlias=" ).append( fromElement.getTableAlias() ); buf.append( ",className=" ).append( fromElement.getClassName() ); buf.append( ",classAlias=" ).append( fromElement.getClassAlias() ); } else { buf.append( ",no from element" ); } buf.append( '}' ); return buf.toString(); } /** * Resolves the left hand side of the DOT. * * @throws SemanticException */ public void resolveFirstChild() throws SemanticException { FromReferenceNode lhs = ( FromReferenceNode ) getFirstChild(); SqlNode property = ( SqlNode ) lhs.getNextSibling(); // Set the attributes of the property reference expression. String propName = property.getText(); propertyName = propName; // If the uresolved property path isn't set yet, just use the property name. if ( propertyPath == null ) { propertyPath = propName; } // Resolve the LHS fully, generate implicit joins. Pass in the property name so that the resolver can // discover foreign key (id) properties. lhs.resolve( true, true, null, this ); setFromElement( lhs.getFromElement() ); // The 'from element' that the property is in. checkSubclassOrSuperclassPropertyReference( lhs, propName ); } public void resolveInFunctionCall(boolean generateJoin, boolean implicitJoin) throws SemanticException { if ( isResolved() ) { return; } Type propertyType = prepareLhs(); // Prepare the left hand side and get the data type. if ( propertyType!=null && propertyType.isCollectionType() ) { resolveIndex(null); } else { resolveFirstChild(); super.resolve(generateJoin, implicitJoin); } } public void resolveIndex(AST parent) throws SemanticException { if ( isResolved() ) { return; } Type propertyType = prepareLhs(); // Prepare the left hand side and get the data type. dereferenceCollection( ( CollectionType ) propertyType, true, true, null, parent ); } public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent) throws SemanticException { // If this dot has already been resolved, stop now. if ( isResolved() ) { return; } Type propertyType = prepareLhs(); // Prepare the left hand side and get the data type. // If there is no data type for this node, and we're at the end of the path (top most dot node), then // this might be a Java constant. if ( propertyType == null ) { if ( parent == null ) { getWalker().getLiteralProcessor().lookupConstant( this ); } // If the propertyType is null and there isn't a parent, just // stop now... there was a problem resolving the node anyway. return; } if ( propertyType.isComponentType() ) { // The property is a component... checkLhsIsNotCollection(); dereferenceComponent( parent ); initText(); } else if ( propertyType.isEntityType() ) { // The property is another class.. checkLhsIsNotCollection(); dereferenceEntity( ( EntityType ) propertyType, implicitJoin, classAlias, generateJoin, parent ); initText(); } else if ( propertyType.isCollectionType() ) { // The property is a collection... checkLhsIsNotCollection(); dereferenceCollection( ( CollectionType ) propertyType, implicitJoin, false, classAlias, parent ); } else { // Otherwise, this is a primitive type. if ( ! CollectionProperties.isAnyCollectionProperty( propertyName ) ) { checkLhsIsNotCollection(); } dereferenceType = DEREF_PRIMITIVE; initText(); } setResolved(); } private void initText() { String[] cols = getColumns(); String text = StringHelper.join( ", ", cols ); if ( cols.length > 1 && getWalker().isComparativeExpressionClause() ) { text = "(" + text + ")"; } setText( text ); } private Type prepareLhs() throws SemanticException { FromReferenceNode lhs = getLhs(); lhs.prepareForDot( propertyName ); return getDataType(); } private void dereferenceCollection(CollectionType collectionType, boolean implicitJoin, boolean indexed, String classAlias, AST parent) throws SemanticException { dereferenceType = DEREF_COLLECTION; String role = collectionType.getRole(); //foo.bars.size (also handles deprecated stuff like foo.bars.maxelement for backwardness) boolean isSizeProperty = getNextSibling()!=null && CollectionProperties.isAnyCollectionProperty( getNextSibling().getText() ); if ( isSizeProperty ) indexed = true; //yuck! QueryableCollection queryableCollection = getSessionFactoryHelper().requireQueryableCollection( role ); String propName = getPath(); FromClause currentFromClause = getWalker().getCurrentFromClause(); if ( getWalker().getStatementType() != SqlTokenTypes.SELECT && indexed && classAlias == null ) { // should indicate that we are processing an INSERT/UPDATE/DELETE // query with a subquery implied via a collection property // function. Here, we need to use the table name itself as the // qualification alias. // TODO : verify this works for all databases... // TODO : is this also the case in non-"indexed" scenarios? String alias = getLhs().getFromElement().getQueryable().getTableName(); columns = getFromElement().toColumns( alias, propertyPath, false, true ); } //We do not look for an existing join on the same path, because //it makes sense to join twice on the same collection role FromElementFactory factory = new FromElementFactory( currentFromClause, getLhs().getFromElement(), propName, classAlias, getColumns(), implicitJoin ); FromElement elem = factory.createCollection( queryableCollection, role, joinType, fetch, indexed ); if ( log.isDebugEnabled() ) { log.debug( "dereferenceCollection() : Created new FROM element for " + propName + " : " + elem ); } setImpliedJoin( elem ); setFromElement( elem ); // This 'dot' expression now refers to the resulting from element. if ( isSizeProperty ) { elem.setText(""); elem.setUseWhereFragment(false); } if ( !implicitJoin ) { EntityPersister entityPersister = elem.getEntityPersister(); if ( entityPersister != null ) { getWalker().addQuerySpaces( entityPersister.getQuerySpaces() ); } } getWalker().addQuerySpaces( queryableCollection.getCollectionSpaces() ); // Always add the collection's query spaces. } private void dereferenceEntity(EntityType entityType, boolean implicitJoin, String classAlias, boolean generateJoin, AST parent) throws SemanticException { checkForCorrelatedSubquery( "dereferenceEntity" ); // three general cases we check here as to whether to render a physical SQL join: // 1) is our parent a DotNode as well? If so, our property reference is // being further de-referenced... // 2) is this a DML statement // 3) we were asked to generate any needed joins (generateJoins==true) *OR* // we are currently processing a select or from clause // (an additional check is the REGRESSION_STYLE_JOIN_SUPPRESSION check solely intended for the test suite) // // The REGRESSION_STYLE_JOIN_SUPPRESSION is an additional check // intended solely for use within the test suite. This forces the // implicit join resolution to behave more like the classic parser. // The underlying issue is that classic translator is simply wrong // about its decisions on whether or not to render an implicit join // into a physical SQL join in a lot of cases. The piece it generally // tends to miss is that INNER joins effect the results by further // restricting the data set! A particular manifestation of this is // the fact that the classic translator will skip the physical join // for ToOne implicit joins *if the query is shallow*; the result // being that Query.list() and Query.iterate() could return // different number of results! DotNode parentAsDotNode = null; String property = propertyName; final boolean joinIsNeeded; if ( isDotNode( parent ) ) { // our parent is another dot node, meaning we are being further dereferenced. // thus we need to generate a join unless the parent refers to the associated // entity's PK (because 'our' table would know the FK). parentAsDotNode = ( DotNode ) parent;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -