Java tutorial
/* Copyright (C) 2011 SpringSource * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.grails.datastore.mapping.query.jpa; import org.grails.datastore.mapping.model.PersistentEntity; import org.grails.datastore.mapping.model.PersistentProperty; import org.grails.datastore.mapping.model.types.Association; import org.grails.datastore.mapping.model.types.ToOne; import org.grails.datastore.mapping.query.AssociationQuery; import org.grails.datastore.mapping.query.Query; import org.grails.datastore.mapping.query.api.AssociationCriteria; import org.grails.datastore.mapping.query.api.QueryableCriteria; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.GenericConversionService; import org.springframework.dao.InvalidDataAccessResourceUsageException; import java.util.*; /** * Builds JPA 1.0 String-based queries from the Query model * * @author Graeme Rocher * @since 1.0 */ public class JpaQueryBuilder { private static final String DISTINCT_CLAUSE = "DISTINCT "; private static final String SELECT_CLAUSE = "SELECT "; private static final String AS_CLAUSE = " AS "; private static final String FROM_CLAUSE = " FROM "; private static final String ORDER_BY_CLAUSE = " ORDER BY "; private static final String WHERE_CLAUSE = " WHERE "; private static final char COMMA = ','; private static final char CLOSE_BRACKET = ')'; private static final char OPEN_BRACKET = '('; private static final char SPACE = ' '; private static final char QUESTIONMARK = '?'; private static final char DOT = '.'; public static final String NOT_CLAUSE = " NOT"; public static final String LOGICAL_AND = " AND "; public static final String UPDATE_CLAUSE = "UPDATE "; public static final String DELETE_CLAUSE = "DELETE "; public static final String LOGICAL_OR = " OR "; private static final Map<Class, QueryHandler> queryHandlers = new HashMap<Class, QueryHandler>(); private PersistentEntity entity; private Query.Junction criteria; private Query.ProjectionList projectionList = new Query.ProjectionList(); private List<Query.Order> orders = Collections.emptyList(); private String logicalName; private ConversionService conversionService = new GenericConversionService(); private boolean hibernateCompatible; public JpaQueryBuilder(QueryableCriteria criteria) { this(criteria.getPersistentEntity(), criteria.getCriteria()); } public JpaQueryBuilder(PersistentEntity entity, List<Query.Criterion> criteria) { this(entity, new Query.Conjunction(criteria)); } public JpaQueryBuilder(PersistentEntity entity, List<Query.Criterion> criteria, Query.ProjectionList projectionList) { this(entity, new Query.Conjunction(criteria), projectionList); } public JpaQueryBuilder(PersistentEntity entity, List<Query.Criterion> criteria, Query.ProjectionList projectionList, List<Query.Order> orders) { this(entity, new Query.Conjunction(criteria), projectionList, orders); } public JpaQueryBuilder(PersistentEntity entity, Query.Junction criteria) { this.entity = entity; this.criteria = criteria; this.logicalName = entity.getDecapitalizedName(); } public JpaQueryBuilder(PersistentEntity entity, Query.Junction criteria, Query.ProjectionList projectionList) { this(entity, criteria); this.projectionList = projectionList; } public JpaQueryBuilder(PersistentEntity entity, Query.Junction criteria, Query.ProjectionList projectionList, List<Query.Order> orders) { this(entity, criteria, projectionList); this.orders = orders; } public void setHibernateCompatible(boolean hibernateCompatible) { this.hibernateCompatible = hibernateCompatible; } public void setConversionService(ConversionService conversionService) { this.conversionService = conversionService; } /** * Builds an UPDATE statement. * * @param propertiesToUpdate THe properties to update * @return The JpaQueryInfo object */ public JpaQueryInfo buildUpdate(Map<String, Object> propertiesToUpdate) { if (propertiesToUpdate.isEmpty()) { throw new InvalidDataAccessResourceUsageException("No properties specified to update"); } StringBuilder queryString = new StringBuilder(UPDATE_CLAUSE).append(entity.getName()).append(SPACE) .append(logicalName); List parameters = new ArrayList(); buildUpdateStatement(queryString, propertiesToUpdate, parameters, hibernateCompatible); StringBuilder whereClause = new StringBuilder(); buildWhereClause(entity, criteria, queryString, whereClause, logicalName, false, parameters); return new JpaQueryInfo(queryString.toString(), parameters); } /** * Builds a DELETE statement * * @return The JpaQueryInfo */ public JpaQueryInfo buildDelete() { StringBuilder queryString = new StringBuilder(DELETE_CLAUSE).append(entity.getName()).append(SPACE) .append(logicalName); StringBuilder whereClause = new StringBuilder(); List parameters = buildWhereClause(entity, criteria, queryString, whereClause, logicalName, false); return new JpaQueryInfo(queryString.toString(), parameters); } /** * Builds SELECT statement * * @return The JpaQueryInfo */ public JpaQueryInfo buildSelect() { StringBuilder queryString = new StringBuilder(SELECT_CLAUSE); buildSelectClause(queryString); StringBuilder whereClause = new StringBuilder(); List parameters = null; if (!criteria.isEmpty()) { parameters = buildWhereClause(entity, criteria, queryString, whereClause, logicalName, true); } appendOrder(queryString, logicalName); return new JpaQueryInfo(queryString.toString(), parameters); } private void buildSelectClause(StringBuilder queryString) { if (projectionList.isEmpty()) { queryString.append(DISTINCT_CLAUSE).append(logicalName); } else { for (Iterator i = projectionList.getProjectionList().iterator(); i.hasNext();) { Query.Projection projection = (Query.Projection) i.next(); if (projection instanceof Query.CountProjection) { queryString.append("COUNT(").append(logicalName).append(CLOSE_BRACKET); } else if (projection instanceof Query.IdProjection) { queryString.append(logicalName).append(DOT).append(entity.getIdentity().getName()); } else if (projection instanceof Query.PropertyProjection) { Query.PropertyProjection pp = (Query.PropertyProjection) projection; if (projection instanceof Query.AvgProjection) { queryString.append("AVG(").append(logicalName).append(DOT).append(pp.getPropertyName()) .append(CLOSE_BRACKET); } else if (projection instanceof Query.SumProjection) { queryString.append("SUM(").append(logicalName).append(DOT).append(pp.getPropertyName()) .append(CLOSE_BRACKET); } else if (projection instanceof Query.MinProjection) { queryString.append("MIN(").append(logicalName).append(DOT).append(pp.getPropertyName()) .append(CLOSE_BRACKET); } else if (projection instanceof Query.MaxProjection) { queryString.append("MAX(").append(logicalName).append(DOT).append(pp.getPropertyName()) .append(CLOSE_BRACKET); } else if (projection instanceof Query.CountDistinctProjection) { queryString.append("COUNT(DISTINCT ").append(logicalName).append(DOT) .append(pp.getPropertyName()).append(CLOSE_BRACKET); } else { queryString.append(logicalName).append(DOT).append(pp.getPropertyName()); } } if (i.hasNext()) { queryString.append(COMMA); } } } queryString.append(FROM_CLAUSE).append(entity.getName()).append(AS_CLAUSE).append(logicalName); } static public int appendCriteriaForOperator(StringBuilder q, String logicalName, final String name, int position, String operator, boolean hibernateCompatible) { q.append(logicalName).append(DOT).append(name).append(operator).append(QUESTIONMARK); if (!hibernateCompatible) q.append(++position); return position; } static { queryHandlers.put(AssociationQuery.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { if (!allowJoins) { throw new InvalidDataAccessResourceUsageException( "Joins cannot be used in a DELETE or UPDATE operation"); } AssociationQuery aq = (AssociationQuery) criterion; final Association<?> association = aq.getAssociation(); Query.Junction associationCriteria = aq.getCriteria(); List<Query.Criterion> associationCriteriaList = associationCriteria.getCriteria(); return handleAssociationCriteria(q, whereClause, logicalName, position, parameters, conversionService, allowJoins, association, associationCriteria, associationCriteriaList, hibernateCompatible); } }); queryHandlers.put(Query.Negation.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { whereClause.append(NOT_CLAUSE).append(OPEN_BRACKET); final Query.Negation negation = (Query.Negation) criterion; position = buildWhereClauseForCriterion(entity, negation, q, whereClause, logicalName, negation.getCriteria(), position, parameters, conversionService, allowJoins, hibernateCompatible); whereClause.append(CLOSE_BRACKET); return position; } }); queryHandlers.put(Query.Conjunction.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { whereClause.append(OPEN_BRACKET); final Query.Conjunction conjunction = (Query.Conjunction) criterion; position = buildWhereClauseForCriterion(entity, conjunction, q, whereClause, logicalName, conjunction.getCriteria(), position, parameters, conversionService, allowJoins, hibernateCompatible); whereClause.append(CLOSE_BRACKET); return position; } }); queryHandlers.put(Query.Disjunction.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { whereClause.append(OPEN_BRACKET); final Query.Disjunction disjunction = (Query.Disjunction) criterion; position = buildWhereClauseForCriterion(entity, disjunction, q, whereClause, logicalName, disjunction.getCriteria(), position, parameters, conversionService, allowJoins, hibernateCompatible); whereClause.append(CLOSE_BRACKET); return position; } }); queryHandlers.put(Query.Equals.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.Equals eq = (Query.Equals) criterion; final String name = eq.getProperty(); PersistentProperty prop = validateProperty(entity, name, Query.Equals.class); Class propType = prop.getType(); position = appendCriteriaForOperator(whereClause, logicalName, name, position, "=", hibernateCompatible); parameters.add(conversionService.convert(eq.getValue(), propType)); return position; } }); queryHandlers.put(Query.EqualsProperty.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.EqualsProperty eq = (Query.EqualsProperty) criterion; final String propertyName = eq.getProperty(); String otherProperty = eq.getOtherProperty(); validateProperty(entity, propertyName, Query.EqualsProperty.class); validateProperty(entity, otherProperty, Query.EqualsProperty.class); appendPropertyComparison(whereClause, logicalName, propertyName, otherProperty, "="); return position; } }); queryHandlers.put(Query.NotEqualsProperty.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.PropertyComparisonCriterion eq = (Query.PropertyComparisonCriterion) criterion; final String propertyName = eq.getProperty(); String otherProperty = eq.getOtherProperty(); validateProperty(entity, propertyName, Query.NotEqualsProperty.class); validateProperty(entity, otherProperty, Query.NotEqualsProperty.class); appendPropertyComparison(whereClause, logicalName, propertyName, otherProperty, "!="); return position; } }); queryHandlers.put(Query.GreaterThanProperty.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.PropertyComparisonCriterion eq = (Query.PropertyComparisonCriterion) criterion; final String propertyName = eq.getProperty(); String otherProperty = eq.getOtherProperty(); validateProperty(entity, propertyName, Query.GreaterThanProperty.class); validateProperty(entity, otherProperty, Query.GreaterThanProperty.class); appendPropertyComparison(whereClause, logicalName, propertyName, otherProperty, ">"); return position; } }); queryHandlers.put(Query.GreaterThanEqualsProperty.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.PropertyComparisonCriterion eq = (Query.PropertyComparisonCriterion) criterion; final String propertyName = eq.getProperty(); String otherProperty = eq.getOtherProperty(); validateProperty(entity, propertyName, Query.GreaterThanEqualsProperty.class); validateProperty(entity, otherProperty, Query.GreaterThanEqualsProperty.class); appendPropertyComparison(whereClause, logicalName, propertyName, otherProperty, ">="); return position; } }); queryHandlers.put(Query.LessThanProperty.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.PropertyComparisonCriterion eq = (Query.PropertyComparisonCriterion) criterion; final String propertyName = eq.getProperty(); String otherProperty = eq.getOtherProperty(); validateProperty(entity, propertyName, Query.LessThanProperty.class); validateProperty(entity, otherProperty, Query.LessThanProperty.class); appendPropertyComparison(whereClause, logicalName, propertyName, otherProperty, "<"); return position; } }); queryHandlers.put(Query.LessThanEqualsProperty.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.PropertyComparisonCriterion eq = (Query.PropertyComparisonCriterion) criterion; final String propertyName = eq.getProperty(); String otherProperty = eq.getOtherProperty(); validateProperty(entity, propertyName, Query.LessThanEqualsProperty.class); validateProperty(entity, otherProperty, Query.LessThanEqualsProperty.class); appendPropertyComparison(whereClause, logicalName, propertyName, otherProperty, "<="); return position; } }); queryHandlers.put(Query.IsNull.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.IsNull isNull = (Query.IsNull) criterion; final String name = isNull.getProperty(); validateProperty(entity, name, Query.IsNull.class); whereClause.append(logicalName).append(DOT).append(name).append(" IS NULL "); return position; } }); queryHandlers.put(Query.IsNotNull.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.IsNotNull isNotNull = (Query.IsNotNull) criterion; final String name = isNotNull.getProperty(); validateProperty(entity, name, Query.IsNotNull.class); whereClause.append(logicalName).append(DOT).append(name).append(" IS NOT NULL "); return position; } }); queryHandlers.put(Query.IsEmpty.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.IsEmpty isEmpty = (Query.IsEmpty) criterion; final String name = isEmpty.getProperty(); validateProperty(entity, name, Query.IsEmpty.class); whereClause.append(logicalName).append(DOT).append(name).append(" IS EMPTY "); return position; } }); queryHandlers.put(Query.IsNotEmpty.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.IsNotEmpty isNotEmpty = (Query.IsNotEmpty) criterion; final String name = isNotEmpty.getProperty(); validateProperty(entity, name, Query.IsNotEmpty.class); whereClause.append(logicalName).append(DOT).append(name).append(" IS EMPTY "); return position; } }); queryHandlers.put(Query.IsNotNull.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.IsNotNull isNotNull = (Query.IsNotNull) criterion; final String name = isNotNull.getProperty(); validateProperty(entity, name, Query.IsNotNull.class); whereClause.append(logicalName).append(DOT).append(name).append(" IS NOT NULL "); return position; } }); queryHandlers.put(Query.IdEquals.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.IdEquals eq = (Query.IdEquals) criterion; PersistentProperty prop = entity.getIdentity(); Class propType = prop.getType(); position = appendCriteriaForOperator(whereClause, logicalName, prop.getName(), position, "=", hibernateCompatible); parameters.add(conversionService.convert(eq.getValue(), propType)); return position; } }); queryHandlers.put(Query.NotEquals.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.NotEquals eq = (Query.NotEquals) criterion; final String name = eq.getProperty(); PersistentProperty prop = validateProperty(entity, name, Query.NotEquals.class); Class propType = prop.getType(); position = appendCriteriaForOperator(whereClause, logicalName, name, position, " != ", hibernateCompatible); parameters.add(conversionService.convert(eq.getValue(), propType)); return position; } }); queryHandlers.put(Query.GreaterThan.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.GreaterThan eq = (Query.GreaterThan) criterion; final String name = eq.getProperty(); PersistentProperty prop = validateProperty(entity, name, Query.GreaterThan.class); Class propType = prop.getType(); position = appendCriteriaForOperator(whereClause, logicalName, name, position, " > ", hibernateCompatible); parameters.add(conversionService.convert(eq.getValue(), propType)); return position; } }); queryHandlers.put(Query.LessThanEquals.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.LessThanEquals eq = (Query.LessThanEquals) criterion; final String name = eq.getProperty(); PersistentProperty prop = validateProperty(entity, name, Query.LessThanEquals.class); Class propType = prop.getType(); position = appendCriteriaForOperator(whereClause, logicalName, name, position, " <= ", hibernateCompatible); parameters.add(conversionService.convert(eq.getValue(), propType)); return position; } }); queryHandlers.put(Query.GreaterThanEquals.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.GreaterThanEquals eq = (Query.GreaterThanEquals) criterion; final String name = eq.getProperty(); PersistentProperty prop = validateProperty(entity, name, Query.GreaterThanEquals.class); Class propType = prop.getType(); position = appendCriteriaForOperator(whereClause, logicalName, name, position, " >= ", hibernateCompatible); parameters.add(conversionService.convert(eq.getValue(), propType)); return position; } }); queryHandlers.put(Query.Between.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.Between between = (Query.Between) criterion; final Object from = between.getFrom(); final Object to = between.getTo(); final String name = between.getProperty(); PersistentProperty prop = validateProperty(entity, name, Query.Between.class); Class propType = prop.getType(); final String qualifiedName = logicalName + DOT + name; whereClause.append(OPEN_BRACKET).append(qualifiedName).append(" >= ").append(QUESTIONMARK); if (!hibernateCompatible) whereClause.append(++position); whereClause.append(" AND ").append(qualifiedName).append(" <= ").append(QUESTIONMARK); if (!hibernateCompatible) whereClause.append(++position); whereClause.append(CLOSE_BRACKET); parameters.add(conversionService.convert(from, propType)); parameters.add(conversionService.convert(to, propType)); return position; } }); queryHandlers.put(Query.LessThan.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.LessThan eq = (Query.LessThan) criterion; final String name = eq.getProperty(); PersistentProperty prop = validateProperty(entity, name, Query.LessThan.class); Class propType = prop.getType(); position = appendCriteriaForOperator(whereClause, logicalName, name, position, " < ", hibernateCompatible); parameters.add(conversionService.convert(eq.getValue(), propType)); return position; } }); queryHandlers.put(Query.Like.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.Like eq = (Query.Like) criterion; final String name = eq.getProperty(); PersistentProperty prop = validateProperty(entity, name, Query.Like.class); Class propType = prop.getType(); position = appendCriteriaForOperator(whereClause, logicalName, name, position, " like ", hibernateCompatible); parameters.add(conversionService.convert(eq.getValue(), propType)); return position; } }); queryHandlers.put(Query.ILike.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.ILike eq = (Query.ILike) criterion; final String name = eq.getProperty(); PersistentProperty prop = validateProperty(entity, name, Query.ILike.class); Class propType = prop.getType(); whereClause.append("lower(").append(logicalName).append(DOT).append(name).append(")") .append(" like lower(").append(QUESTIONMARK); if (!hibernateCompatible) whereClause.append(++position); whereClause.append(")"); parameters.add(conversionService.convert(eq.getValue(), propType)); return position; } }); queryHandlers.put(Query.In.class, new QueryHandler() { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { Query.In eq = (Query.In) criterion; final String name = eq.getProperty(); PersistentProperty prop = validateProperty(entity, name, Query.In.class); Class propType = prop.getType(); whereClause.append(logicalName).append(DOT).append(name).append(" IN ("); for (Iterator i = eq.getValues().iterator(); i.hasNext();) { Object val = i.next(); whereClause.append(QUESTIONMARK); if (!hibernateCompatible) whereClause.append(++position); if (i.hasNext()) { whereClause.append(COMMA); } parameters.add(conversionService.convert(val, propType)); } whereClause.append(CLOSE_BRACKET); return position; } }); } private static int handleAssociationCriteria(StringBuilder query, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, Association<?> association, Query.Junction associationCriteria, List<Query.Criterion> associationCriteriaList, boolean hibernateCompatible) { if (association instanceof ToOne) { final String associationName = association.getName(); logicalName = logicalName + DOT + associationName; return buildWhereClauseForCriterion(association.getAssociatedEntity(), associationCriteria, query, whereClause, logicalName, associationCriteriaList, position, parameters, conversionService, allowJoins, hibernateCompatible); } if (association != null) { final String associationName = association.getName(); // TODO: Allow customization of join strategy! String joinType = " INNER JOIN "; query.append(joinType).append(logicalName).append(DOT).append(associationName).append(SPACE) .append(associationName); return buildWhereClauseForCriterion(association.getAssociatedEntity(), associationCriteria, query, whereClause, associationName, associationCriteriaList, position, parameters, conversionService, allowJoins, hibernateCompatible); } return position; } private void buildUpdateStatement(StringBuilder queryString, Map<String, Object> propertiesToUpdate, List parameters, boolean hibernateCompatible) { queryString.append(SPACE).append("SET"); // keys need to be sorted before query is built Set<String> keys = new TreeSet<String>(propertiesToUpdate.keySet()); Iterator<String> iterator = keys.iterator(); while (iterator.hasNext()) { String propertyName = iterator.next(); PersistentProperty prop = entity.getPropertyByName(propertyName); if (prop == null) throw new InvalidDataAccessResourceUsageException("Property '" + propertyName + "' of class '" + entity.getName() + "' specified in update does not exist"); parameters.add(propertiesToUpdate.get(propertyName)); queryString.append(SPACE).append(logicalName).append(DOT).append(propertyName).append('=') .append(QUESTIONMARK); if (!hibernateCompatible) queryString.append(parameters.size()); if (iterator.hasNext()) { queryString.append(COMMA); } } } private static void appendPropertyComparison(StringBuilder q, String logicalName, String propertyName, String otherProperty, String operator) { q.append(logicalName).append(DOT).append(propertyName).append(operator).append(logicalName).append(DOT) .append(otherProperty); } private static PersistentProperty validateProperty(PersistentEntity entity, String name, Class criterionType) { if (entity.getIdentity().getName().equals(name)) return entity.getIdentity(); PersistentProperty prop = entity.getPropertyByName(name); if (prop == null) { throw new InvalidDataAccessResourceUsageException("Cannot use [" + criterionType.getSimpleName() + "] criterion on non-existent property: " + name); } return prop; } private static interface QueryHandler { public int handle(PersistentEntity entity, Query.Criterion criterion, StringBuilder q, StringBuilder whereClause, String logicalName, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible); } private List buildWhereClause(PersistentEntity entity, Query.Junction criteria, StringBuilder q, StringBuilder whereClause, String logicalName, boolean allowJoins) { List parameters = new ArrayList(); return buildWhereClause(entity, criteria, q, whereClause, logicalName, allowJoins, parameters); } private List buildWhereClause(PersistentEntity entity, Query.Junction criteria, StringBuilder q, StringBuilder whereClause, String logicalName, boolean allowJoins, List parameters) { int position = parameters.size(); final List<Query.Criterion> criterionList = criteria.getCriteria(); whereClause.append(WHERE_CLAUSE); if (criteria instanceof Query.Negation) { whereClause.append(NOT_CLAUSE); } whereClause.append(OPEN_BRACKET); position = buildWhereClauseForCriterion(entity, criteria, q, whereClause, logicalName, criterionList, position, parameters, conversionService, allowJoins, this.hibernateCompatible); q.append(whereClause.toString()); q.append(CLOSE_BRACKET); return parameters; } protected void appendOrder(StringBuilder queryString, String logicalName) { if (!orders.isEmpty()) { queryString.append(ORDER_BY_CLAUSE); for (Query.Order order : orders) { queryString.append(logicalName).append(DOT).append(order.getProperty()).append(SPACE) .append(order.getDirection().toString()).append(SPACE); } } } static int buildWhereClauseForCriterion(PersistentEntity entity, Query.Junction criteria, StringBuilder q, StringBuilder whereClause, String logicalName, final List<Query.Criterion> criterionList, int position, List parameters, ConversionService conversionService, boolean allowJoins, boolean hibernateCompatible) { for (Iterator<Query.Criterion> iterator = criterionList.iterator(); iterator.hasNext();) { Query.Criterion criterion = iterator.next(); final String operator = criteria instanceof Query.Conjunction ? LOGICAL_AND : LOGICAL_OR; QueryHandler qh = queryHandlers.get(criterion.getClass()); if (qh != null) { position = qh.handle(entity, criterion, q, whereClause, logicalName, position, parameters, conversionService, allowJoins, hibernateCompatible); } else if (criterion instanceof AssociationCriteria) { if (!allowJoins) { throw new InvalidDataAccessResourceUsageException( "Joins cannot be used in a DELETE or UPDATE operation"); } AssociationCriteria ac = (AssociationCriteria) criterion; Association association = ac.getAssociation(); List<Query.Criterion> associationCriteriaList = ac.getCriteria(); handleAssociationCriteria(q, whereClause, logicalName, position, parameters, conversionService, allowJoins, association, new Query.Conjunction(), associationCriteriaList, hibernateCompatible); } else { throw new InvalidDataAccessResourceUsageException("Queries of type " + criterion.getClass().getSimpleName() + " are not supported by this implementation"); } if (iterator.hasNext()) { whereClause.append(operator); } } return position; } }