/* * 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.envers.tools.query; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.envers.tools.MutableInteger; import org.hibernate.envers.tools.Pair; import org.hibernate.envers.tools.StringTools; /** * A class for incrementaly building a HQL query. * @author Adam Warski (adam at warski dot org) */ public class QueryBuilder { private final String entityName; private final String alias; /** * For use by alias generator (in case an alias is not provided by the user). */ private final MutableInteger aliasCounter; /** * For use by parameter generator, in {@link Parameters}. This counter must be * the same in all parameters and sub-queries of this query. */ private final MutableInteger paramCounter; /** * Main "where" parameters for this query. */ private final Parameters rootParameters; /** * A list of pairs (from entity name, alias name). */ private final List<Pair<String, String>> froms; /** * A list of pairs (property name, order ascending?). */ private final List<Pair<String, Boolean>> orders; /** * A list of complete projection definitions: either a sole property name, or a function(property name). */ private final List<String> projections; /** * * @param entityName Main entity which should be selected. * @param alias Alias of the entity */ public QueryBuilder(String entityName, String alias) { this(entityName, alias, new MutableInteger(), new MutableInteger()); } private QueryBuilder(String entityName, String alias, MutableInteger aliasCounter, MutableInteger paramCounter) { this.entityName = entityName; this.alias = alias; this.aliasCounter = aliasCounter; this.paramCounter = paramCounter; rootParameters = new Parameters(alias, "and", paramCounter); froms = new ArrayList<Pair<String, String>>(); orders = new ArrayList<Pair<String, Boolean>>(); projections = new ArrayList<String>(); addFrom(entityName, alias); } /** * Add an entity from which to select. * @param entityName Name of the entity from which to select. * @param alias Alias of the entity. Should be different than all other aliases. */ public void addFrom(String entityName, String alias) { froms.add(Pair.make(entityName, alias)); } private String generateAlias() { return "_e" + aliasCounter.getAndIncrease(); } /** * @return A sub-query builder for the same entity (with an auto-generated alias). The sub-query can * be later used as a value of a parameter. */ public QueryBuilder newSubQueryBuilder() { return newSubQueryBuilder(entityName, generateAlias()); } /** * @param entityName Entity name, which will be the main entity for the sub-query. * @param alias Alias of the entity, which can later be used in parameters. * @return A sub-query builder for the given entity, with the given alias. The sub-query can * be later used as a value of a parameter. */ public QueryBuilder newSubQueryBuilder(String entityName, String alias) { return new QueryBuilder(entityName, alias, aliasCounter, paramCounter); } public Parameters getRootParameters() { return rootParameters; } public void addOrder(String propertyName, boolean ascending) { orders.add(Pair.make(propertyName, ascending)); } public void addProjection(String function, String propertyName, boolean distinct) { addProjection(function, propertyName, distinct, true); } public void addProjection(String function, String propertyName, boolean distinct, boolean addAlias) { if (function == null) { projections.add((distinct ? "distinct " : "") + (addAlias ? alias+ "." : "") + propertyName); } else { projections.add(function + "(" + (distinct ? "distinct " : "") + (addAlias ? alias + "." : "") + propertyName + ")"); } } /** * Builds the given query, appending results to the given string buffer, and adding all query parameter values * that are used to the map provided. * @param sb String builder to which the query will be appended. * @param queryParamValues Map to which name and values of parameters used in the query should be added. */ public void build(StringBuilder sb, Map<String, Object> queryParamValues) { sb.append("select "); if (projections.size() > 0) { // all projections separated with commas StringTools.append(sb, projections.iterator(), ", "); } else { // all aliases separated with commas StringTools.append(sb, getAliasList().iterator(), ", "); } sb.append(" from "); // all from entities with aliases, separated with commas StringTools.append(sb, getFromList().iterator(), ", "); // where part - rootParameters if (!rootParameters.isEmpty()) { sb.append(" where "); rootParameters.build(sb, queryParamValues); } // orders if (orders.size() > 0) { sb.append(" order by "); StringTools.append(sb, getOrderList().iterator(), ", "); } } private List<String> getAliasList() { List<String> aliasList = new ArrayList<String>(); for (Pair<String, String> from : froms) { aliasList.add(from.getSecond()); } return aliasList; } private List<String> getFromList() { List<String> fromList = new ArrayList<String>(); for (Pair<String, String> from : froms) { fromList.add(from.getFirst() + " " + from.getSecond()); } return fromList; } private List<String> getOrderList() { List<String> orderList = new ArrayList<String>(); for (Pair<String, Boolean> order : orders) { orderList.add(alias + "." + order.getFirst() + " " + (order.getSecond() ? "asc" : "desc")); } return orderList; } public Query toQuery(Session session) { StringBuilder querySb = new StringBuilder(); Map<String, Object> queryParamValues = new HashMap<String, Object>(); build(querySb, queryParamValues); Query query = session.createQuery(querySb.toString()); for (Map.Entry<String, Object> paramValue : queryParamValues.entrySet()) { query.setParameter(paramValue.getKey(), paramValue.getValue()); } return query; } }