alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Hibernate example source code file (WhereParser.java)

This example Hibernate source code file (WhereParser.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Hibernate tags/keywords

hashset, linkedlist, mappingexception, note, pathexpressionparser, queryexception, queryexception, querytranslatorimpl, querytranslatorimpl, set, string, string, stringbuffer, stringbuffer, util

The Hibernate WhereParser.java source code

/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
 *
 * 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.hql.internal.classic;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.hibernate.MappingException;
import org.hibernate.QueryException;
import org.hibernate.engine.internal.JoinSequence;
import org.hibernate.hql.spi.QueryTranslator;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.persister.collection.CollectionPropertyNames;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.InFragment;
import org.hibernate.type.EntityType;
import org.hibernate.type.LiteralType;
import org.hibernate.type.Type;

/**
 * Parses the where clause of a hibernate query and translates it to an
 * SQL where clause.
 */

// We should reengineer this class so that, rather than the current ad -
// hoc linear approach to processing a stream of tokens, we instead
// build up a tree of expressions.

// We would probably refactor to have LogicParser (builds a tree of simple
// expressions connected by and, or, not), ExpressionParser (translates
// from OO terms like foo, foo.Bar, foo.Bar.Baz to SQL terms like
// FOOS.ID, FOOS.BAR_ID, etc) and PathExpressionParser (which does much
// the same thing it does now)

public class WhereParser implements Parser {

	private final PathExpressionParser pathExpressionParser;

	{
		pathExpressionParser = new PathExpressionParser();
		pathExpressionParser.setUseThetaStyleJoin( true ); //Need this, since join condition can appear inside parens!
	}

	private static final Set EXPRESSION_TERMINATORS = new HashSet();   //tokens that close a sub expression
	private static final Set EXPRESSION_OPENERS = new HashSet();       //tokens that open a sub expression
	private static final Set BOOLEAN_OPERATORS = new HashSet();        //tokens that would indicate a sub expression is a boolean expression
	private static final Map NEGATIONS = new HashMap();

	static {
		EXPRESSION_TERMINATORS.add( "and" );
		EXPRESSION_TERMINATORS.add( "or" );
		EXPRESSION_TERMINATORS.add( ")" );
		//expressionTerminators.add(","); // deliberately excluded

		EXPRESSION_OPENERS.add( "and" );
		EXPRESSION_OPENERS.add( "or" );
		EXPRESSION_OPENERS.add( "(" );
		//expressionOpeners.add(","); // deliberately excluded

		BOOLEAN_OPERATORS.add( "<" );
		BOOLEAN_OPERATORS.add( "=" );
		BOOLEAN_OPERATORS.add( ">" );
		BOOLEAN_OPERATORS.add( "#" );
		BOOLEAN_OPERATORS.add( "~" );
		BOOLEAN_OPERATORS.add( "like" );
		BOOLEAN_OPERATORS.add( "ilike" );
		BOOLEAN_OPERATORS.add( "regexp" );
		BOOLEAN_OPERATORS.add( "rlike" );
		BOOLEAN_OPERATORS.add( "is" );
		BOOLEAN_OPERATORS.add( "in" );
		BOOLEAN_OPERATORS.add( "any" );
		BOOLEAN_OPERATORS.add( "some" );
		BOOLEAN_OPERATORS.add( "all" );
		BOOLEAN_OPERATORS.add( "exists" );
		BOOLEAN_OPERATORS.add( "between" );
		BOOLEAN_OPERATORS.add( "<=" );
		BOOLEAN_OPERATORS.add( ">=" );
		BOOLEAN_OPERATORS.add( "=>" );
		BOOLEAN_OPERATORS.add( "=<" );
		BOOLEAN_OPERATORS.add( "!=" );
		BOOLEAN_OPERATORS.add( "<>" );
		BOOLEAN_OPERATORS.add( "!#" );
		BOOLEAN_OPERATORS.add( "!~" );
		BOOLEAN_OPERATORS.add( "!<" );
		BOOLEAN_OPERATORS.add( "!>" );
		BOOLEAN_OPERATORS.add( "is not" );
		BOOLEAN_OPERATORS.add( "not like" );
		BOOLEAN_OPERATORS.add( "not ilike" );
		BOOLEAN_OPERATORS.add( "not regexp" );
		BOOLEAN_OPERATORS.add( "not rlike" );
		BOOLEAN_OPERATORS.add( "not in" );
		BOOLEAN_OPERATORS.add( "not between" );
		BOOLEAN_OPERATORS.add( "not exists" );

		NEGATIONS.put( "and", "or" );
		NEGATIONS.put( "or", "and" );
		NEGATIONS.put( "<", ">=" );
		NEGATIONS.put( "=", "<>" );
		NEGATIONS.put( ">", "<=" );
		NEGATIONS.put( "#", "!#" );
		NEGATIONS.put( "~", "!~" );
		NEGATIONS.put( "like", "not like" );
		NEGATIONS.put( "ilike", "not ilike" );
		NEGATIONS.put( "regexp", "not regexp" );
		NEGATIONS.put( "rlike", "not rlike" );
		NEGATIONS.put( "is", "is not" );
		NEGATIONS.put( "in", "not in" );
		NEGATIONS.put( "exists", "not exists" );
		NEGATIONS.put( "between", "not between" );
		NEGATIONS.put( "<=", ">" );
		NEGATIONS.put( ">=", "<" );
		NEGATIONS.put( "=>", "<" );
		NEGATIONS.put( "=<", ">" );
		NEGATIONS.put( "!=", "=" );
		NEGATIONS.put( "<>", "=" );
		NEGATIONS.put( "!#", "#" );
		NEGATIONS.put( "!~", "~" );
		NEGATIONS.put( "!<", "<" );
		NEGATIONS.put( "!>", ">" );
		NEGATIONS.put( "is not", "is" );
		NEGATIONS.put( "not like", "like" );
		NEGATIONS.put( "not ilike", "ilike" );
		NEGATIONS.put( "not regexp", "regexp" );
		NEGATIONS.put( "not rlike", "rlike" );
		NEGATIONS.put( "not in", "in" );
		NEGATIONS.put( "not between", "between" );
		NEGATIONS.put( "not exists", "exists" );

	}
	// Handles things like:
	// a and b or c
	// a and ( b or c )
	// not a and not b
	// not ( a and b )
	// x between y and z            (overloaded "and")
	// x in ( a, b, c )             (overloaded brackets)
	// not not a
	// a is not null                (overloaded "not")
	// etc......
	// and expressions like
	// foo = bar                    (maps to: foo.id = bar.id)
	// foo.Bar = 'foo'              (maps to: foo.bar = 'foo')
	// foo.Bar.Baz = 1.0            (maps to: foo.bar = bar.id and bar.baz = 1.0)
	// 1.0 = foo.Bar.Baz            (maps to: bar.baz = 1.0 and foo.Bar = bar.id)
	// foo.Bar.Baz = a.B.C          (maps to: bar.Baz = b.C and foo.Bar = bar.id and a.B = b.id)
	// foo.Bar.Baz + a.B.C          (maps to: bar.Baz + b.C and foo.Bar = bar.id and a.B = b.id)
	// ( foo.Bar.Baz + 1.0 ) < 2.0  (maps to: ( bar.Baz + 1.0 ) < 2.0 and foo.Bar = bar.id)

	private boolean betweenSpecialCase = false;       //Inside a BETWEEN ... AND ... expression
	private boolean negated = false;

	private boolean inSubselect = false;
	private int bracketsSinceSelect = 0;
	private StringBuffer subselect;

	private boolean expectingPathContinuation = false;
	private int expectingIndex = 0;

	// The following variables are stacks that keep information about each subexpression
	// in the list of nested subexpressions we are currently processing.

	private LinkedList<Boolean> nots = new LinkedList();           //were an odd or even number of NOTs encountered
	private LinkedList joins = new LinkedList();          //the join string built up by compound paths inside this expression
	private LinkedList<Boolean> booleanTests = new LinkedList();   //a flag indicating if the subexpression is known to be boolean

	private String getElementName(PathExpressionParser.CollectionElement element, QueryTranslatorImpl q) throws QueryException {
		String name;
		if ( element.isOneToMany ) {
			name = element.alias;
		}
		else {
			Type type = element.elementType;
			if ( type.isEntityType() ) { //ie. a many-to-many
				String entityName = ( ( EntityType ) type ).getAssociatedEntityName();
				name = pathExpressionParser.continueFromManyToMany( entityName, element.elementColumns, q );
			}
			else {
				throw new QueryException( "illegally dereferenced collection element" );
			}
		}
		return name;
	}

	public void token(String token, QueryTranslatorImpl q) throws QueryException {

		String lcToken = token.toLowerCase();

		//Cope with [,]
		if ( token.equals( "[" ) && !expectingPathContinuation ) {
			expectingPathContinuation = false;
			if ( expectingIndex == 0 ) throw new QueryException( "unexpected [" );
			return;
		}
		else if ( token.equals( "]" ) ) {
			expectingIndex--;
			expectingPathContinuation = true;
			return;
		}

		//Cope with a continued path expression (ie. ].baz)
		if ( expectingPathContinuation ) {
			boolean pathExpressionContinuesFurther = continuePathExpression( token, q );
			if ( pathExpressionContinuesFurther ) return; //NOTE: early return
		}

		//Cope with a subselect
		if ( !inSubselect && ( lcToken.equals( "select" ) || lcToken.equals( "from" ) ) ) {
			inSubselect = true;
			subselect = new StringBuffer( 20 );
		}
		if ( inSubselect && token.equals( ")" ) ) {
			bracketsSinceSelect--;

			if ( bracketsSinceSelect == -1 ) {
				QueryTranslatorImpl subq = new QueryTranslatorImpl(
				        subselect.toString(),
						q.getEnabledFilters(),
						q.getFactory()
				);
				try {
					subq.compile( q );
				}
				catch ( MappingException me ) {
					throw new QueryException( "MappingException occurred compiling subquery", me );
				}
				appendToken( q, subq.getSQLString() );
				inSubselect = false;
				bracketsSinceSelect = 0;
			}
		}
		if ( inSubselect ) {
			if ( token.equals( "(" ) ) bracketsSinceSelect++;
			subselect.append( token ).append( ' ' );
			return;
		}

		//Cope with special cases of AND, NOT, ()
		specialCasesBefore( lcToken );

		//Close extra brackets we opened
		if ( !betweenSpecialCase && EXPRESSION_TERMINATORS.contains( lcToken ) ) {
			closeExpression( q, lcToken );
		}

		//take note when this is a boolean expression
		if ( BOOLEAN_OPERATORS.contains( lcToken ) ) {
			booleanTests.removeLast();
			booleanTests.addLast( Boolean.TRUE );
		}

		if ( lcToken.equals( "not" ) ) {
			nots.addLast(  !(  nots.removeLast() ) );
			negated = !negated;
			return; //NOTE: early return
		}

		//process a token, mapping OO path expressions to SQL expressions
		doToken( token, q );

		//Open any extra brackets we might need.
		if ( !betweenSpecialCase && EXPRESSION_OPENERS.contains( lcToken ) ) {
			openExpression( q, lcToken );
		}

		//Cope with special cases of AND, NOT, )
		specialCasesAfter( lcToken );

	}

	public void start(QueryTranslatorImpl q) throws QueryException {
		token( "(", q );
	}

	public void end(QueryTranslatorImpl q) throws QueryException {
		if ( expectingPathContinuation ) {
			expectingPathContinuation = false;
			PathExpressionParser.CollectionElement element = pathExpressionParser.lastCollectionElement();
			if ( element.elementColumns.length != 1 ) throw new QueryException( "path expression ended in composite collection element" );
			appendToken( q, element.elementColumns[0] );
			addToCurrentJoin( element );
		}
		token( ")", q );
	}

	private void closeExpression(QueryTranslatorImpl q, String lcToken) {
		if ( booleanTests.removeLast() ) { //it was a boolean expression

			if ( booleanTests.size() > 0 ) {
				// the next one up must also be
				booleanTests.removeLast();
				booleanTests.addLast( Boolean.TRUE );
			}

			// Add any joins
			appendToken( q, ( joins.removeLast() ).toString() );

		}
		else {
			StringBuffer join = ( StringBuffer ) joins.removeLast();
			( ( StringBuffer ) joins.getLast() ).append( join.toString() );
		}

		if ( nots.removeLast() ) negated = !negated;

		if ( !")".equals( lcToken ) ) appendToken( q, ")" );
	}

	private void openExpression(QueryTranslatorImpl q, String lcToken) {
		nots.addLast( Boolean.FALSE );
		booleanTests.addLast( Boolean.FALSE );
		joins.addLast( new StringBuffer() );
		if ( !"(".equals( lcToken ) ) appendToken( q, "(" );
	}

	private void preprocess(String token, QueryTranslatorImpl q) throws QueryException {
		// ugly hack for cases like "elements(foo.bar.collection)"
		// (multi-part path expression ending in elements or indices)
		String[] tokens = StringHelper.split( ".", token, true );
		if (
				tokens.length > 5 &&
				( CollectionPropertyNames.COLLECTION_ELEMENTS.equals( tokens[tokens.length - 1] )
				|| CollectionPropertyNames.COLLECTION_INDICES.equals( tokens[tokens.length - 1] ) )
		) {
			pathExpressionParser.start( q );
			for ( int i = 0; i < tokens.length - 3; i++ ) {
				pathExpressionParser.token( tokens[i], q );
			}
			pathExpressionParser.token( null, q );
			pathExpressionParser.end( q );
			addJoin( pathExpressionParser.getWhereJoin(), q );
			pathExpressionParser.ignoreInitialJoin();
		}
	}

	private void doPathExpression(String token, QueryTranslatorImpl q) throws QueryException {

		preprocess( token, q );

		StringTokenizer tokens = new StringTokenizer( token, ".", true );
		pathExpressionParser.start( q );
		while ( tokens.hasMoreTokens() ) {
			pathExpressionParser.token( tokens.nextToken(), q );
		}
		pathExpressionParser.end( q );
		if ( pathExpressionParser.isCollectionValued() ) {
			openExpression( q, "" );
			appendToken( q, pathExpressionParser.getCollectionSubquery( q.getEnabledFilters() ) );
			closeExpression( q, "" );
			// this is ugly here, but needed because its a subquery
			q.addQuerySpaces( q.getCollectionPersister( pathExpressionParser.getCollectionRole() ).getCollectionSpaces() );
		}
		else {
			if ( pathExpressionParser.isExpectingCollectionIndex() ) {
				expectingIndex++;
			}
			else {
				addJoin( pathExpressionParser.getWhereJoin(), q );
				appendToken( q, pathExpressionParser.getWhereColumn() );
			}
		}
	}

	private void addJoin(JoinSequence joinSequence, QueryTranslatorImpl q) throws QueryException {
		//JoinFragment fromClause = q.createJoinFragment(true);
		//fromClause.addJoins( join.toJoinFragment().toFromFragmentString(), StringHelper.EMPTY_STRING );
		q.addFromJoinOnly( pathExpressionParser.getName(), joinSequence );
		try {
			addToCurrentJoin( joinSequence.toJoinFragment( q.getEnabledFilters(), true ).toWhereFragmentString() );
		}
		catch ( MappingException me ) {
			throw new QueryException( me );
		}
	}

	private void doToken(String token, QueryTranslatorImpl q) throws QueryException {
		if ( q.isName( StringHelper.root( token ) ) ) { //path expression
			doPathExpression( q.unalias( token ), q );
		}
		else if ( token.startsWith( ParserHelper.HQL_VARIABLE_PREFIX ) ) { //named query parameter
			q.addNamedParameter( token.substring( 1 ) );
			appendToken( q, "?" );
		}
		else {
			Queryable persister = q.getEntityPersisterUsingImports( token );
			if ( persister != null ) { // the name of a class
				final String discrim = persister.getDiscriminatorSQLValue();
				if ( InFragment.NULL.equals(discrim) || InFragment.NOT_NULL.equals(discrim) ) {
					throw new QueryException( "subclass test not allowed for null or not null discriminator" );
				}
				else {
					appendToken( q, discrim );
				}
			}
			else {
				Object constant;
				if (
						token.indexOf( '.' ) > -1 &&
						( constant = ReflectHelper.getConstantValue( token ) ) != null
				) {
					Type type;
					try {
						type = q.getFactory().getTypeResolver().heuristicType( constant.getClass().getName() );
					}
					catch ( MappingException me ) {
						throw new QueryException( me );
					}
					if ( type == null ) throw new QueryException( QueryTranslator.ERROR_CANNOT_DETERMINE_TYPE + token );
					try {
						appendToken( q, ( ( LiteralType ) type ).objectToSQLString( constant, q.getFactory().getDialect() ) );
					}
					catch ( Exception e ) {
						throw new QueryException( QueryTranslator.ERROR_CANNOT_FORMAT_LITERAL + token, e );
					}
				}
				else { //anything else

					String negatedToken = negated ? ( String ) NEGATIONS.get( token.toLowerCase() ) : null;
					if ( negatedToken != null && ( !betweenSpecialCase || !"or".equals( negatedToken ) ) ) {
						appendToken( q, negatedToken );
					}
					else {
						appendToken( q, token );
					}
				}
			}
		}
	}

	private void addToCurrentJoin(String sql) {
		( ( StringBuffer ) joins.getLast() ).append( sql );
	}

	private void addToCurrentJoin(PathExpressionParser.CollectionElement ce)
			throws QueryException {
		try {
			addToCurrentJoin( ce.joinSequence.toJoinFragment().toWhereFragmentString() + ce.indexValue.toString() );
		}
		catch ( MappingException me ) {
			throw new QueryException( me );
		}
	}

	private void specialCasesBefore(String lcToken) {
		if ( lcToken.equals( "between" ) || lcToken.equals( "not between" ) ) {
			betweenSpecialCase = true;
		}
	}

	private void specialCasesAfter(String lcToken) {
		if ( betweenSpecialCase && lcToken.equals( "and" ) ) {
			betweenSpecialCase = false;
		}
	}

	void appendToken(QueryTranslatorImpl q, String token) {
		if ( expectingIndex > 0 ) {
			pathExpressionParser.setLastCollectionElementIndexValue( token );
		}
		else {
			q.appendWhereToken( token );
		}
	}

	private boolean continuePathExpression(String token, QueryTranslatorImpl q) throws QueryException {

		expectingPathContinuation = false;

		PathExpressionParser.CollectionElement element = pathExpressionParser.lastCollectionElement();

		if ( token.startsWith( "." ) ) { // the path expression continues after a ]

			doPathExpression( getElementName( element, q ) + token, q ); // careful with this!

			addToCurrentJoin( element );
			return true; //NOTE: EARLY EXIT!

		}

		else { // the path expression ends at the ]
			if ( element.elementColumns.length != 1 ) {
				throw new QueryException( "path expression ended in composite collection element" );
			}
			appendToken( q, element.elementColumns[0] );
			addToCurrentJoin( element );
			return false;
		}
	}
}

Other Hibernate examples (source code examples)

Here is a short list of links related to this Hibernate WhereParser.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.