Save This Page
Home » Hibernate-3.3.2.GA » org.hibernate » hql » ast » [javadoc | source]
    1   /*
    2    * Hibernate, Relational Persistence for Idiomatic Java
    3    *
    4    * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
    5    * indicated by the @author tags or express copyright attribution
    6    * statements applied by the authors.  All third-party contributions are
    7    * distributed under license by Red Hat Middleware LLC.
    8    *
    9    * This copyrighted material is made available to anyone wishing to use, modify,
   10    * copy, or redistribute it subject to the terms and conditions of the GNU
   11    * Lesser General Public License, as published by the Free Software Foundation.
   12    *
   13    * This program is distributed in the hope that it will be useful,
   14    * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   15    * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
   16    * for more details.
   17    *
   18    * You should have received a copy of the GNU Lesser General Public License
   19    * along with this distribution; if not, write to:
   20    * Free Software Foundation, Inc.
   21    * 51 Franklin Street, Fifth Floor
   22    * Boston, MA  02110-1301  USA
   23    *
   24    */
   25   package org.hibernate.hql.ast;
   26   
   27   import antlr.ANTLRException;
   28   import antlr.RecognitionException;
   29   import antlr.TokenStreamException;
   30   import antlr.collections.AST;
   31   import org.slf4j.Logger;
   32   import org.slf4j.LoggerFactory;
   33   import org.hibernate.HibernateException;
   34   import org.hibernate.MappingException;
   35   import org.hibernate.QueryException;
   36   import org.hibernate.ScrollableResults;
   37   import org.hibernate.engine.QueryParameters;
   38   import org.hibernate.engine.RowSelection;
   39   import org.hibernate.engine.SessionFactoryImplementor;
   40   import org.hibernate.engine.SessionImplementor;
   41   import org.hibernate.event.EventSource;
   42   import org.hibernate.hql.FilterTranslator;
   43   import org.hibernate.hql.QueryExecutionRequestException;
   44   import org.hibernate.hql.ParameterTranslations;
   45   import org.hibernate.hql.antlr.HqlSqlTokenTypes;
   46   import org.hibernate.hql.antlr.HqlTokenTypes;
   47   import org.hibernate.hql.antlr.SqlTokenTypes;
   48   import org.hibernate.hql.ast.exec.BasicExecutor;
   49   import org.hibernate.hql.ast.exec.MultiTableDeleteExecutor;
   50   import org.hibernate.hql.ast.exec.MultiTableUpdateExecutor;
   51   import org.hibernate.hql.ast.exec.StatementExecutor;
   52   import org.hibernate.hql.ast.tree.FromElement;
   53   import org.hibernate.hql.ast.tree.InsertStatement;
   54   import org.hibernate.hql.ast.tree.QueryNode;
   55   import org.hibernate.hql.ast.tree.Statement;
   56   import org.hibernate.hql.ast.util.ASTPrinter;
   57   import org.hibernate.hql.ast.util.NodeTraverser;
   58   import org.hibernate.hql.ast.util.ASTUtil;
   59   import org.hibernate.loader.hql.QueryLoader;
   60   import org.hibernate.persister.entity.Queryable;
   61   import org.hibernate.type.Type;
   62   import org.hibernate.util.IdentitySet;
   63   import org.hibernate.util.StringHelper;
   64   import org.hibernate.util.ReflectHelper;
   65   
   66   import java.util.HashMap;
   67   import java.util.Iterator;
   68   import java.util.List;
   69   import java.util.Map;
   70   import java.util.Set;
   71   import java.util.ArrayList;
   72   
   73   /**
   74    * A QueryTranslator that uses an Antlr-based parser.
   75    *
   76    * @author Joshua Davis (pgmjsd@sourceforge.net)
   77    */
   78   public class QueryTranslatorImpl implements FilterTranslator {
   79   
   80   	private static final Logger log = LoggerFactory.getLogger( QueryTranslatorImpl.class );
   81   	private static final Logger AST_LOG = LoggerFactory.getLogger( "org.hibernate.hql.ast.AST" );
   82   
   83   	private SessionFactoryImplementor factory;
   84   
   85   	private final String queryIdentifier;
   86   	private String hql;
   87   	private boolean shallowQuery;
   88   	private Map tokenReplacements;
   89   
   90   	private Map enabledFilters; //TODO:this is only needed during compilation .. can we eliminate the instvar?
   91   
   92   	private boolean compiled;
   93   	private QueryLoader queryLoader;
   94   	private StatementExecutor statementExecutor;
   95   
   96   	private Statement sqlAst;
   97   	private String sql;
   98   
   99   	private ParameterTranslations paramTranslations;
  100   	private List collectedParameterSpecifications;
  101   
  102   
  103   	/**
  104   	 * Creates a new AST-based query translator.
  105   	 *
  106   	 * @param queryIdentifier The query-identifier (used in stats collection)
  107   	 * @param query The hql query to translate
  108   	 * @param enabledFilters Currently enabled filters
  109   	 * @param factory The session factory constructing this translator instance.
  110   	 */
  111   	public QueryTranslatorImpl(
  112   			String queryIdentifier,
  113   	        String query,
  114   	        Map enabledFilters,
  115   	        SessionFactoryImplementor factory) {
  116   		this.queryIdentifier = queryIdentifier;
  117   		this.hql = query;
  118   		this.compiled = false;
  119   		this.shallowQuery = false;
  120   		this.enabledFilters = enabledFilters;
  121   		this.factory = factory;
  122   	}
  123   
  124   	/**
  125   	 * Compile a "normal" query. This method may be called multiple
  126   	 * times. Subsequent invocations are no-ops.
  127   	 *
  128   	 * @param replacements Defined query substitutions.
  129   	 * @param shallow      Does this represent a shallow (scalar or entity-id) select?
  130   	 * @throws QueryException   There was a problem parsing the query string.
  131   	 * @throws MappingException There was a problem querying defined mappings.
  132   	 */
  133   	public void compile(
  134   	        Map replacements,
  135   	        boolean shallow) throws QueryException, MappingException {
  136   		doCompile( replacements, shallow, null );
  137   	}
  138   
  139   	/**
  140   	 * Compile a filter. This method may be called multiple
  141   	 * times. Subsequent invocations are no-ops.
  142   	 *
  143   	 * @param collectionRole the role name of the collection used as the basis for the filter.
  144   	 * @param replacements   Defined query substitutions.
  145   	 * @param shallow        Does this represent a shallow (scalar or entity-id) select?
  146   	 * @throws QueryException   There was a problem parsing the query string.
  147   	 * @throws MappingException There was a problem querying defined mappings.
  148   	 */
  149   	public void compile(
  150   	        String collectionRole,
  151   	        Map replacements,
  152   	        boolean shallow) throws QueryException, MappingException {
  153   		doCompile( replacements, shallow, collectionRole );
  154   	}
  155   
  156   	/**
  157   	 * Performs both filter and non-filter compiling.
  158   	 *
  159   	 * @param replacements   Defined query substitutions.
  160   	 * @param shallow        Does this represent a shallow (scalar or entity-id) select?
  161   	 * @param collectionRole the role name of the collection used as the basis for the filter, NULL if this
  162   	 *                       is not a filter.
  163   	 */
  164   	private synchronized void doCompile(Map replacements, boolean shallow, String collectionRole) {
  165   		// If the query is already compiled, skip the compilation.
  166   		if ( compiled ) {
  167   			if ( log.isDebugEnabled() ) {
  168   				log.debug( "compile() : The query is already compiled, skipping..." );
  169   			}
  170   			return;
  171   		}
  172   
  173   		// Remember the parameters for the compilation.
  174   		this.tokenReplacements = replacements;
  175   		if ( tokenReplacements == null ) {
  176   			tokenReplacements = new HashMap();
  177   		}
  178   		this.shallowQuery = shallow;
  179   
  180   		try {
  181   			// PHASE 1 : Parse the HQL into an AST.
  182   			HqlParser parser = parse( true );
  183   
  184   			// PHASE 2 : Analyze the HQL AST, and produce an SQL AST.
  185   			HqlSqlWalker w = analyze( parser, collectionRole );
  186   
  187   			sqlAst = ( Statement ) w.getAST();
  188   
  189   			// at some point the generate phase needs to be moved out of here,
  190   			// because a single object-level DML might spawn multiple SQL DML
  191   			// command executions.
  192   			//
  193   			// Possible to just move the sql generation for dml stuff, but for
  194   			// consistency-sake probably best to just move responsiblity for
  195   			// the generation phase completely into the delegates
  196   			// (QueryLoader/StatementExecutor) themselves.  Also, not sure why
  197   			// QueryLoader currently even has a dependency on this at all; does
  198   			// it need it?  Ideally like to see the walker itself given to the delegates directly...
  199   
  200   			if ( sqlAst.needsExecutor() ) {
  201   				statementExecutor = buildAppropriateStatementExecutor( w );
  202   			}
  203   			else {
  204   				// PHASE 3 : Generate the SQL.
  205   				generate( ( QueryNode ) sqlAst );
  206   				queryLoader = new QueryLoader( this, factory, w.getSelectClause() );
  207   			}
  208   
  209   			compiled = true;
  210   		}
  211   		catch ( QueryException qe ) {
  212   			qe.setQueryString( hql );
  213   			throw qe;
  214   		}
  215   		catch ( RecognitionException e ) {
  216   			// we do not actually propogate ANTLRExceptions as a cause, so
  217   			// log it here for diagnostic purposes
  218   			if ( log.isTraceEnabled() ) {
  219   				log.trace( "converted antlr.RecognitionException", e );
  220   			}
  221   			throw QuerySyntaxException.convert( e, hql );
  222   		}
  223   		catch ( ANTLRException e ) {
  224   			// we do not actually propogate ANTLRExceptions as a cause, so
  225   			// log it here for diagnostic purposes
  226   			if ( log.isTraceEnabled() ) {
  227   				log.trace( "converted antlr.ANTLRException", e );
  228   			}
  229   			throw new QueryException( e.getMessage(), hql );
  230   		}
  231   
  232   		this.enabledFilters = null; //only needed during compilation phase...
  233   	}
  234   
  235   	private void generate(AST sqlAst) throws QueryException, RecognitionException {
  236   		if ( sql == null ) {
  237   			SqlGenerator gen = new SqlGenerator(factory);
  238   			gen.statement( sqlAst );
  239   			sql = gen.getSQL();
  240   			if ( log.isDebugEnabled() ) {
  241   				log.debug( "HQL: " + hql );
  242   				log.debug( "SQL: " + sql );
  243   			}
  244   			gen.getParseErrorHandler().throwQueryException();
  245   			collectedParameterSpecifications = gen.getCollectedParameters();
  246   		}
  247   	}
  248   
  249   	private HqlSqlWalker analyze(HqlParser parser, String collectionRole) throws QueryException, RecognitionException {
  250   		HqlSqlWalker w = new HqlSqlWalker( this, factory, parser, tokenReplacements, collectionRole );
  251   		AST hqlAst = parser.getAST();
  252   
  253   		// Transform the tree.
  254   		w.statement( hqlAst );
  255   
  256   		if ( AST_LOG.isDebugEnabled() ) {
  257   			ASTPrinter printer = new ASTPrinter( SqlTokenTypes.class );
  258   			AST_LOG.debug( printer.showAsString( w.getAST(), "--- SQL AST ---" ) );
  259   		}
  260   
  261   		w.getParseErrorHandler().throwQueryException();
  262   
  263   		return w;
  264   	}
  265   
  266   	private HqlParser parse(boolean filter) throws TokenStreamException, RecognitionException {
  267   		// Parse the query string into an HQL AST.
  268   		HqlParser parser = HqlParser.getInstance( hql );
  269   		parser.setFilter( filter );
  270   
  271   		if ( log.isDebugEnabled() ) {
  272   			log.debug( "parse() - HQL: " + hql );
  273   		}
  274   		parser.statement();
  275   
  276   		AST hqlAst = parser.getAST();
  277   
  278   		JavaConstantConverter converter = new JavaConstantConverter();
  279   		NodeTraverser walker = new NodeTraverser( converter );
  280   		walker.traverseDepthFirst( hqlAst );
  281   
  282   		showHqlAst( hqlAst );
  283   
  284   		parser.getParseErrorHandler().throwQueryException();
  285   		return parser;
  286   	}
  287   
  288   	void showHqlAst(AST hqlAst) {
  289   		if ( AST_LOG.isDebugEnabled() ) {
  290   			ASTPrinter printer = new ASTPrinter( HqlTokenTypes.class, false );
  291   			AST_LOG.debug( printer.showAsString( hqlAst, "--- HQL AST ---" ) );
  292   		}
  293   	}
  294   
  295   	private void errorIfDML() throws HibernateException {
  296   		if ( sqlAst.needsExecutor() ) {
  297   			throw new QueryExecutionRequestException( "Not supported for DML operations", hql );
  298   		}
  299   	}
  300   
  301   	private void errorIfSelect() throws HibernateException {
  302   		if ( !sqlAst.needsExecutor() ) {
  303   			throw new QueryExecutionRequestException( "Not supported for select queries", hql );
  304   		}
  305   	}
  306   
  307   	public String getQueryIdentifier() {
  308   		return queryIdentifier;
  309   	}
  310   
  311   	public Statement getSqlAST() {
  312   		return sqlAst;
  313   	}
  314   
  315   	private HqlSqlWalker getWalker() {
  316   		return sqlAst.getWalker();
  317   	}
  318   
  319   	/**
  320   	 * Types of the return values of an <tt>iterate()</tt> style query.
  321   	 *
  322   	 * @return an array of <tt>Type</tt>s.
  323   	 */
  324   	public Type[] getReturnTypes() {
  325   		errorIfDML();
  326   		return getWalker().getReturnTypes();
  327   	}
  328   
  329   	public String[] getReturnAliases() {
  330   		errorIfDML();
  331   		return getWalker().getReturnAliases();
  332   	}
  333   
  334   	public String[][] getColumnNames() {
  335   		errorIfDML();
  336   		return getWalker().getSelectClause().getColumnNames();
  337   	}
  338   
  339   	public Set getQuerySpaces() {
  340   		return getWalker().getQuerySpaces();
  341   	}
  342   
  343   	public List list(SessionImplementor session, QueryParameters queryParameters)
  344   			throws HibernateException {
  345   		// Delegate to the QueryLoader...
  346   		errorIfDML();
  347   		QueryNode query = ( QueryNode ) sqlAst;
  348   		boolean hasLimit = queryParameters.getRowSelection() != null && queryParameters.getRowSelection().definesLimits();
  349   		boolean needsDistincting = ( query.getSelectClause().isDistinct() || hasLimit ) && containsCollectionFetches();
  350   
  351   		QueryParameters queryParametersToUse;
  352   		if ( hasLimit && containsCollectionFetches() ) {
  353   			log.warn( "firstResult/maxResults specified with collection fetch; applying in memory!" );
  354   			RowSelection selection = new RowSelection();
  355   			selection.setFetchSize( queryParameters.getRowSelection().getFetchSize() );
  356   			selection.setTimeout( queryParameters.getRowSelection().getTimeout() );
  357   			queryParametersToUse = queryParameters.createCopyUsing( selection );
  358   		}
  359   		else {
  360   			queryParametersToUse = queryParameters;
  361   		}
  362   
  363   		List results = queryLoader.list( session, queryParametersToUse );
  364   
  365   		if ( needsDistincting ) {
  366   			int includedCount = -1;
  367   			// NOTE : firstRow is zero-based
  368   			int first = !hasLimit || queryParameters.getRowSelection().getFirstRow() == null
  369   						? 0
  370   						: queryParameters.getRowSelection().getFirstRow().intValue();
  371   			int max = !hasLimit || queryParameters.getRowSelection().getMaxRows() == null
  372   						? -1
  373   						: queryParameters.getRowSelection().getMaxRows().intValue();
  374   			int size = results.size();
  375   			List tmp = new ArrayList();
  376   			IdentitySet distinction = new IdentitySet();
  377   			for ( int i = 0; i < size; i++ ) {
  378   				final Object result = results.get( i );
  379   				if ( !distinction.add( result ) ) {
  380   					continue;
  381   				}
  382   				includedCount++;
  383   				if ( includedCount < first ) {
  384   					continue;
  385   				}
  386   				tmp.add( result );
  387   				// NOTE : ( max - 1 ) because first is zero-based while max is not...
  388   				if ( max >= 0 && ( includedCount - first ) >= ( max - 1 ) ) {
  389   					break;
  390   				}
  391   			}
  392   			results = tmp;
  393   		}
  394   
  395   		return results;
  396   	}
  397   
  398   	/**
  399   	 * Return the query results as an iterator
  400   	 */
  401   	public Iterator iterate(QueryParameters queryParameters, EventSource session)
  402   			throws HibernateException {
  403   		// Delegate to the QueryLoader...
  404   		errorIfDML();
  405   		return queryLoader.iterate( queryParameters, session );
  406   	}
  407   
  408   	/**
  409   	 * Return the query results, as an instance of <tt>ScrollableResults</tt>
  410   	 */
  411   	public ScrollableResults scroll(QueryParameters queryParameters, SessionImplementor session)
  412   			throws HibernateException {
  413   		// Delegate to the QueryLoader...
  414   		errorIfDML();
  415   		return queryLoader.scroll( queryParameters, session );
  416   	}
  417   
  418   	public int executeUpdate(QueryParameters queryParameters, SessionImplementor session)
  419   			throws HibernateException {
  420   		errorIfSelect();
  421   		return statementExecutor.execute( queryParameters, session );
  422   	}
  423   
  424   	/**
  425   	 * The SQL query string to be called; implemented by all subclasses
  426   	 */
  427   	public String getSQLString() {
  428   		return sql;
  429   	}
  430   
  431   	public List collectSqlStrings() {
  432   		ArrayList list = new ArrayList();
  433   		if ( isManipulationStatement() ) {
  434   			String[] sqlStatements = statementExecutor.getSqlStatements();
  435   			for ( int i = 0; i < sqlStatements.length; i++ ) {
  436   				list.add( sqlStatements[i] );
  437   			}
  438   		}
  439   		else {
  440   			list.add( sql );
  441   		}
  442   		return list;
  443   	}
  444   
  445   	// -- Package local methods for the QueryLoader delegate --
  446   
  447   	public boolean isShallowQuery() {
  448   		return shallowQuery;
  449   	}
  450   
  451   	public String getQueryString() {
  452   		return hql;
  453   	}
  454   
  455   	public Map getEnabledFilters() {
  456   		return enabledFilters;
  457   	}
  458   
  459   	public int[] getNamedParameterLocs(String name) {
  460   		return getWalker().getNamedParameterLocations( name );
  461   	}
  462   
  463   	public boolean containsCollectionFetches() {
  464   		errorIfDML();
  465   		List collectionFetches = ( ( QueryNode ) sqlAst ).getFromClause().getCollectionFetches();
  466   		return collectionFetches != null && collectionFetches.size() > 0;
  467   	}
  468   
  469   	public boolean isManipulationStatement() {
  470   		return sqlAst.needsExecutor();
  471   	}
  472   
  473   	public void validateScrollability() throws HibernateException {
  474   		// Impl Note: allows multiple collection fetches as long as the
  475   		// entire fecthed graph still "points back" to a single
  476   		// root entity for return
  477   
  478   		errorIfDML();
  479   
  480   		QueryNode query = ( QueryNode ) sqlAst;
  481   
  482   		// If there are no collection fetches, then no further checks are needed
  483   		List collectionFetches = query.getFromClause().getCollectionFetches();
  484   		if ( collectionFetches.isEmpty() ) {
  485   			return;
  486   		}
  487   
  488   		// A shallow query is ok (although technically there should be no fetching here...)
  489   		if ( isShallowQuery() ) {
  490   			return;
  491   		}
  492   
  493   		// Otherwise, we have a non-scalar select with defined collection fetch(es).
  494   		// Make sure that there is only a single root entity in the return (no tuples)
  495   		if ( getReturnTypes().length > 1 ) {
  496   			throw new HibernateException( "cannot scroll with collection fetches and returned tuples" );
  497   		}
  498   
  499   		FromElement owner = null;
  500   		Iterator itr = query.getSelectClause().getFromElementsForLoad().iterator();
  501   		while ( itr.hasNext() ) {
  502   			// should be the first, but just to be safe...
  503   			final FromElement fromElement = ( FromElement ) itr.next();
  504   			if ( fromElement.getOrigin() == null ) {
  505   				owner = fromElement;
  506   				break;
  507   			}
  508   		}
  509   
  510   		if ( owner == null ) {
  511   			throw new HibernateException( "unable to locate collection fetch(es) owner for scrollability checks" );
  512   		}
  513   
  514   		// This is not strictly true.  We actually just need to make sure that
  515   		// it is ordered by root-entity PK and that that order-by comes before
  516   		// any non-root-entity ordering...
  517   
  518   		AST primaryOrdering = query.getOrderByClause().getFirstChild();
  519   		if ( primaryOrdering != null ) {
  520   			// TODO : this is a bit dodgy, come up with a better way to check this (plus see above comment)
  521   			String [] idColNames = owner.getQueryable().getIdentifierColumnNames();
  522   			String expectedPrimaryOrderSeq = StringHelper.join(
  523   			        ", ",
  524   			        StringHelper.qualify( owner.getTableAlias(), idColNames )
  525   			);
  526   			if (  !primaryOrdering.getText().startsWith( expectedPrimaryOrderSeq ) ) {
  527   				throw new HibernateException( "cannot scroll results with collection fetches which are not ordered primarily by the root entity's PK" );
  528   			}
  529   		}
  530   	}
  531   
  532   	private StatementExecutor buildAppropriateStatementExecutor(HqlSqlWalker walker) {
  533   		Statement statement = ( Statement ) walker.getAST();
  534   		if ( walker.getStatementType() == HqlSqlTokenTypes.DELETE ) {
  535   			FromElement fromElement = walker.getFinalFromClause().getFromElement();
  536   			Queryable persister = fromElement.getQueryable();
  537   			if ( persister.isMultiTable() ) {
  538   				return new MultiTableDeleteExecutor( walker );
  539   			}
  540   			else {
  541   				return new BasicExecutor( walker, persister );
  542   			}
  543   		}
  544   		else if ( walker.getStatementType() == HqlSqlTokenTypes.UPDATE ) {
  545   			FromElement fromElement = walker.getFinalFromClause().getFromElement();
  546   			Queryable persister = fromElement.getQueryable();
  547   			if ( persister.isMultiTable() ) {
  548   				// even here, if only properties mapped to the "base table" are referenced
  549   				// in the set and where clauses, this could be handled by the BasicDelegate.
  550   				// TODO : decide if it is better performance-wise to perform that check, or to simply use the MultiTableUpdateDelegate
  551   				return new MultiTableUpdateExecutor( walker );
  552   			}
  553   			else {
  554   				return new BasicExecutor( walker, persister );
  555   			}
  556   		}
  557   		else if ( walker.getStatementType() == HqlSqlTokenTypes.INSERT ) {
  558   			return new BasicExecutor( walker, ( ( InsertStatement ) statement ).getIntoClause().getQueryable() );
  559   		}
  560   		else {
  561   			throw new QueryException( "Unexpected statement type" );
  562   		}
  563   	}
  564   
  565   	public ParameterTranslations getParameterTranslations() {
  566   		if ( paramTranslations == null ) {
  567   			paramTranslations = new ParameterTranslationsImpl( getWalker().getParameters() );
  568   //			paramTranslations = new ParameterTranslationsImpl( collectedParameterSpecifications );
  569   		}
  570   		return paramTranslations;
  571   	}
  572   
  573   	public List getCollectedParameterSpecifications() {
  574   		return collectedParameterSpecifications;
  575   	}
  576   
  577   	public static class JavaConstantConverter implements NodeTraverser.VisitationStrategy {
  578   		private AST dotRoot;
  579   		public void visit(AST node) {
  580   			if ( dotRoot != null ) {
  581   				// we are already processing a dot-structure
  582   				if ( ASTUtil.isSubtreeChild( dotRoot, node ) ) {
  583   					// igndore it...
  584   					return;
  585   				}
  586   				else {
  587   					// we are now at a new tree level
  588   					dotRoot = null;
  589   				}
  590   			}
  591   
  592   			if ( dotRoot == null && node.getType() == HqlTokenTypes.DOT ) {
  593   				dotRoot = node;
  594   				handleDotStructure( dotRoot );
  595   			}
  596   		}
  597   		private void handleDotStructure(AST dotStructureRoot) {
  598   			String expression = ASTUtil.getPathText( dotStructureRoot );
  599   			Object constant = ReflectHelper.getConstantValue( expression );
  600   			if ( constant != null ) {
  601   				dotStructureRoot.setFirstChild( null );
  602   				dotStructureRoot.setType( HqlTokenTypes.JAVA_CONSTANT );
  603   				dotStructureRoot.setText( expression );
  604   			}
  605   		}
  606   	}
  607   }

Save This Page
Home » Hibernate-3.3.2.GA » org.hibernate » hql » ast » [javadoc | source]