当拿到一个sql,想要获取其中的select字段,是否首先想到的是手写一个来获取查询字段,可是事实并没有想象的那么简单,设想 select 'a' from xx;select id as user_id from xx;select current_date gmt_create from xx;select case when .. from xx;select count(1) from xx;等等,这些语句该怎么解析呢?
推荐使用JSQLParser
JSQLParser是一款开源的SQL语句解析器,使用它可以把SQL语句解析成一组层次分明的java类。JSQLParsers所能解释的SQL语句不受具体数据库的限制,在支持标准SQL的同时,也支持一些特定数据库的方法。如支持Oracle关联语法(+),PostgreSQL的方法using::,以及关系运算符!=,等等。
解析之前,先来看看常见的解析器(expression)有哪些,有利于后面对代码的理解。
1、条件表达式
如:AndExpression(and),OrExpression(or)
2、关系表达式
如:EqualsTo(=),MinorThan(<),GreaterThan(>),……
3、算术表达式
如:Addition(+),Subtraction(-),Multiplication(*),Division(/),……
4、列表达式
如:Column
5、case表达式
如:CaseExpression
6、值表达式
如:StringValue,DateValue,LongValue,DoubleValue,……
7、函数表达式
如:Function
8、参数表达式
如:JdbcParameter,JdbcNameParameter,……
实现代码:
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>3.1</version>
</dependency>
Map<String, String> paramMap = new HashMap<>(queryParamList.size());
for (String queryParam : queryParamList) {
paramMap.put(queryParam, "1");
}
String parsedSql = generalQueryService.replaceParameterWithValue(querySql, paramMap);
try {
CCJSqlParserManager parserManager = new CCJSqlParserManager();
Select select = (Select) parserManager.parse(new StringReader(parsedSql));
PlainSelect plain = (PlainSelect) select.getSelectBody();
List<SelectItem> selectItems = plain.getSelectItems();
List<String> items = new ArrayList<>();
if (selectItems != null) {
for (SelectItem selectItem : selectItems) {
if (selectItem instanceof SelectExpressionItem) {
SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem;
String columnName = "";
Alias alias = selectExpressionItem.getAlias();
Expression expression = selectExpressionItem.getExpression();
if (expression instanceof CaseExpression) {
// case表达式
columnName = alias.getName();
} else if (expression instanceof LongValue || expression instanceof StringValue || expression instanceof DateValue || expression instanceof DoubleValue) {
// 值表达式
columnName = Objects.nonNull(alias.getName()) ? alias.getName() : expression.getASTNode().jjtGetValue().toString();
} else if (expression instanceof TimeKeyExpression) {
// 日期
columnName = alias.getName();
} else {
if (alias != null) {
columnName = alias.getName();
} else {
SimpleNode node = expression.getASTNode();
Object value = node.jjtGetValue();
if (value instanceof Column) {
columnName = ((Column) value).getColumnName();
} else if (value instanceof Function) {
columnName = value.toString();
}else {
// 增加对select 'aaa' from table; 的支持
columnName = String.valueOf(value);
columnName = columnName.replace("'", "");
columnName = columnName.replace("\"", "");
columnName = columnName.replace("`", "");
}
}
}
columnName = columnName.replace("'", "");
columnName = columnName.replace("\"", "");
columnName = columnName.replace("`", "");
items.add(columnName);
} else if (selectItem instanceof AllTableColumns) {
AllTableColumns allTableColumns = (AllTableColumns) selectItem;
items.add(allTableColumns.toString());
} else {
items.add(selectItem.toString());
}
}
}
return StringUtils.join(items, ",");
} catch (JSQLParserException e) {
// ignore
}
return null;
代码可能未包含所有的情况,所以当发现问题时,需要查看 expression 按需解决。