Java tutorial
/* * Copyright 2008-2010 the original author or authors. * * 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.synyx.hades.dao.query; import static java.util.regex.Pattern.*; import java.util.Collection; import java.util.Iterator; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.persistence.EntityManager; import javax.persistence.Query; import org.springframework.util.Assert; import org.synyx.hades.domain.Sort; import org.synyx.hades.domain.Sort.Property; import org.synyx.hades.util.ClassUtils; /** * Simple utility class to create JPA queries. * * @author Oliver Gierke */ public abstract class QueryUtils { public static final String COUNT_QUERY_STRING = "select count(%s) from %s x"; public static final String DELETE_ALL_QUERY_STRING = "delete from %s x"; public static final String READ_ALL_QUERY = "select x from %s x"; private static final String DEFAULT_ALIAS = "x"; private static final String COUNT_REPLACEMENT = "select count($3$5) $4$5$6"; private static final Pattern ALIAS_MATCH; private static final Pattern COUNT_MATCH; private static final String IDENTIFIER = "[\\p{L}._$]+"; private static final String IDENTIFIER_GROUP = String.format("(%s)", IDENTIFIER); static { StringBuilder builder = new StringBuilder(); builder.append("(?<=from)"); // from as starting delimiter builder.append("(?:\\s)+"); // at least one space separating builder.append(IDENTIFIER_GROUP); // Entity name, can be qualified (any builder.append("(?:\\sas)*"); // exclude possible "as" keyword builder.append("(?:\\s)+"); // at least one space separating builder.append("(\\w*)"); // the actual alias ALIAS_MATCH = compile(builder.toString(), CASE_INSENSITIVE); builder = new StringBuilder(); builder.append("(select\\s+((distinct )?.+?)\\s+)?(from\\s+"); builder.append(IDENTIFIER); builder.append("(?:\\s+as)?\\s+)"); builder.append(IDENTIFIER_GROUP); builder.append("(.*)"); COUNT_MATCH = compile(builder.toString(), CASE_INSENSITIVE); } /** * Private constructor to prevent instantiation. */ private QueryUtils() { } /** * Returns the query string for the given class. * * @return */ public static String getQueryString(String template, Class<?> clazz) { if (null == clazz) { throw new IllegalArgumentException("Class must not be null!"); } return getQueryString(template, ClassUtils.getEntityName(clazz)); } /** * Returns the query string for the given class name. * * @param template * @param clazzName * @return */ public static String getQueryString(String template, String clazzName) { Assert.hasText(clazzName, "Classname must not be null or empty!"); return String.format(template, clazzName); } /** * Adds {@literal order by} clause to the JPQL query. Uses the * {@link #DEFAULT_ALIAS} to bind the sorting property to. * * @param query * @param alias * @param sort * @return */ public static String applySorting(String query, Sort sort) { return applySorting(query, sort, DEFAULT_ALIAS); } /** * Adds {@literal order by} clause to the JPQL query. * * @param query * @param sort * @param alias * @return */ public static String applySorting(String query, Sort sort, String alias) { if (null == sort) { return query; } Assert.hasText(alias); StringBuilder builder = new StringBuilder(query); builder.append(" order by"); for (Property property : sort) { builder.append(String.format(" %s.", alias)); builder.append(property.getName()); builder.append(" "); builder.append(property.getOrder().getJpaValue()); builder.append(","); } builder.deleteCharAt(builder.length() - 1); return builder.toString(); } /** * Resolves the alias for the entity to be retrieved from the given JPA * query. * * @param query * @return */ public static String detectAlias(String query) { Matcher matcher = ALIAS_MATCH.matcher(query); return matcher.find() ? matcher.group(2) : null; } /** * Creates a where-clause referencing the given entities and appends it to * the given query string. Binds the given entities to the query. * * @param <T> * @param queryString * @param entities * @param entityManager * @return */ public static <T> Query applyAndBind(String queryString, Collection<T> entities, EntityManager entityManager) { Assert.notNull(queryString); Assert.notNull(entities); Assert.notNull(entityManager); Iterator<T> iterator = entities.iterator(); if (!iterator.hasNext()) { return entityManager.createQuery(queryString); } String alias = detectAlias(queryString); StringBuilder builder = new StringBuilder(queryString); builder.append(" where"); for (int i = 0; i < entities.size(); i++) { builder.append(String.format(" %s = ?%d", alias, i + 1)); if (i < entities.size() - 1) { builder.append(" or"); } } Query query = entityManager.createQuery(builder.toString()); for (int i = 0; i < entities.size(); i++) { query.setParameter(i + 1, iterator.next()); } return query; } /** * Creates a count projected query from the given orginal query. * * @param originalQuery must not be {@literal null} or empty * @return */ public static String createCountQueryFor(String originalQuery) { Assert.hasText(originalQuery); Matcher matcher = COUNT_MATCH.matcher(originalQuery); return matcher.replaceFirst(COUNT_REPLACEMENT); } }