All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.sql.Template Maven / Gradle / Ivy

There is a newer version: 7.0.0.Beta1
Show newest version
/*
 * 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.sql;

import java.util.HashSet;
import java.util.StringTokenizer;

import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.dialect.function.SQLFunctionRegistry;
import org.hibernate.util.StringHelper;
import org.hibernate.sql.ordering.antlr.ColumnMapper;
import org.hibernate.sql.ordering.antlr.TranslationContext;
import org.hibernate.sql.ordering.antlr.OrderByFragmentTranslator;
import org.hibernate.engine.SessionFactoryImplementor;

/**
 * Parses SQL fragments specified in mapping documents
 *
 * @author Gavin King
 */
public final class Template {

	private static final java.util.Set KEYWORDS = new HashSet();
	private static final java.util.Set BEFORE_TABLE_KEYWORDS = new HashSet();
	private static final java.util.Set FUNCTION_KEYWORDS = new HashSet();
	static {
		KEYWORDS.add("and");
		KEYWORDS.add("or");
		KEYWORDS.add("not");
		KEYWORDS.add("like");
		KEYWORDS.add("is");
		KEYWORDS.add("in");
		KEYWORDS.add("between");
		KEYWORDS.add("null");
		KEYWORDS.add("select");
		KEYWORDS.add("distinct");
		KEYWORDS.add("from");
		KEYWORDS.add("join");
		KEYWORDS.add("inner");
		KEYWORDS.add("outer");
		KEYWORDS.add("left");
		KEYWORDS.add("right");
		KEYWORDS.add("on");
		KEYWORDS.add("where");
		KEYWORDS.add("having");
		KEYWORDS.add("group");
		KEYWORDS.add("order");
		KEYWORDS.add("by");
		KEYWORDS.add("desc");
		KEYWORDS.add("asc");
		KEYWORDS.add("limit");
		KEYWORDS.add("any");
		KEYWORDS.add("some");
		KEYWORDS.add("exists");
		KEYWORDS.add("all");
		KEYWORDS.add("union");
		KEYWORDS.add("minus");

		BEFORE_TABLE_KEYWORDS.add("from");
		BEFORE_TABLE_KEYWORDS.add("join");
		
		FUNCTION_KEYWORDS.add("as");
		FUNCTION_KEYWORDS.add("leading");
		FUNCTION_KEYWORDS.add("trailing");
		FUNCTION_KEYWORDS.add("from");
		FUNCTION_KEYWORDS.add("case");
		FUNCTION_KEYWORDS.add("when");
		FUNCTION_KEYWORDS.add("then");
		FUNCTION_KEYWORDS.add("else");
		FUNCTION_KEYWORDS.add("end");
	}

	public static final String TEMPLATE = "$PlaceHolder$";

	private Template() {}

	public static String renderWhereStringTemplate(String sqlWhereString, Dialect dialect, SQLFunctionRegistry functionRegistry) {
		return renderWhereStringTemplate(sqlWhereString, TEMPLATE, dialect, functionRegistry);
	}

	/**
	 * Same functionality as {@link #renderWhereStringTemplate(String, String, Dialect, SQLFunctionRegistry)},
	 * except that a SQLFunctionRegistry is not provided (i.e., only the dialect-defined functions are
	 * considered).  This is only intended for use by the annotations project until the
	 * many-to-many/map-key-from-target-table feature is pulled into core.
	 *
	 * @deprecated Only intended for annotations usage; use {@link #renderWhereStringTemplate(String, String, Dialect, SQLFunctionRegistry)} instead
	 */
	public static String renderWhereStringTemplate(String sqlWhereString, String placeholder, Dialect dialect) {
		return renderWhereStringTemplate( sqlWhereString, placeholder, dialect, new SQLFunctionRegistry( dialect, java.util.Collections.EMPTY_MAP ) );
	}

	/**
	 * Takes the where condition provided in the mapping attribute and interpolates the alias. 
	 * Handles subselects, quoted identifiers, quoted strings, expressions, SQL functions, 
	 * named parameters.
	 *
	 * @param sqlWhereString The string into which to interpolate the placeholder value
	 * @param placeholder The value to be interpolated into the the sqlWhereString
	 * @param dialect The dialect to apply
	 * @param functionRegistry The registry of all sql functions
	 * @return The rendered sql fragment
	 */
	public static String renderWhereStringTemplate(String sqlWhereString, String placeholder, Dialect dialect, SQLFunctionRegistry functionRegistry ) {
		//TODO: make this a bit nicer
		String symbols = new StringBuffer()
			.append("=>ORDER BY template
	 * has all column references "qualified" with a placeholder identified by {@link Template#TEMPLATE}
	 *
	 * @param orderByFragment The order-by fragment to render.
	 * @param dialect The SQL dialect being used.
	 * @param functionRegistry The SQL function registry
	 *
	 * @return The rendered ORDER BY template.
	 * 
	 * @see #renderOrderByStringTemplate(String,ColumnMapper,SessionFactoryImplementor,Dialect,SQLFunctionRegistry)
	 */
	public static String renderOrderByStringTemplate(
			String orderByFragment,
			Dialect dialect,
			SQLFunctionRegistry functionRegistry) {
		return renderOrderByStringTemplate(
				orderByFragment,
				NoOpColumnMapper.INSTANCE,
				null,
				dialect,
				functionRegistry
		);
	}

	/**
	 * Performs order-by template rendering allowing {@link ColumnMapper column mapping}.  An ORDER BY template
	 * has all column references "qualified" with a placeholder identified by {@link Template#TEMPLATE} which can later
	 * be used to easily inject the SQL alias.
	 *
	 * @param orderByFragment The order-by fragment to render.
	 * @param columnMapper The column mapping strategy to use.
	 * @param sessionFactory The session factory.
	 * @param dialect The SQL dialect being used.
	 * @param functionRegistry The SQL function registry
	 *
	 * @return The rendered ORDER BY template.
	 */
	public static String renderOrderByStringTemplate(
			String orderByFragment,
			final ColumnMapper columnMapper,
			final SessionFactoryImplementor sessionFactory,
			final Dialect dialect,
			final SQLFunctionRegistry functionRegistry) {
		TranslationContext context = new TranslationContext() {
			public SessionFactoryImplementor getSessionFactory() {
				return sessionFactory;
			}

			public Dialect getDialect() {
				return dialect;
			}

			public SQLFunctionRegistry getSqlFunctionRegistry() {
				return functionRegistry;
			}

			public ColumnMapper getColumnMapper() {
				return columnMapper;
			}
		};

		OrderByFragmentTranslator translator = new OrderByFragmentTranslator( context );
		return translator.render( orderByFragment );
	}

	private static boolean isNamedParameter(String token) {
		return token.startsWith(":");
	}

	private static boolean isFunctionOrKeyword(String lcToken, String nextToken, Dialect dialect, SQLFunctionRegistry functionRegistry) {
		return "(".equals(nextToken) ||
			KEYWORDS.contains(lcToken) ||
			isFunction(lcToken, nextToken, functionRegistry ) ||
			dialect.getKeywords().contains(lcToken) ||
			FUNCTION_KEYWORDS.contains(lcToken);
	}

	private static boolean isFunction(String lcToken, String nextToken, SQLFunctionRegistry functionRegistry) {
		// checking for "(" is currently redundant because it is checked before getting here;
		// doing the check anyhow, in case that earlier check goes away;
		if ( "(".equals( nextToken ) ) {
			return true;
		}
		SQLFunction function = functionRegistry.findSQLFunction(lcToken);
		if ( function == null ) {
			// lcToken does not refer to a function
			return false;
		}
		// if function.hasArguments() and function.hasParenthesesIfNoArguments() is true,
		// then assume that lcToken is not a function, since it is not followed by "(";
		// can't seem to use function.hasParenthesesIfNoArguments() alone because
		// function definitions may return true if "()" is optional when there are no arguments.
		return function.hasArguments() && function.hasParenthesesIfNoArguments() ? false : true;
	}

	private static boolean isIdentifier(String token, Dialect dialect) {
		return token.charAt(0)=='`' || ( //allow any identifier quoted with backtick
			Character.isLetter( token.charAt(0) ) && //only recognizes identifiers beginning with a letter
			token.indexOf('.') < 0
		);
	}

	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy