JSqlParser是一款常用的SQL解析工具,能够将SQL语句解析为结构化的语法树,方便开发者进行SQL分析、改写等操作。在实际业务中,我们常常需要限制系统支持的SQL语法范围,拒绝不符合要求的语句,这就需要基于JSqlParser实现严格的语法校验逻辑。

JSqlParser基础解析流程
JSqlParser的核心解析逻辑由CCJSqlParserUtil类提供,通过调用parse方法可以将SQL字符串转换为对应的语句对象。如果SQL语法不符合JSqlParser的默认支持范围,解析过程会直接抛出异常。
基础的解析示例如下:
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
public class BasicParseDemo {
public static void main(String[] args) {
String sql = "SELECT id, name FROM user WHERE age > 18";
try {
// 解析SQL语句,返回结构化的Statement对象
Statement statement = CCJSqlParserUtil.parse(sql);
System.out.println("解析成功,语句类型:" + statement.getClass().getSimpleName());
} catch (Exception e) {
System.out.println("解析失败,原因:" + e.getMessage());
}
}
}
捕获解析异常拒绝不兼容语法
JSqlParser默认支持的SQL语法覆盖大部分常用场景,但如果遇到不支持的语法,比如某些数据库特有的语法,解析时会抛出JSQLParserException。我们可以通过捕获这个异常,直接拒绝不兼容的SQL语句。
示例代码如下:
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
public class SyntaxRejectDemo {
public static boolean validateSql(String sql) {
try {
CCJSqlParserUtil.parse(sql);
return true;
} catch (JSQLParserException e) {
// 解析失败说明存在不兼容语法,返回false表示校验不通过
return false;
}
}
public static void main(String[] args) {
// 测试一条包含不支持语法的SQL,比如某些数据库特有的CONNECT BY语法
String unsupportedSql = "SELECT id FROM user CONNECT BY PRIOR id = parent_id";
boolean isValid = validateSql(unsupportedSql);
System.out.println("SQL校验结果:" + isValid);
}
}
自定义校验规则扩展功能支持限制
仅依赖解析异常捕获只能判断语法是否符合JSqlParser的默认支持范围,无法细化到具体功能维度。比如我们需要禁止DELETE语句、只允许查询类语句,就需要在解析成功后对语法树做进一步校验。
我们可以通过判断解析得到的Statement对象类型,实现功能维度的限制:
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.update.Update;
public class CustomValidateDemo {
// 只允许SELECT语句,拒绝其他类型的SQL
public static boolean validateOnlySelect(String sql) {
try {
Statement statement = CCJSqlParserUtil.parse(sql);
// 判断语句类型是否为Select,不是则返回false
if (statement instanceof Select) {
return true;
} else if (statement instanceof Delete || statement instanceof Insert || statement instanceof Update) {
return false;
}
return false;
} catch (JSQLParserException e) {
return false;
}
}
public static void main(String[] args) {
String selectSql = "SELECT id FROM user";
String deleteSql = "DELETE FROM user WHERE id = 1";
System.out.println("查询SQL校验结果:" + validateOnlySelect(selectSql));
System.out.println("删除SQL校验结果:" + validateOnlySelect(deleteSql));
}
}
深度校验语法树节点
如果需要更细粒度的校验,比如禁止SQL中使用子查询、禁止使用某些函数,就需要遍历解析后的语法树节点进行判断。JSqlParser的语法树节点都实现了对应的访问者接口,我们可以通过自定义访问者实现节点遍历。
以下示例实现禁止SQL中使用子查询的校验:
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SubSelect;
import net.sf.jsqlparser.util.TablesNamesFinder;
import net.sf.jsqlparser.visitor.VisitorAdapter;
import java.util.ArrayList;
import java.util.List;
public class DeepValidateDemo {
// 自定义访问者,收集SQL中的子查询信息
static class SubSelectVisitor extends VisitorAdapter {
private List<SubSelect> subSelects = new ArrayList<>();
@Override
public void visit(SubSelect subSelect) {
subSelects.add(subSelect);
super.visit(subSelect);
}
public List<SubSelect> getSubSelects() {
return subSelects;
}
}
public static boolean validateNoSubQuery(String sql) {
try {
Statement statement = CCJSqlParserUtil.parse(sql);
if (statement instanceof Select) {
SubSelectVisitor visitor = new SubSelectVisitor();
((Select) statement).getSelectBody().accept(visitor);
// 如果存在子查询则返回false
return visitor.getSubSelects().isEmpty();
}
return true;
} catch (JSQLParserException e) {
return false;
}
}
public static void main(String[] args) {
String sqlWithSubQuery = "SELECT id FROM (SELECT id FROM user WHERE age > 18) tmp";
String sqlWithoutSubQuery = "SELECT id FROM user WHERE age > 18";
System.out.println("包含子查询的SQL校验结果:" + validateNoSubQuery(sqlWithSubQuery));
System.out.println("不包含子查询的SQL校验结果:" + validateNoSubQuery(sqlWithoutSubQuery));
}
}
校验逻辑整合建议
实际业务中可以将上述几种校验方式整合,形成完整的校验流程:首先调用JSqlParser解析SQL,捕获解析异常拒绝基础不兼容语法;然后判断语句类型,限制允许的操作类型;最后遍历语法树,校验细粒度的语法规则。这样就能实现严格的SQL功能支持校验,准确拒绝所有不兼容的语法。
需要注意的是,JSqlParser的版本不同支持的语法范围会有差异,如果需要支持特定数据库的语法,可以选择对应版本的JSqlParser,或者在自定义校验规则中补充对应语法的兼容处理。
JSqlParserSQL校验语法解析不兼容语法拒绝修改时间:2026-07-18 22:42:32