本节来看一个实战案例,这个案例参考了开源框架 JPA 的 SQL 构造模式。
实际应用中,在构造 SQL 查询条件的时候,需要根据不同的条件来拼接 SQL 字符串。如果查询条件复杂,SQL 拼接的过程也会变得非常复杂,从而给代码维护带来非常大的困难。
下面我们用建造者类 QueryRuleSqlBuilder 将复杂的 SQL 构造过程进行封装,用 QueryRule 对象专门保存 SQL 查询时的条件,最后根据查询条件,自动生成 SQL 语句。
首先创建 QueryRule 类,代码如下。
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* QueryRule 主要功能用于构造查询条件
*/
public final class QueryRule implements Serializable {
private static final long serialVersionUID = 1L;
public static final int ASC_ORDER = 101;
public static final int DESC_ORDER = 102;
public static final int LIKE = 1;
public static final int IN = 2;
public static final int NOTIN = 3;
public static final int BETWEEN = 4;
public static final int EQ = 5;
public static final int NOTEQ = 6;
public static final int GT = 7;
public static final int GE = 8;
public static final int LT = 9;
public static final int LE = 10;
public static final int ISNULL = 11;
public static final int ISNOTNULL = 12;
public static final int ISEMPTY = 13;
public static final int ISNOTEMPTY = 14;
public static final int AND = 201;
public static final int OR = 202;
private List ruleList = new ArrayList();
private List queryRuleList = new ArrayList();
private String propertyName;
private QueryRule() {
}
private QueryRule(String propertyName) {
this.propertyName = propertyName;
}
public static QueryRule getInstance() {
return new QueryRule();
}
/**
* 添加升序规则
*
* @param propertyName
* @return
*/
public QueryRule addAscOrder(String propertyName) {
this.ruleList.add(new Rule(ASC_ORDER, propertyName));
return this;
}
/**
* 添加降序规则
*
* @param propertyName
*/
public QueryRule addDescOrder(String propertyName) {
this.ruleList.add(new Rule(DESC_ORDER, propertyName));
return this;
}
public QueryRule andIsNull(String propertyName) {
this.ruleList.add(new Rule(ISNULL, propertyName).setAndOr(AND));
return this;
}
public QueryRule andIsNotNull(String propertyName) {
this.ruleList.add(new Rule(ISNOTNULL, propertyName).setAndOr(AND));
return this;
}
public QueryRule andIsEmpty(String propertyName) {
this.ruleList.add(new Rule(ISEMPTY, propertyName).setAndOr(AND));
return this;
}
public QueryRule andIsNotEmpty(String propertyName) {
this.ruleList.add(new Rule(ISNOTEMPTY, propertyName).setAndOr(AND));
return this;
}
public QueryRule andLike(String propertyName, Object value) {
this.ruleList.add(new Rule(LIKE, propertyName, new Object[]{value}).setAndOr(AND));
return this;
}
public QueryRule andEqual(String propertyName, Object value) {
this.ruleList.add(new Rule(EQ, propertyName, new Object[]{value}).setAndOr(AND));
return this;
}
public QueryRule andBetween(String propertyName, Object... values) {
this.ruleList.add(new Rule(BETWEEN, propertyName, values).setAndOr(AND));
return this;
}
public QueryRule andIn(String propertyName, List values) {
this.ruleList.add(new Rule(IN, propertyName, new Object[]{values}).setAndOr(AND));
return this;
}
public QueryRule andIn(String propertyName, Object... values) {
this.ruleList.add(new Rule(IN, propertyName, values).setAndOr(AND));
return this;
}
public QueryRule andNotIn(String propertyName, List values) {
this.ruleList.add(new Rule(NOTIN, propertyName, new Object[]{values}).setAndOr(AND));
return this;
}
public QueryRule orNotIn(String propertyName, Object... values) {
this.ruleList.add(new Rule(NOTIN, propertyName, values).setAndOr(OR));
return this;
}
public QueryRule andNotEqual(String propertyName, Object value) {
this.ruleList.add(new Rule(NOTEQ, propertyName, new Object[]{value}).setAndOr(AND));
return this;
}
public QueryRule andGreaterThan(String propertyName, Object value) {
this.ruleList.add(new Rule(GT, propertyName, new Object[]{value}).setAndOr(AND));
return this;
}
public QueryRule andGreaterEqual(String propertyName, Object value) {
this.ruleList.add(new Rule(GE, propertyName, new Object[]{value}).setAndOr(AND));
return this;
}
public QueryRule andLessThan(String propertyName, Object value) {
this.ruleList.add(new Rule(LT, propertyName, new Object[]{value}).setAndOr(AND));
return this;
}
public QueryRule andLessEqual(String propertyName, Object value) {
this.ruleList.add(new Rule(LE, propertyName, new Object[]{value}).setAndOr(AND));
return this;
}
public QueryRule orIsNull(String propertyName) {
this.ruleList.add(new Rule(ISNULL, propertyName).setAndOr(OR));
return this;
}
public QueryRule orIsNotNull(String propertyName) {
this.ruleList.add(new Rule(ISNOTNULL, propertyName).setAndOr(OR));
return this;
}
public QueryRule orIsEmpty(String propertyName) {
this.ruleList.add(new Rule(ISEMPTY, propertyName).setAndOr(OR));
return this;
}
public QueryRule orIsNotEmpty(String propertyName) {
this.ruleList.add(new Rule(ISNOTEMPTY, propertyName).setAndOr(OR));
return this;
}
public QueryRule orLike(String propertyName, Object value) {
this.ruleList.add(new Rule(LIKE, propertyName, new Object[]{value}).setAndOr(OR));
return this;
}
public QueryRule orEqual(String propertyName, Object value) {
this.ruleList.add(new Rule(EQ, propertyName, new Object[]{value}).setAndOr(OR));
return this;
}
public QueryRule orBetween(String propertyName, Object... values) {
this.ruleList.add(new Rule(BETWEEN, propertyName, values).setAndOr(OR));
return this;
}
public QueryRule orIn(String propertyName, List values) {
this.ruleList.add(new Rule(IN, propertyName, new Object[]{values}).setAndOr(OR));
return this;
}
public QueryRule orIn(String propertyName, Object... values) {
this.ruleList.add(new Rule(IN, propertyName, values).setAndOr(OR));
return this;
}
public QueryRule orNotEqual(String propertyName, Object value) {
this.ruleList.add(new Rule(NOTEQ, propertyName, new Object[]{value}).setAndOr(OR));
return this;
}
public QueryRule orGreaterThan(String propertyName, Object value) {
this.ruleList.add(new Rule(GT, propertyName, new Object[]{value}).setAndOr(OR));
return this;
}
public QueryRule orGreaterEqual(String propertyName, Object value) {
this.ruleList.add(new Rule(GE, propertyName, new Object[]{value}).setAndOr(OR));
return this;
}
public QueryRule orLessThan(String propertyName, Object value) {
this.ruleList.add(new Rule(LT, propertyName, new Object[]{value}).setAndOr(OR));
return this;
}
public QueryRule orLessEqual(String propertyName, Object value) {
this.ruleList.add(new Rule(LE, propertyName, new Object[]{value}).setAndOr(OR));
return this;
}
public List getRuleList() {
return this.ruleList;
}
public List getQueryRuleList() {
return this.queryRuleList;
}
public String getPropertyName() {
return this.propertyName;
}
protected class Rule implements Serializable {
private static final long serialVersionUID = 1L;
private int type; //规则的类型
private String property_name;
private Object[] values;
private int andOr = AND;
public Rule(int paramInt, String paramString) {
this.property_name = paramString;
this.type = paramInt;
}
public Rule(int paramInt, String paramString,
Object[] paramArrayOfObject) {
this.property_name = paramString;
this.values = paramArrayOfObject;
this.type = paramInt;
}
public Rule setAndOr(int andOr) {
this.andOr = andOr;
return this;
}
public int getAndOr() {
return this.andOr;
}
public Object[] getValues() {
return this.values;
}
public int getType() {
return this.type;
}
public String getPropertyName() {
return this.property_name;
}
}
}
然后创建 QueryRuleSqlBuilder 类,代码如下:
import builder.QueryRule.Rule;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 根据QueryRule自动构建sql语句
*/
public class QueryRuleSqlBuilder {
private int CURR_INDEX = 0; //记录参数所在的位置
private List properties; //保存列名列表
private List values; //保存参数值列表
private List orders; //保存排序规则列表
private String whereSql = "";
private String orderSql = "";
private Object[] valueArr = new Object[]{};
private Map valueMap = new HashMap();
/**
* 或得查询条件
*
* @return
*/
private String getWhereSql() {
return this.whereSql;
}
/**
* 获得排序条件
*
* @return
*/
private String getOrderSql() {
return this.orderSql;
}
/**
* 获得参数值列表
*
* @return
*/
public Object[] getValues() {
return this.valueArr;
}
/**
* 获取参数列表
*
* @return
*/
private Map getValueMap() {
return this.valueMap;
}
/**
* 创建SQL构造器
*
* @param queryRule
*/
public QueryRuleSqlBuilder(QueryRule queryRule) {
CURR_INDEX = 0;
properties = new ArrayList();
values = new ArrayList();
orders = new ArrayList();
for (QueryRule.Rule rule : queryRule.getRuleList()) {
switch (rule.getType()) {
case QueryRule.BETWEEN:
processBetween(rule);
break;
case QueryRule.EQ:
processEqual(rule);
break;
case QueryRule.LIKE:
processLike(rule);
break;
case QueryRule.NOTEQ:
processNotEqual(rule);
break;
case QueryRule.GT:
processGreaterThen(rule);
break;
case QueryRule.GE:
processGreaterEqual(rule);
break;
case QueryRule.LT:
processLessThen(rule);
break;
case QueryRule.LE:
processLessEqual(rule);
break;
case QueryRule.IN:
processIN(rule);
break;
case QueryRule.NOTIN:
processNotIN(rule);
break;
case QueryRule.ISNULL:
processIsNull(rule);
break;
case QueryRule.ISNOTNULL:
processIsNotNull(rule);
break;
case QueryRule.ISEMPTY:
processIsEmpty(rule);
break;
case QueryRule.ISNOTEMPTY:
processIsNotEmpty(rule);
break;
case QueryRule.ASC_ORDER:
processOrder(rule);
break;
case QueryRule.DESC_ORDER:
processOrder(rule);
break;
default:
throw new IllegalArgumentException("type " + rule.getType() + " not supported.");
}
}
//拼装where语句
appendWhereSql();
//拼装排序语句
appendOrderSql();
//拼装参数值
appendValues();
}
/**
* 去掉order
*
* @param sql
* @return
*/
private String removeOrders(String sql) {
Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(sql);
StringBuffer sb = new StringBuffer();
while (m.find()) {
m.appendReplacement(sb, "");
}
m.appendTail(sb);
return sb.toString();
}
/**
* 去掉select
*
* @param sql
* @return
*/
private String removeSelect(String sql) {
if (sql.toLowerCase().matches("from\\s+")) {
int beginPos = sql.toLowerCase().indexOf("from");
return sql.substring(beginPos);
} else {
return sql;
}
}
/**
* 处理like
*
* @param rule
*/
private void processLike(QueryRule.Rule rule) {
if (ArrayUtils.isEmpty(rule.getValues())) {
return;
}
Object obj = rule.getValues()[0];
if (obj != null) {
String value = obj.toString();
if (!StringUtils.isEmpty(value)) {
value = value.replace('*', '%');
obj = value;
}
}
add(rule.getAndOr(), rule.getPropertyName(), "like", "%" + rule.getValues()[0] + "%");
}
/**
* 处理between
*
* @param rule
*/
private void processBetween(QueryRule.Rule rule) {
if ((ArrayUtils.isEmpty(rule.getValues()))
|| (rule.getValues().length < 2)) {
return;
}
add(rule.getAndOr(), rule.getPropertyName(), "", "between", rule.getValues()[0], "and");
add(0, "", "", "", rule.getValues()[1], "");
}
/**
* 处理 =
*
* @param rule
*/
private void processEqual(QueryRule.Rule rule) {
if (ArrayUtils.isEmpty(rule.getValues())) {
return;
}
add(rule.getAndOr(), rule.getPropertyName(), "=", rule.getValues()[0]);
}
/**
* 处理 <>
*
* @param rule
*/
private void processNotEqual(QueryRule.Rule rule) {
if (ArrayUtils.isEmpty(rule.getValues())) {
return;
}
add(rule.getAndOr(), rule.getPropertyName(), "<>", rule.getValues()[0]);
}
/**
* 处理 >
*
* @param rule
*/
private void processGreaterThen(
QueryRule.Rule rule) {
if (ArrayUtils.isEmpty(rule.getValues())) {
return;
}
add(rule.getAndOr(), rule.getPropertyName(), ">", rule.getValues()[0]);
}
/**
* 处理>=
*
* @param rule
*/
private void processGreaterEqual(
QueryRule.Rule rule) {
if (ArrayUtils.isEmpty(rule.getValues())) {
return;
}
add(rule.getAndOr(), rule.getPropertyName(), ">=", rule.getValues()[0]);
}
/**
* 处理<
*
* @param rule
*/
private void processLessThen(QueryRule.Rule rule) {
if (ArrayUtils.isEmpty(rule.getValues())) {
return;
}
add(rule.getAndOr(), rule.getPropertyName(), "
}
/**
* 处理<=
*
* @param rule
*/
private void processLessEqual(
QueryRule.Rule rule) {
if (ArrayUtils.isEmpty(rule.getValues())) {
return;
}
add(rule.getAndOr(), rule.getPropertyName(), "<=", rule.getValues()[0]);
}
/**
* 处理 is null
*
* @param rule
*/
private void processIsNull(QueryRule.Rule rule) {
add(rule.getAndOr(), rule.getPropertyName(), "is null", null);
}
/**
* 处理 is not null
*
* @param rule
*/
private void processIsNotNull(QueryRule.Rule rule) {
add(rule.getAndOr(), rule.getPropertyName(), "is not null", null);
}
/**
* 处理 <>''
*
* @param rule
*/
private void processIsNotEmpty(QueryRule.Rule rule) {
add(rule.getAndOr(), rule.getPropertyName(), "<>", "''");
}
/**
* 处理 =''
*
* @param rule
*/
private void processIsEmpty(QueryRule.Rule rule) {
add(rule.getAndOr(), rule.getPropertyName(), "=", "''");
}
/**
* 处理in和not in
*
* @param rule
* @param name
*/
private void inAndNotIn(QueryRule.Rule rule, String name) {
if (ArrayUtils.isEmpty(rule.getValues())) {
return;
}
if ((rule.getValues().length == 1) && (rule.getValues()[0] != null)
&& (rule.getValues()[0] instanceof List)) {
List list = (List) rule.getValues()[0];
if ((list != null) && (list.size() > 0)) {
for (int i = 0; i < list.size(); i++) {
if (i == 0 && i == list.size() - 1) {
add(rule.getAndOr(), rule.getPropertyName(), "", name + " (", list.get(i), ")");
} else if (i == 0 && i < list.size() - 1) {
add(rule.getAndOr(), rule.getPropertyName(), "", name + " (", list.get(i), "");
}
if (i > 0 && i < list.size() - 1) {
add(0, "", ",", "", list.get(i), "");
}
if (i == list.size() - 1 && i != 0) {
add(0, "", ",", "", list.get(i), ")");
}
}
}
} else {
Object[] list = rule.getValues();
for (int i = 0; i < list.length; i++) {
if (i == 0 && i == list.length - 1) {
add(rule.getAndOr(), rule.getPropertyName(), "", name + " (", list[i], ")");
} else if (i == 0 && i < list.length - 1) {
add(rule.getAndOr(), rule.getPropertyName(), "", name + " (", list[i], "");
}
if (i > 0 && i < list.length - 1) {
add(0, "", ",", "", list[i], "");
}
if (i == list.length - 1 && i != 0) {
add(0, "", ",", "", list[i], ")");
}
}
}
}
/**
* 处理 not in
*
* @param rule
*/
private void processNotIN(QueryRule.Rule rule) {
inAndNotIn(rule, "not in");
}
/**
* 处理 in
*
* @param rule
*/
private void processIN(QueryRule.Rule rule) {
inAndNotIn(rule, "in");
}
/**
* 处理 order by
*
* @param rule 查询规则
*/
private void processOrder(Rule rule) {
switch (rule.getType()) {
case QueryRule.ASC_ORDER:
// propertyName非空
if (!StringUtils.isEmpty(rule.getPropertyName())) {
orders.add(Order.asc(rule.getPropertyName()));
}
break;
case QueryRule.DESC_ORDER:
// propertyName非空
if (!StringUtils.isEmpty(rule.getPropertyName())) {
orders.add(Order.desc(rule.getPropertyName()));
}
break;
default:
break;
}
}
/**
* 加入到sql查询规则队列
*
* @param andOr and 或者 or
* @param key 列名
* @param split 列名与值之间的间隔
* @param value 值
*/
private void add(int andOr, String key, String split, Object value) {
add(andOr, key, split, "", value, "");
}
/**
* 加入到sql查询规则队列
*
* @param andOr and 或则 or
* @param key 列名
* @param split 列名与值之间的间隔
* @param prefix 值前缀
* @param value 值
* @param suffix 值后缀
*/
private void add(int andOr, String key, String split, String prefix, Object value, String suffix) {
String andOrStr = (0 == andOr ? "" : (QueryRule.AND == andOr ? " and " : " or "));
properties.add(CURR_INDEX, andOrStr + key + " " + split + prefix + (null != value ? " ? " : " ") + suffix);
if (null != value) {
values.add(CURR_INDEX, value);
CURR_INDEX++;
}
}
/**
* 拼装 where 语句
*/
private void appendWhereSql() {
StringBuffer whereSql = new StringBuffer();
for (String p : properties) {
whereSql.append(p);
}
this.whereSql = removeSelect(removeOrders(whereSql.toString()));
}
/**
* 拼装排序语句
*/
private void appendOrderSql() {
StringBuffer orderSql = new StringBuffer();
for (int i = 0; i < orders.size(); i++) {
if (i > 0 && i < orders.size()) {
orderSql.append(",");
}
orderSql.append(orders.get(i).toString());
}
this.orderSql = removeSelect(removeOrders(orderSql.toString()));
}
/**
* 拼装参数值
*/
private void appendValues() {
Object[] val = new Object[values.size()];
for (int i = 0; i < values.size(); i++) {
val[i] = values.get(i);
valueMap.put(i, values.get(i));
}
this.valueArr = val;
}
public String builder(String tableName) {
String ws = removeFirstAnd(this.getWhereSql());
String whereSql = ("".equals(ws) ? ws : (" where " + ws));
String sql = "select * from " + tableName + whereSql;
Object[] values = this.getValues();
String orderSql = this.getOrderSql();
orderSql = (StringUtils.isEmpty(orderSql) ? " " : (" order by " + orderSql));
sql += orderSql;
return sql;
}
private String removeFirstAnd(String sql) {
if (StringUtils.isEmpty(sql)) {
return sql;
}
return sql.trim().toLowerCase().replaceAll("^\\s*and", "") + " ";
}
}
接着创建 Order 类,代码如下:
/**
* sql排序组件
*/
public class Order {
private boolean ascending; //升序还是降序
private String propertyName; //哪个字段升序,哪个字段降序
public String toString() {
return propertyName + ' ' + (ascending ? "asc" : "desc");
}
/**
* Constructor for Order.
*/
protected Order(String propertyName, boolean ascending) {
this.propertyName = propertyName;
this.ascending = ascending;
}
/**
* Ascending order
*
* @param propertyName
* @return Order
*/
public static Order asc(String propertyName) {
return new Order(propertyName, true);
}
/**
* Descending order
*
* @param propertyName
* @return Order
*/
public static Order desc(String propertyName) {
return new Order(propertyName, false);
}
}
最后编写客户端测试代码,如下:
/**
* Created by C语言中文网.
*/
public class Test {
public static void main(String[] args) {
QueryRule queryRule = QueryRule.getInstance();
queryRule.addAscOrder("age")
.andEqual("addr", "HeBei")
.andLike("name", "C语言中文网")
.andGreaterEqual("age", 18);
QueryRuleSqlBuilder builder = new QueryRuleSqlBuilder(queryRule);
System.out.println(builder.builder("t_member"));
System.out.println("Params: " + Arrays.toString(builder.getValues()));
}
}
这样一来,客户端代码就非常清楚了,运行结果如下所示:
select * from t_member where addr = ? and name like ? order by age asc
Params: [HeBei, %C语言中文网%]