package com.primeton.dsp.datarelease.api.sql;
import com.alibaba.druid.sql.SQLUtils;
import com.google.common.base.Preconditions;
import com.primeton.dsp.datarelease.api.model.Field;
import com.primeton.dsp.datarelease.api.model.Table;
import com.primeton.dsp.datarelease.api.model.WhereCause;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.BinaryExpression;
import net.sf.jsqlparser.expression.DateValue;
import net.sf.jsqlparser.expression.DoubleValue;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.TimestampValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.GreaterThan;
import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.expression.operators.relational.MinorThan;
import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals;
import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList;
import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectExpressionItem;
import net.sf.jsqlparser.statement.select.SelectItem;
import net.sf.jsqlparser.util.SelectUtils;
import org.apache.commons.lang.StringUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
*
* 单表服务发布,SQL 生成预览
*
*
*
* Created by zhaopx.
* User: zhaopx
* Date: 2020/3/25
* Time: 17:50
*
*
*
* @author zhaopx
*/
public abstract class SQLQuery {
/**
* 输出 SQL
*
* @return 返回 sql str
*/
public abstract String show(boolean format);
public static class Builder {
/**
* 核心 Select
*/
Select select;
public Builder(Table table) {
net.sf.jsqlparser.schema.Table t = new net.sf.jsqlparser.schema.Table(table.getTableName());
t.setAlias(new Alias(table.getAlias(), false));
Select select = null;
// select x from t
if(table.getFields().isEmpty()) {
select = SelectUtils.buildSelectFromTable(t);
} else {
List fields = table.getFields();
SelectItem[] items = new SelectItem[fields.size()];
int i = 0;
for (Field field : fields) {
SelectExpressionItem item = new SelectExpressionItem(new Column(t, field.getFieldName()));
item.setAlias(new Alias(field.getAlias(), true));
items[i] = item;
i++;
}
select = SelectUtils.buildSelectFromTableAndSelectItems(t, items);
}
this.select = select;
}
public Builder(Table table, WhereCause... conds) {
this(table);
// 处理条件
where(conds);
}
/**
* 设置 where 调用条件。调用 where 后就不应该再调用 join 了
* @return
*/
public Builder where(WhereCause... wheres) {
if(wheres == null || wheres.length == 0) {
// 没有可加的条件
return this;
}
PlainSelect ps = (PlainSelect)select.getSelectBody();
Expression where = ps.getWhere();
net.sf.jsqlparser.schema.Table table = (net.sf.jsqlparser.schema.Table)ps.getFromItem();
if(where == null && wheres.length == 1) {
// 一个条件,就这样了。
Expression expr = buildExpression(table, wheres[0]);
ps.setWhere(expr);
} else if(where == null){
// where is null,wheres 第一个不加 and,后续都加 and。
Expression firstExpr = buildExpression(table, wheres[0]);
WhereCause[] whereCauses1toEnd = new WhereCause[wheres.length - 1];
System.arraycopy(wheres, 1, whereCauses1toEnd, 0, whereCauses1toEnd.length);
ps.setWhere(buildWhereCause(table, firstExpr, whereCauses1toEnd));
} else {
// where is not null,第一个条件就需要加 and
ps.setWhere(buildWhereCause(table, where, wheres));
}
return this;
}
/**
* 创建循环的 where 条件
* @param wheres 一个或者多个 where
* @return
*/
private Expression buildWhereCause(net.sf.jsqlparser.schema.Table table, Expression last, WhereCause... wheres) {
if(wheres.length == 1) {
Expression expression = buildExpression(table, wheres[0]);
BinaryExpression expr = null;
if("or".equalsIgnoreCase(wheres[0].getCond())){
expr = new OrExpression(last, expression);
} else {
expr = new AndExpression(last, expression);
}
return expr;
}
Expression addExpr = buildExpression(table, wheres[0]);
BinaryExpression expr = null;
if("or".equalsIgnoreCase(wheres[0].getCond())){
expr = new OrExpression(last, addExpr);
} else {
expr = new AndExpression(last, addExpr);
}
WhereCause[] whereCauses1toEnd = new WhereCause[wheres.length - 1];
System.arraycopy(wheres, 1, whereCauses1toEnd, 0, whereCauses1toEnd.length);
// 递归处理每一个表达式
return buildWhereCause(table, expr, whereCauses1toEnd);
}
/**
* 根据 where 条件,把前台选择的条件转为 sql 支持的结构。
* @param table 表名称
* @param cause 条件
* @return
*/
private Expression buildExpression(net.sf.jsqlparser.schema.Table table, WhereCause cause) {
String[] mutilValue = cause.getValue() != null ? cause.getValue().split(",") : new String[]{};
if (mutilValue.length > 1) {
// 多值的情况 select * from table where id in('a', 'b')
// 多值的必须是 in 或者 notin 的情况,如果不是,强制改变语句为 in 的条件
String opera = cause.getOpera();
if(!"in".equalsIgnoreCase(opera) && !"notin".equalsIgnoreCase(opera)) {
cause.setOpera("in");
}
// 多个值的情况
return buildSingleValue(table, cause);
}
// 单值的条件
return buildSingleValue(table, cause);
}
/**
* 编译单值的条件
* @param table
* @param cause
* @return
*/
private Expression buildSingleValue(net.sf.jsqlparser.schema.Table table, WhereCause cause) {
Expression valueExpr = null;
if(StringUtils.isNotBlank(cause.getToFieldName())) {
// 第二个表名称和表字段名都不为 null,则表达式的值为第二个表中的字段
// a.AGE > a.AGE2
valueExpr = new Column(table, cause.getToFieldName());
} else if("INT".equalsIgnoreCase(cause.getType())) {
// 表达式为常量,但是值为数值类型,SQL 中数值不加引号
valueExpr = new LongValue(cause.getValue());
} else if("DOUBLE".equalsIgnoreCase(cause.getType())) {
// 表达式为常量,但是值为浮点类型,SQL 中数值不加引号
valueExpr = new DoubleValue(cause.getValue());
} else if("DATE".equalsIgnoreCase(cause.getType())) {
// 表达式为常量,但是值为日期类型,SQL 中数值不加引号
// 日期类型为:yyyy-[M]M-[d]d
valueExpr = new DateValue(cause.getValue());
} else if("DATETIME".equalsIgnoreCase(cause.getType())) {
// 表达式为常量,但是值为日期类型,SQL 中数值不加引号
// 日期类型为:yyyy-[M]M-[d]d HH:mm:ss
valueExpr = new TimestampValue(cause.getValue());
} else {
// 表达式值为常量,字符串, NAME = 'X'
valueExpr = new StringValue(cause.getValue());
}
if("=".equals(cause.getOpera())) {
EqualsTo equals = new EqualsTo();
equals.setLeftExpression(new Column(table, cause.getFieldName()));
equals.setRightExpression(valueExpr);
return equals;
} else if(">".equals(cause.getOpera())) {
GreaterThan greaterThan = new GreaterThan();
greaterThan.setLeftExpression(new Column(table, cause.getFieldName()));
greaterThan.setRightExpression(valueExpr);
return greaterThan;
} else if(">=".equals(cause.getOpera())) {
GreaterThanEquals greaterThanEquals = new GreaterThanEquals();
greaterThanEquals.setLeftExpression(new Column(table, cause.getFieldName()));
greaterThanEquals.setRightExpression(valueExpr);
return greaterThanEquals;
} else if("<".equals(cause.getOpera())) {
MinorThan minorThan = new MinorThan();
minorThan.setLeftExpression(new Column(table, cause.getFieldName()));
minorThan.setRightExpression(valueExpr);
return minorThan;
} else if("<=".equals(cause.getOpera())) {
MinorThanEquals minorThanEquals = new MinorThanEquals();
minorThanEquals.setLeftExpression(new Column(table, cause.getFieldName()));
minorThanEquals.setRightExpression(valueExpr);
return minorThanEquals;
} else if("<>".equals(cause.getOpera()) || "!=".equals(cause.getOpera())) {
NotEqualsTo notEqualsTo = new NotEqualsTo();
notEqualsTo.setLeftExpression(new Column(table, cause.getFieldName()));
notEqualsTo.setRightExpression(valueExpr);
return notEqualsTo;
} else if("in".equalsIgnoreCase(cause.getOpera())) {
String[] mutilValue = cause.getValue().split(",");
InExpression inExpression = new InExpression();
inExpression.setLeftExpression(new Column(table, cause.getFieldName()));
MultiExpressionList list = new MultiExpressionList();
List inVals = new ArrayList<>();
for (String val : mutilValue) {
inVals.add(new StringValue(val));
}
list.addExpressionList(new ExpressionList(inVals));
inExpression.setRightItemsList(list);
return inExpression;
} else if("notin".equalsIgnoreCase(cause.getOpera())) {
String[] mutilValue = cause.getValue().split(",");
InExpression inExpression = new InExpression();
inExpression.setLeftExpression(new Column(table, cause.getFieldName()));
// not 否定条件
inExpression.setNot(true);
MultiExpressionList list = new MultiExpressionList();
List inVals = new ArrayList<>();
for (String val : mutilValue) {
inVals.add(new StringValue(val));
}
list.addExpressionList(new ExpressionList(inVals));
inExpression.setRightItemsList(list);
return inExpression;
} else if("l".equalsIgnoreCase(cause.getOpera())) {
Preconditions.checkNotNull(cause.getValue(), " like value must not be blank");
// 如果自带 %,则说明需要匹配值的 %,用转义
String likeValue = cause.getValue().replaceAll("%", "\\%");
valueExpr = new StringValue("%"+likeValue+"%");
LikeExpression likeExpression = new LikeExpression();
likeExpression.setLeftExpression(new Column(table, cause.getFieldName()));
likeExpression.setRightExpression(valueExpr);
return likeExpression;
}
EqualsTo equals = new EqualsTo();
equals.setLeftExpression(new Column(table, cause.getFieldName()));
equals.setRightExpression(valueExpr);
return equals;
}
/**
* 设置 where 调用条件。调用 where 后就不应该再调用 join 了
* @return
*/
public Builder where(Collection wheres) {
where(wheres.toArray(new WhereCause[wheres.size()]));
return this;
}
/**
* 创建 SQL
*
* @return
*/
public SQLQuery build() {
return new SQLQuery() {
@Override
public String show(boolean format) {
if(!format) {
return select.toString();
}
String sql = select.toString();
// 美化一下 SQL
sql = SQLUtils.formatMySql(sql);
return sql;
}
};
}
}
public static void main(String[] args) {
StringValue valueExpr = new StringValue("%王%");
LikeExpression likeExpression = new LikeExpression();
likeExpression.setLeftExpression(new Column("NAME"));
likeExpression.setRightExpression(valueExpr);
System.out.println(likeExpression.toString());
}
}