📄 hqlsqlwalker.java
字号:
// $Id: HqlSqlWalker.java 10060 2006-06-28 02:53:39Z steve.ebersole@jboss.com $
package org.hibernate.hql.ast;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.QueryException;
import org.hibernate.HibernateException;
import org.hibernate.engine.JoinSequence;
import org.hibernate.engine.ParameterBinder;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.hql.QueryTranslator;
import org.hibernate.hql.antlr.HqlSqlBaseWalker;
import org.hibernate.hql.antlr.HqlSqlTokenTypes;
import org.hibernate.hql.antlr.HqlTokenTypes;
import org.hibernate.hql.antlr.SqlTokenTypes;
import org.hibernate.hql.ast.tree.AssignmentSpecification;
import org.hibernate.hql.ast.tree.CollectionFunction;
import org.hibernate.hql.ast.tree.ConstructorNode;
import org.hibernate.hql.ast.tree.DeleteStatement;
import org.hibernate.hql.ast.tree.DotNode;
import org.hibernate.hql.ast.tree.FromClause;
import org.hibernate.hql.ast.tree.FromElement;
import org.hibernate.hql.ast.tree.FromReferenceNode;
import org.hibernate.hql.ast.tree.IdentNode;
import org.hibernate.hql.ast.tree.IndexNode;
import org.hibernate.hql.ast.tree.InsertStatement;
import org.hibernate.hql.ast.tree.IntoClause;
import org.hibernate.hql.ast.tree.MethodNode;
import org.hibernate.hql.ast.tree.ParameterNode;
import org.hibernate.hql.ast.tree.QueryNode;
import org.hibernate.hql.ast.tree.ResolvableNode;
import org.hibernate.hql.ast.tree.RestrictableStatement;
import org.hibernate.hql.ast.tree.SelectClause;
import org.hibernate.hql.ast.tree.SelectExpression;
import org.hibernate.hql.ast.tree.UpdateStatement;
import org.hibernate.hql.ast.tree.Node;
import org.hibernate.hql.ast.tree.OperatorNode;
import org.hibernate.hql.ast.util.ASTPrinter;
import org.hibernate.hql.ast.util.ASTUtil;
import org.hibernate.hql.ast.util.AliasGenerator;
import org.hibernate.hql.ast.util.JoinProcessor;
import org.hibernate.hql.ast.util.LiteralProcessor;
import org.hibernate.hql.ast.util.SessionFactoryHelper;
import org.hibernate.hql.ast.util.SyntheticAndFactory;
import org.hibernate.hql.ast.util.NodeTraverser;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.PostInsertIdentifierGenerator;
import org.hibernate.id.SequenceGenerator;
import org.hibernate.param.NamedParameterSpecification;
import org.hibernate.param.ParameterSpecification;
import org.hibernate.param.PositionalParameterSpecification;
import org.hibernate.param.VersionTypeSeedParameterSpecification;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.JoinFragment;
import org.hibernate.type.AssociationType;
import org.hibernate.type.Type;
import org.hibernate.type.VersionType;
import org.hibernate.type.DbTimestampType;
import org.hibernate.usertype.UserVersionType;
import org.hibernate.util.ArrayHelper;
import antlr.ASTFactory;
import antlr.RecognitionException;
import antlr.SemanticException;
import antlr.collections.AST;
/**
* Implements methods used by the HQL->SQL tree transform grammar (a.k.a. the second phase).
* <ul>
* <li>Isolates the Hibernate API-specific code from the ANTLR generated code.</li>
* <li>Handles the SQL framgents generated by the persisters in order to create the SELECT and FROM clauses,
* taking into account the joins and projections that are implied by the mappings (persister/queryable).</li>
* <li>Uses SqlASTFactory to create customized AST nodes.</li>
* </ul>
*
* @see SqlASTFactory
*/
public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, ParameterBinder.NamedParameterSource {
private static Log log = LogFactory.getLog( HqlSqlWalker.class );
/**
* A delegate that handles the Hiberanate meta data model.
*/
private SessionFactoryHelper sessionFactoryHelper;
/**
* A delegate that handles literal constants.
*/
private LiteralProcessor literalProcessor;
/**
* Handles parser errors.
*/
private ParseErrorHandler parseErrorHandler;
/**
* The current context.
*/
private FromClause currentFromClause = null;
/**
* The top-level SelectClause.
*/
private SelectClause selectClause;
/**
* Generates alias names for tables.
*/
private AliasGenerator aliasGenerator = new AliasGenerator();
/**
* The set of unique query spaces (a.k.a. table names).
*/
private Set querySpaces = new HashSet();
/**
* A (string->string) map is used to substitute function names and literals.
*/
private Map tokenReplacements;
private QueryTranslatorImpl queryTranslatorImpl;
/**
* The number of parameters encountered so far.
*/
private int parameterCount;
/**
* A map of lists which associates named and numbered parameters to a list of occurrences in the query.
*/
private Map namedParameters = new HashMap();
/**
* The filter collection role, or null if this isn't a filter compilation.
*/
private String filterCollectionRole;
/**
* The parser that performed phase 1 - parse the HQL into an HQL tree.
*/
private HqlParser hqlParser;
private ASTPrinter printer;
/**
* The join type for any implied joins.
*/
private int impliedJoinType;
private ArrayList parameters = new ArrayList();
private int numberOfParametersInSetClause;
private int positionalParameterCount;
private ArrayList assignmentSpecifications = new ArrayList();
/**
* Create a new tree transformer.
*
* @param qti Back pointer to the query translator implementation that is using this tree transform.
* @param sfi The session factory implementor where the Hibernate mappings can be found.
* @param parser
* @param tokenReplacements Registers the token replacement map with the walker. This map will
* be used to substitute function names and constants.
* @param collectionRole the role name of the collection used as the basis for the filter, NULL if this
*/
public HqlSqlWalker(QueryTranslatorImpl qti,
SessionFactoryImplementor sfi,
HqlParser parser,
Map tokenReplacements,
String collectionRole) {
setASTFactory( new SqlASTFactory( this ) );
// Initialize the error handling delegate.
this.parseErrorHandler = new ErrorCounter();
this.queryTranslatorImpl = qti;
this.sessionFactoryHelper = new SessionFactoryHelper( sfi );
this.literalProcessor = new LiteralProcessor( this );
this.tokenReplacements = tokenReplacements;
this.filterCollectionRole = collectionRole;
this.hqlParser = parser;
this.printer = new ASTPrinter( SqlTokenTypes.class );
}
protected void prepareFromClauseInputTree(AST fromClauseInput) {
// Handle fiter compilation.
// IMPORTANT NOTE: This is modifying the INPUT (HQL) tree, not the output tree!
if ( isFilter() && !isSubQuery() ) {
QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( filterCollectionRole );
Type collectionElementType = persister.getElementType();
if ( !collectionElementType.isEntityType() ) {
throw new QueryException( "collection of values in filter: this" );
}
String collectionElementEntityName = persister.getElementPersister().getEntityName();
ASTFactory inputAstFactory = hqlParser.getASTFactory();
AST fromElement = ASTUtil.create( inputAstFactory, HqlTokenTypes.FILTER_ENTITY, collectionElementEntityName );
ASTUtil.createSibling( inputAstFactory, HqlTokenTypes.ALIAS, "this", fromElement );
fromClauseInput.addChild( fromElement );
// Show the modified AST.
if ( log.isDebugEnabled() ) {
log.debug( "prepareFromClauseInputTree() : Filter - Added 'this' as a from element..." );
}
queryTranslatorImpl.showHqlAst( hqlParser.getAST() );
}
}
private boolean isFilter() {
return filterCollectionRole != null;
}
public SessionFactoryHelper getSessionFactoryHelper() {
return sessionFactoryHelper;
}
public Map getTokenReplacements() {
return tokenReplacements;
}
public AliasGenerator getAliasGenerator() {
return aliasGenerator;
}
public FromClause getCurrentFromClause() {
return currentFromClause;
}
public ParseErrorHandler getParseErrorHandler() {
return parseErrorHandler;
}
public void reportError(RecognitionException e) {
parseErrorHandler.reportError( e ); // Use the delegate.
}
public void reportError(String s) {
parseErrorHandler.reportError( s ); // Use the delegate.
}
public void reportWarning(String s) {
parseErrorHandler.reportWarning( s );
}
/**
* Returns the set of unique query spaces (a.k.a.
* table names) that occurred in the query.
*
* @return A set of table names (Strings).
*/
public Set getQuerySpaces() {
return querySpaces;
}
protected AST createFromElement(String path, AST alias, AST propertyFetch) throws SemanticException {
FromElement fromElement = currentFromClause.addFromElement( path, alias );
fromElement.setAllPropertyFetch(propertyFetch!=null);
return fromElement;
}
protected AST createFromFilterElement(AST filterEntity, AST alias) throws SemanticException {
FromElement fromElement = currentFromClause.addFromElement( filterEntity.getText(), alias );
FromClause fromClause = fromElement.getFromClause();
QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( filterCollectionRole );
// Get the names of the columns used to link between the collection
// owner and the collection elements.
String[] keyColumnNames = persister.getKeyColumnNames();
String fkTableAlias = persister.isOneToMany()
? fromElement.getTableAlias()
: fromClause.getAliasGenerator().createName( filterCollectionRole );
JoinSequence join = sessionFactoryHelper.createJoinSequence();
join.setRoot( persister, fkTableAlias );
if ( !persister.isOneToMany() ) {
join.addJoin( ( AssociationType ) persister.getElementType(),
fromElement.getTableAlias(),
JoinFragment.INNER_JOIN,
persister.getElementColumnNames( fkTableAlias ) );
}
join.addCondition( fkTableAlias, keyColumnNames, " = ?" );
fromElement.setJoinSequence( join );
fromElement.setFilter( true );
if ( log.isDebugEnabled() ) {
log.debug( "createFromFilterElement() : processed filter FROM element." );
}
return fromElement;
}
protected void createFromJoinElement(
AST path,
AST alias,
int joinType,
AST fetchNode,
AST propertyFetch,
AST with) throws SemanticException {
boolean fetch = fetchNode != null;
if ( fetch && isSubQuery() ) {
throw new QueryException( "fetch not allowed in subquery from-elements" );
}
// The path AST should be a DotNode, and it should have been evaluated already.
if ( path.getType() != SqlTokenTypes.DOT ) {
throw new SemanticException( "Path expected for join!" );
}
DotNode dot = ( DotNode ) path;
int hibernateJoinType = JoinProcessor.toHibernateJoinType( joinType );
dot.setJoinType( hibernateJoinType ); // Tell the dot node about the join type.
dot.setFetch( fetch );
// Generate an explicit join for the root dot node. The implied joins will be collected and passed up
// to the root dot node.
dot.resolve( true, false, alias == null ? null : alias.getText() );
FromElement fromElement = dot.getImpliedJoin();
fromElement.setAllPropertyFetch(propertyFetch!=null);
if ( with != null ) {
if ( fetch ) {
throw new SemanticException( "with-clause not allowed on fetched associations; use filters" );
}
handleWithFragment( fromElement, with );
}
if ( log.isDebugEnabled() ) {
log.debug( "createFromJoinElement() : " + getASTPrinter().showAsString( fromElement, "-- join tree --" ) );
}
}
private void handleWithFragment(FromElement fromElement, AST hqlWithNode) throws SemanticException
{
try {
withClause( hqlWithNode );
AST hqlSqlWithNode = returnAST;
if ( log.isDebugEnabled() ) {
log.debug( "handleWithFragment() : " + getASTPrinter().showAsString( hqlSqlWithNode, "-- with clause --" ) );
}
WithClauseVisitor visitor = new WithClauseVisitor();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -