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

Hibernate example source code file (SQLQueryParser.java)

This example Hibernate source code file (SQLQueryParser.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

arraylist, hibernate_placeholder_prefix, map, map, no, parametersubstitutionrecognizer, parsercontext, queryexception, queryexception, sqlqueryparser, string, string, stringbuilder, stringbuilder, util

The Hibernate SQLQueryParser.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.loader.custom.sql;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hibernate.QueryException;
import org.hibernate.engine.query.spi.ParameterParser;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.collection.SQLLoadableCollection;
import org.hibernate.persister.entity.SQLLoadable;

/**
 * @author Gavin King
 * @author Max Andersen
 * @author Steve Ebersole
 * @author Paul Benedict
 */
public class SQLQueryParser {
	private static final String HIBERNATE_PLACEHOLDER_PREFIX = "h-";
	private static final String DOMAIN_PLACEHOLDER = "h-domain";
	private static final String CATALOG_PLACEHOLDER = "h-catalog";
	private static final String SCHEMA_PLACEHOLDER = "h-schema";

	private final SessionFactoryImplementor factory;
	private final String originalQueryString;
	private final ParserContext context;

	private final Map namedParameters = new HashMap();
	private long aliasesFound = 0;

	static interface ParserContext {
		boolean isEntityAlias(String aliasName);
		SQLLoadable getEntityPersisterByAlias(String alias);
		String getEntitySuffixByAlias(String alias);
		boolean isCollectionAlias(String aliasName);
		SQLLoadableCollection getCollectionPersisterByAlias(String alias);
		String getCollectionSuffixByAlias(String alias);
		Map getPropertyResultsMapByAlias(String alias);
	}

	public SQLQueryParser(String queryString, ParserContext context, SessionFactoryImplementor factory) {
		this.originalQueryString = queryString;
		this.context = context;
		this.factory = factory;
	}

	public Map getNamedParameters() {
		return namedParameters;
	}

	public boolean queryHasAliases() {
		return aliasesFound>0;
	}

	public String process() {
		String processedSql = substituteBrackets( originalQueryString );
		processedSql = substituteParams( processedSql );
		return processedSql;
	}

	// TODO: should "record" how many properties we have reffered to - and if we 
	//       don't get'em'all we throw an exception! Way better than trial and error ;)
	private String substituteBrackets(String sqlQuery) throws QueryException {

		StringBuilder result = new StringBuilder( sqlQuery.length() + 20 );
		int left, right;

		// replace {....} with corresponding column aliases
		for ( int curr = 0; curr < sqlQuery.length(); curr = right + 1 ) {
			if ( ( left = sqlQuery.indexOf( '{', curr ) ) < 0 ) {
				// No additional open braces found in the string, append the
				// rest of the string in its entirty and quit this loop
				result.append( sqlQuery.substring( curr ) );
				break;
			}

			// apend everything up until the next encountered open brace
			result.append( sqlQuery.substring( curr, left ) );

			if ( ( right = sqlQuery.indexOf( '}', left + 1 ) ) < 0 ) {
				throw new QueryException( "Unmatched braces for alias path", sqlQuery );
			}

			final String aliasPath = sqlQuery.substring( left + 1, right );
			boolean isPlaceholder = aliasPath.startsWith( HIBERNATE_PLACEHOLDER_PREFIX );

			if ( isPlaceholder ) {
				// Domain replacement
				if ( DOMAIN_PLACEHOLDER.equals( aliasPath ) ) {
					final String catalogName = factory.getSettings().getDefaultCatalogName();
					if ( catalogName != null ) {
						result.append( catalogName );
						result.append( "." );
					}
					final String schemaName = factory.getSettings().getDefaultSchemaName();
					if ( schemaName != null ) {
						result.append( schemaName );
						result.append( "." );
					}
				}
				// Schema replacement
				else if ( SCHEMA_PLACEHOLDER.equals( aliasPath ) ) {
					final String schemaName = factory.getSettings().getDefaultSchemaName();
					if ( schemaName != null ) {
						result.append(schemaName);
						result.append(".");
					}
				} 
				// Catalog replacement
				else if ( CATALOG_PLACEHOLDER.equals( aliasPath ) ) {
					final String catalogName = factory.getSettings().getDefaultCatalogName();
					if ( catalogName != null ) {
						result.append( catalogName );
						result.append( "." );
					}
				}
				else {
					throw new QueryException( "Unknown placeholder ", aliasPath );
				}
			}
			else {
				int firstDot = aliasPath.indexOf( '.' );
				if ( firstDot == -1 ) {
					if ( context.isEntityAlias( aliasPath ) ) {
						// it is a simple table alias {foo}
						result.append( aliasPath );
						aliasesFound++;
					} 
					else {
						// passing through anything we do not know : to support jdbc escape sequences HB-898
						result.append( '{' ).append(aliasPath).append( '}' );					
					}
				}
				else {
					final String aliasName = aliasPath.substring( 0, firstDot );
					if ( context.isCollectionAlias( aliasName ) ) {
						// The current alias is referencing the collection to be eagerly fetched
						String propertyName = aliasPath.substring( firstDot + 1 );
						result.append( resolveCollectionProperties( aliasName, propertyName ) );
						aliasesFound++;
					} 
					else if ( context.isEntityAlias( aliasName ) ) {
						// it is a property reference {foo.bar}
						String propertyName = aliasPath.substring( firstDot + 1 );
						result.append( resolveProperties( aliasName, propertyName ) );
						aliasesFound++;
					}
					else {
						// passing through anything we do not know : to support jdbc escape sequences HB-898
						result.append( '{' ).append(aliasPath).append( '}' );
					}
				}
			}
		}

		// Possibly handle :something parameters for the query ?

		return result.toString();
	}	

	private String resolveCollectionProperties(
			String aliasName,
			String propertyName) {

		Map fieldResults = context.getPropertyResultsMapByAlias( aliasName );
		SQLLoadableCollection collectionPersister = context.getCollectionPersisterByAlias( aliasName );
		String collectionSuffix = context.getCollectionSuffixByAlias( aliasName );

		if ( "*".equals( propertyName ) ) {
			if( !fieldResults.isEmpty() ) {
				throw new QueryException("Using return-propertys together with * syntax is not supported.");
			}
			
			String selectFragment = collectionPersister.selectFragment( aliasName, collectionSuffix );
			aliasesFound++;
			return selectFragment 
						+ ", " 
						+ resolveProperties( aliasName, propertyName );
		}
		else if ( "element.*".equals( propertyName ) ) {
			return resolveProperties( aliasName, "*" );
		}
		else {
			String[] columnAliases;

			// Let return-propertys override whatever the persister has for aliases.
			columnAliases = ( String[] ) fieldResults.get(propertyName);
			if ( columnAliases==null ) {
				columnAliases = collectionPersister.getCollectionPropertyColumnAliases( propertyName, collectionSuffix );
			}
			
			if ( columnAliases == null || columnAliases.length == 0 ) {
				throw new QueryException(
						"No column name found for property [" + propertyName + "] for alias [" + aliasName + "]",
						originalQueryString
				);
			}
			if ( columnAliases.length != 1 ) {
				// TODO: better error message since we actually support composites if names are explicitly listed.
				throw new QueryException(
						"SQL queries only support properties mapped to a single column - property [" +
						propertyName + "] is mapped to " + columnAliases.length + " columns.",
						originalQueryString
				);
			}
			aliasesFound++;
			return columnAliases[0];
		
		}
	}
	private String resolveProperties(
			String aliasName,
	        String propertyName) {
		Map fieldResults = context.getPropertyResultsMapByAlias( aliasName );
		SQLLoadable persister = context.getEntityPersisterByAlias( aliasName );
		String suffix = context.getEntitySuffixByAlias( aliasName );

		if ( "*".equals( propertyName ) ) {
			if( !fieldResults.isEmpty() ) {
				throw new QueryException("Using return-propertys together with * syntax is not supported.");
			}			
			aliasesFound++;
			return persister.selectFragment( aliasName, suffix ) ;
		}
		else {

			String[] columnAliases;

			// Let return-propertys override whatever the persister has for aliases.
			columnAliases = (String[]) fieldResults.get( propertyName );
			if ( columnAliases == null ) {
				columnAliases = persister.getSubclassPropertyColumnAliases( propertyName, suffix );
			}

			if ( columnAliases == null || columnAliases.length == 0 ) {
				throw new QueryException(
						"No column name found for property [" + propertyName + "] for alias [" + aliasName + "]",
						originalQueryString
				);
			}
			if ( columnAliases.length != 1 ) {
				// TODO: better error message since we actually support composites if names are explicitly listed.
				throw new QueryException(
						"SQL queries only support properties mapped to a single column - property [" + propertyName + "] is mapped to " + columnAliases.length + " columns.",
						originalQueryString
				);
			}			
			aliasesFound++;
			return columnAliases[0];
		}
	}

	/**
	 * Substitues JDBC parameter placeholders (?) for all encountered
	 * parameter specifications.  It also tracks the positions of these
	 * parameter specifications within the query string.  This accounts for
	 * ordinal-params, named-params, and ejb3-positional-params.
	 *
	 * @param sqlString The query string.
	 * @return The SQL query with parameter substitution complete.
	 */
	private String substituteParams(String sqlString) {
		ParameterSubstitutionRecognizer recognizer = new ParameterSubstitutionRecognizer();
		ParameterParser.parse( sqlString, recognizer );

		namedParameters.clear();
		namedParameters.putAll( recognizer.namedParameterBindPoints );

		return recognizer.result.toString();
	}

	public static class ParameterSubstitutionRecognizer implements ParameterParser.Recognizer {
		StringBuilder result = new StringBuilder();
		Map namedParameterBindPoints = new HashMap();
		int parameterCount = 0;

		public void outParameter(int position) {
			result.append( '?' );
		}

		public void ordinalParameter(int position) {
			result.append( '?' );
		}

		public void namedParameter(String name, int position) {
			addNamedParameter( name );
			result.append( '?' );
		}

		public void jpaPositionalParameter(String name, int position) {
			namedParameter( name, position );
		}

		public void other(char character) {
			result.append( character );
		}

		private void addNamedParameter(String name) {
			Integer loc = parameterCount++;
			Object o = namedParameterBindPoints.get( name );
			if ( o == null ) {
				namedParameterBindPoints.put( name, loc );
			}
			else if ( o instanceof Integer ) {
				ArrayList list = new ArrayList( 4 );
				list.add( o );
				list.add( loc );
				namedParameterBindPoints.put( name, list );
			}
			else {
				( ( List ) o ).add( loc );
			}
		}
	}
}

Other Hibernate examples (source code examples)

Here is a short list of links related to this Hibernate SQLQueryParser.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.