JSqlParser基本使用
1、表达式评估示例
这是一个使用 JSqlParser 进行加法和乘法运算的非常简单的表达式求值器。 ExpressionDeParser 用于遍历和解释解析树。
import java.util.Stack;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.operators.arithmetic.Addition;
import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.util.deparser.ExpressionDeParser;
public class SimpleEvaluateExpr {
public static void main( String[] args ) throws JSQLParserException {
evaluate("4+5*6");
evaluate("4*5+6");
evaluate("4*(5+6)");
evaluate("4*(5+6)*(2+3)");
}
static void evaluate(String expr) throws JSQLParserException {
final Stack<Long> stack = new Stack<Long>();
System.out.println("expr=" + expr);
Expression parseExpression = CCJSqlParserUtil.parseExpression(expr);
ExpressionDeParser deparser = new ExpressionDeParser() {
@Override
public void visit(Addition addition) {
super.visit(addition);
long sum1 = stack.pop();
long sum2 = stack.pop();
stack.push(sum1 + sum2);
}
@Override
public void visit(Multiplication multiplication) {
super.visit(multiplication);
long fac1 = stack.pop();
long fac2 = stack.pop();
stack.push(fac1 * fac2);
}
@Override
public void visit(LongValue longValue) {
super.visit(longValue);
stack.push(longValue.getValue());
}
};
StringBuilder b = new StringBuilder();
deparser.setBuffer(b);
parseExpression.accept(deparser);
System.out.println(expr + " = " + stack.pop() );
}
}
这是结果输出:
expr=4+5*6
4+5*6 = 34
expr=4*5+6
4*5+6 = 26
expr=4*(5+6)
4*(5+6) = 44
expr=4*(5+6)*(2+3)
4*(5+6)*(2+3) = 220
2、SQL分析示例
2.1、检索 SQL 语句的所有选定项
这里的任务是构建一个映射,其中别名是键,该别名的表达式是一个映射条目的值。
Select stmt = (Select) CCJSqlParserUtil.parse("SELECT col1 AS a, col2 AS b, col3 AS c FROM table WHERE col1 = 10 AND col2 = 20 AND col3 = 30");
Map<String, Expression> map = new HashMap<>();
for (SelectItem selectItem : ((PlainSelect)stmt.getSelectBody()).getSelectItems()) {
selectItem.accept(new SelectItemVisitorAdapter() {
@Override
public void visit(SelectExpressionItem item) {
map.put(item.getAlias().getName(), item.getExpression());
}
});
}
System.out.println("map " + map);
SQL 的选定项可以是不同的类型。我们现在对所谓的 SelectExpressionItems 感兴趣。这些是由可选的别名命名的表达式组成的那些。因此,我们使用 SelectItemVisitor 过滤这些项目。直接从我们的 SelectExpressionItem 我们可以得到别名和表达式。
这将返回:
map {a=col1, b=col2, c=col3}
3、SQL构建示例
3.1、构建一个简单的select
Select select = SelectUtils.buildSelectFromTable(new Table("mytable"));
select contains 现在从 mytable 中选择 *。
Select select = SelectUtils.buildSelectFromTableAndExpressions(new Table("mytable"), new Column("a"), new Column("b"));
select contains 现在从 mytable 中select a、b。
或者更简单,如果您不想构建正确的表达式树,您可以提供简单的文本表达式,这些表达式将被解析并包含在您的select中。
Select select = SelectUtils.buildSelectFromTableAndExpressions(new Table("mytable"), "a+b", "test");
3.2、扩展一个简单的insert
Insert insert = (Insert)CCJSqlParserUtil.parse("insert into mytable (col1) values (1)");
System.out.println(insert.toString());
//adding a column
insert.getColumns().add(new Column("col2"));
//adding a value using a visitor
insert.getItemsList().accept(new ItemsListVisitor() {
public void visit(SubSelect subSelect) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void visit(ExpressionList expressionList) {
expressionList.getExpressions().add(new LongValue(5));
}
public void visit(MultiExpressionList multiExprList) {
throw new UnsupportedOperationException("Not supported yet.");
}
});
System.out.println(insert.toString());
//adding another column
insert.getColumns().add(new Column("col3"));
//adding another value (the easy way)
((ExpressionList)insert.getItemsList()).getExpressions().add(new LongValue(10));
System.out.println(insert.toString());
3.3、替换字符串值
有人想发布一些 SQL,但想打乱所有具体值。所以这里有一个小例子来说明如何实现这一点。简而言之,访问者扫描整个树,找到所有 StringValues 并将当前值替换为 XXXX。
String sql ="SELECT NAME, ADDRESS, COL1 FROM USER WHERE SSN IN ('11111111111111', '22222222222222');";
Select select = (Select) CCJSqlParserUtil.parse(sql);
//Start of value modification
StringBuilder buffer = new StringBuilder();
ExpressionDeParser expressionDeParser = new ExpressionDeParser() {
@Override
public void visit(StringValue stringValue) {
this.getBuffer().append("XXXX");
}
};
SelectDeParser deparser = new SelectDeParser(expressionDeParser,buffer );
expressionDeParser.setSelectVisitor(deparser);
expressionDeParser.setBuffer(buffer);
select.getSelectBody().accept(deparser);
//End of value modification
System.out.println(buffer.toString());
//Result is: SELECT NAME, ADDRESS, COL1 FROM USER WHERE SSN IN (XXXX, XXXX)
3.4、更一般地替换语句中的字符串值
这是一种更通用的方法,用于替换各种语句中的字符串和长值。
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.util.deparser.ExpressionDeParser;
import net.sf.jsqlparser.util.deparser.SelectDeParser;
import net.sf.jsqlparser.util.deparser.StatementDeParser;
public class ReplaceColumnValues {
static class ReplaceColumnAndLongValues extends ExpressionDeParser {
@Override
public void visit(StringValue stringValue) {
this.getBuffer().append("?");
}
@Override
public void visit(LongValue longValue) {
this.getBuffer().append("?");
}
}
public static String cleanStatement(String sql) throws JSQLParserException {
StringBuilder buffer = new StringBuilder();
ExpressionDeParser expr = new ReplaceColumnAndLongValues();
SelectDeParser selectDeparser = new SelectDeParser(expr, buffer);
expr.setSelectVisitor(selectDeparser);
expr.setBuffer(buffer);
StatementDeParser stmtDeparser = new StatementDeParser(expr, selectDeparser, buffer);
Statement stmt = CCJSqlParserUtil.parse(sql);
stmt.accept(stmtDeparser);
return stmtDeparser.getBuffer().toString();
}
public static void main(String[] args) throws JSQLParserException {
System.out.println(cleanStatement("SELECT 'abc', 5 FROM mytable WHERE col='test'"));
System.out.println(cleanStatement("UPDATE table1 A SET A.columna = 'XXX' WHERE A.cod_table = 'YYY'"));
System.out.println(cleanStatement("INSERT INTO example (num, name, address, tel) VALUES (1, 'name', 'test ', '1234-1234')"));
System.out.println(cleanStatement("DELETE FROM table1 where col=5 and col2=4"));
}
}
它输出:
SELECT ?, ? FROM mytable WHERE col = ?
UPDATE table1 A SET A.columna = ? WHERE A.cod_table = ?
INSERT INTO example (num, name, address, tel) VALUES (?, ?, ?, ?)
DELETE FROM table1 WHERE col = ? AND col2 = ?
3.5、替换语句的 where 子句中的列名
SELECT col1 AS a, col2 AS b, col3 AS c FROM table WHERE col_1 = 10 AND col_2 = 20 AND col_3 = 30
在此 sql 中的所有列名中,应从中删除下划线。
Select stmt = (Select) CCJSqlParserUtil.parse("SELECT col1 AS a, col2 AS b, col3 AS c FROM table WHERE col_1 = 10 AND col_2 = 20 AND col_3 = 30");
System.out.println("before " + stmt.toString());
((PlainSelect)stmt.getSelectBody()).getWhere().accept(new ExpressionVisitorAdapter() {
@Override
public void visit(Column column) {
column.setColumnName(column.getColumnName().replace("_", ""));
}
});
System.out.println("after " + stmt.toString());
有趣的部分从将 getSelectBody() 转换为 PlainSelect 开始。如果有更复杂的 sql,也应该在这里使用访问者。现在使用 ExpressionVisitor 遍历来自 getWhere 的表达式。 ExpressionVisitorAdapter 是 ExpressionVisitor 的完整实现。因此,我们只需要重写 visit(Column column) 方法即可访问 where 表达式中的所有 Column 实例。替换本身是微不足道的。
4、SQL解析示例
4.1、简单的 SQL 解析
Statement stmt = CCJSqlParserUtil.parse("SELECT * FROM tab1");
从 stmt 开始,您可以深入了解解析结果。
4.2、SQL脚本解析
Statements stmt = CCJSqlParserUtil.parseStatements("SELECT * FROM tab1; SELECT * FROM tab2");
从 stmt 开始,您可以使用解析结果。
4.3、简单的表达式解析
Expression expr = CCJSqlParserUtil.parseExpression("a*(5+mycolumn)");
从 expr 开始,您可以使用解析结果。
4.4、括号引述
一些数据库使用括号引号作为标识符 (SqlServer):[mytable].[mycolumn]。由于 JSqlParser 的第 3 版括号被认为是每个默认数组寻址。
要使括号引号成为默认行为,您必须使用以下命令启用它:
CCJSqlParserUtil.parse("select * from mytable", parser -> parser.withSquareBracketQuotation(true));
例如查看#1001。
4.5、从 SQL 中提取表名
Statement statement = CCJSqlParserUtil.parse("SELECT * FROM MY_TABLE1");
Select selectStatement = (Select) statement;
TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
List<String> tableList = tablesNamesFinder.getTableList(selectStatement);
tableList 中是解析后的 SQL 语句的所有表名。表名查找器是 JSqlParser 访问者模式结构的一个示例。您可以使用访问者模式来遍历解析结果并对其进行计算。
4.6、将别名应用于所有表达式
Select select = (Select) CCJSqlParserUtil.parse("select a,b,c from test");
final AddAliasesVisitor instance = new AddAliasesVisitor();
select.getSelectBody().accept(instance);
结果,您将获得 SELECT a AS A1, b AS A2, c AS A3 FROM test。目前可以配置前缀。
4.7、将列或表达式添加到选择
Select select = (Select) CCJSqlParserUtil.parse("select a from mytable");
SelectUtils.addExpression(select, new Column("b"));
现在select包含 SELECT a, b FROM mytable。
4.8、可视化解析
有时您需要知道,JSqlParser 正在做什么来解析特殊的 SQL 语句。所以实现这一点的最简单方法是生成一个输出调试消息的解析器。
-
所以克隆 JSqlParser 存储库。
-
打开文件 JSqlParser.jjt。
-
编辑此文件中的选项部分。寻找以 DEBUG_ 开头的特殊选项。
options{...DEBUG_PARSER=true;DEBUG_LOOKAHEAD=false ;DEBUG_TOKEN_MANAGER=false;...} -
构建解析器。
你会得到这样的输出:
Call: Statement
Call: SingleStatement
Call: Select
Call: SelectBody
Call: PlainSelect
Consumed token: <"SELECT" at line 1 column 1>
Call: SelectItemsList
Call: SelectItem
Consumed token: <"*" at line 1 column 8>
Return: SelectItem
Return: SelectItemsList
Consumed token: <"FROM" at line 1 column 10>
Call: FromItem
Call: Table
Call: RelObjectName
Consumed token: <<S_IDENTIFIER>: "MYTABLE" at line 1 column 15>
Return: RelObjectName
Return: Table
Return: FromItem
5、SQL 验证示例
5.1、SQL 验证框架
从 4.0 开始,JSQLParser 框架包括一个验证框架。
验证框架映射了几种类型的验证,它们是接口 ValidationCapability 的实现。
目前存在以下实现:
- ParseCapability: 检查是否可以解析语句(始终包含在 Validation#validate() 中)
- FeatureSetValidation:
- DatabaseType: 检查已解析的语句对于支持的数据库语法是否有效
- Version: 检查已解析语句对于特定数据库版本是否有效。
- FeaturesAllowed: 检查语句是否仅使用允许的语法元素。
- DatabaseMetaDataValidation: 验证元数据,例如表、视图、列的名称是否存在
5.2、SQL 验证数据库类型/版本
检查已解析语句是否对所选数据库有效
String sql = "DROP INDEX IF EXISTS idx_tab2_id;";
// validate statement if it's valid for all given databases.
Validation validation = new Validation(Arrays.asList(DatabaseType.SQLSERVER, DatabaseType.MARIADB,
DatabaseType.POSTGRESQL, DatabaseType.H2), sql);
List<ValidationError> errors = validation.validate();
检查已解析语句是否对特定数据库版本有效。
// validate statement if it's valid for all given databases.
Validation validation = new Validation(Arrays.asList(PostgresqlVersion.V10), sql);
List<ValidationError> errors = validation.validate();
5.3、SQL 验证功能允许
针对预定义的 FeaturesAllowed.DML 集进行验证
// validate against pre-defined FeaturesAllowed.DML set
String sql = "CREATE TABLE tab1 (id NUMERIC(10), val VARCHAR(30))";
Validation validation = new Validation(Arrays.asList(FeaturesAllowed.DML), sql);
List<ValidationError> errors = validation.validate();
// only DML is allowed, got error for using a DDL statement
log.error (errors);
针对预定义的 FeaturesAllowed.SELECT 集进行验证
String sql = "SELECT * FROM myview v JOIN secondview v2 ON v.id = v2.ref";
Validation validation = new Validation(Arrays.asList(FeaturesAllowed.SELECT), sql);
List<ValidationError> errors = validation.validate();
// no errors, select - statement is allowed
if (errors.isEmpty()) {
// do something else with the parsed statements
Statements statements = validation.getParsedStatements();
}
针对您自己的 FeatureSet 进行验证
FeaturesAllowed exec = new FeaturesAllowed("EXECUTE", Feature.execute).unmodifyable();
组合多个预定义的 FeaturesAllowed 集
FeaturesAllowed myAllowedFeatures = new FeaturesAllowed("myAllowedFeatures")
.add (FeaturesAllowed.DDL, FeaturesAllowed.DML);
5.4、SQL 验证数据库MetaDataValidation
验证元数据,例如表、视图、列的名称是否存在
java.sql.Connection connection = ...;
String sql = "ALTER TABLE mytable ADD price numeric(10,5) not null";
Validation validation = new Validation(Arrays.asList(new JdbcDatabaseMetaDataCapability(connection,
// NamesLookup: Databases handle names differently
NamesLookup.UPPERCASE)), sql);
List<ValidationError> errors = validation.validate();
// do something else with the parsed statements
Statements statements = validation.getParsedStatements();
// check for validation-errors
if (!errors.isEmpty()) {
...
}
5.5、具有多个 ValidationCapability 的 SQL 验证
java.sql.Connection connection = ...;
String sql = "ALTER TABLE mytable ADD price numeric(10,5) not null";
Validation validation = new Validation(Arrays.asList(
DatabaseType.POSTGRESQL,
new JdbcDatabaseMetaDataCapability(connection,NamesLookup.LOWERCASE)), sql);
List<ValidationError> errors = validation.validate();
5.6、验证已解析的语句:
Statement statement = ...;
ValidationContext context = Validation.createValidationContext(
new FeatureConfiguration(), Arrays.asList(DatabaseType.POSTGRESQL));
Map<ValidationCapability, Set<ValidationException>> errors =
Validation.validate(statement, context);