当我们在写Hibernate Orm组件的时候,经常会遇到分页查询这种操作,分页查询的原理不在熬述,比较重要的一点是需要计算查询的总数count,大部分人还是采用传统的hql/sql字符串截取或者拼接等方式实现。下面给出的代码是通过字符串模板、正则匹配等方式实现的,直接上代码:
public class QueryTemplateUtil {
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";
private static final String COUNT_REPLACEMENT_TEMPLATE = "select count(%s) $5$6$7";
private static final String SIMPLE_COUNT_VALUE = "$2";
private static final String COMPLEX_COUNT_VALUE = "$3$6";
private static final Pattern COUNT_MATCH;
private static final String IDENTIFIER = "[\\p{Alnum}._$]+";
private static final String IDENTIFIER_GROUP = String.format("(%s)",
IDENTIFIER);
private static final String EQUALS_CONDITION_STRING = "%s.%s = ?";
static {
StringBuilder builder = new StringBuilder();
builder.append("(?<=from)"); // from as starting delimiter
builder.append("(?: )+"); // at least one space separating
builder.append(IDENTIFIER_GROUP); // Entity name, can be qualified (any
builder.append("(?: as)*"); // exclude possible "as" keyword
builder.append("(?: )+"); // at least one space separating
builder.append("(\\w*)"); // the actual alias
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 QueryTemplateUtil() {
}
/**
* getCountQueryString:根据实体名称,count字段,查询条件生成count查询字符串
* <p>
* <b>例如:</b>{@code entityName="User"},{@code countQueryPlaceHolder="*"},
* {@code conditionAttributes=["name","sex"]},生成的countQueryString是:
* </p>
* <p>
* {@code select count(*) from User x where x.name=? and x.sex=?}
* </p>
*
* @Title: getCountQueryString
* @param entityName
* 实体名称,不允许{@literal null}或者{@literal empty}
* @param countQueryPlaceHolder
* count查询占位字段,不允许{@literal null}或者{@literal empty}
* @param conditionAttributes
* 查询条件字段
* @return String
*/
public static String getCountQueryString(String entityName,
String countQueryPlaceHolder, Iterable<String> conditionAttributes) {
Assert.hasText(entityName, "实体名称不允许为null或empty!");
StringBuilder sb = new StringBuilder(String.format(COUNT_QUERY_STRING,
countQueryPlaceHolder, entityName));
sb.append(" WHERE ");
if (conditionAttributes != null) {
for (String conditionAttribute : conditionAttributes) {
sb.append(String.format(EQUALS_CONDITION_STRING, "x",
conditionAttribute));
sb.append(" AND ");
}
}
sb.append("1 = 1");
return sb.toString();
}
/**
* getCountQueryString:根据实体名称,生成count查询字符串
* <p>
* <b>注意:</b>count占位字符默认为{@literal *},不带查询条件
* </p>
*
* @Title: getCountQueryString
* @param entityName
* 实体名称,不允许{@literal null}或者{@literal empty}
* @return {@code select count(*) from SomeEntity x }
*/
public static String getCountQueryString(String entityName) {
return getCountQueryString(entityName, "*", null);
}
/**
* getCountQueryString:根据实体名称、查询条件生成count查询字符串
*
* @Title: getCountQueryString
* @param entityName
* 实体名称,不允许{@literal null}或者{@literal empty}
* @param conditionAttributes
* 查询条件字段
* @return {@code select count(*) from SomeEntity x where condition1=? and condition2=? and 1=1}
*/
public static String getCountQueryString(String entityName,
Iterable<String> conditionAttributes) {
return getCountQueryString(entityName, "*", conditionAttributes);
}
/**
* getQueryString:通过实体名称与查询字符串模板,创建查询字符串
* <p>
* <b>参见:</b>{@link org.mk.mini.orm.util.DELETE_ALL_QUERY_STRING}
* </p>
*
* @Title: getQueryStringByTemplate
* @param template
* 查询字符串模板
* @param entityName
* 实体名称
* @return 查询字符串
*/
public static String getQueryStringByTemplate(String template,
String entityName) {
Assert.hasText(entityName, "实体名称不允许为null或empty!");
return String.format(template, entityName);
}
/**
* createCountQueryFor:通过给定的原始sql或hql创建count查询字符串.
*
* @Title: createCountQueryFor
* @param originalQuery
* 原始查询字符串,支持sql与hql 不能为 {@literal null} 或者 {@literal empty}
* @return
*/
public static String createCountQueryFor(String originalQuery) {
Assert.hasText(originalQuery, "查询字符串不允许为null或empty");
Matcher matcher = COUNT_MATCH.matcher(originalQuery);
String variable = matcher.matches() ? matcher.group(4) : null;
boolean useVariable = ExtraStringUtils.hasText(variable)
&& !variable.startsWith("new")
&& !variable.startsWith("count(");
return matcher.replaceFirst(String.format(COUNT_REPLACEMENT_TEMPLATE,
useVariable ? SIMPLE_COUNT_VALUE : COMPLEX_COUNT_VALUE));
}
}
实例就不用写了,注释已经写的很清楚,关于Assert和ExtraStringUtils只是做了断言判断与字符串判断,需要copy该代码的同学们可以改造一下,写的不好,请指正。