如何用Java定位SQL中含{变量}的条件并替换为1=1
解决SQL条件中带变量的精准替换问题
我之前刚好踩过类似的坑——直接用字符串拆分或者简单正则处理带括号的SQL条件,很容易把逻辑搞乱,比如你遇到的括号内OR条件拆分错误的情况。下面分享两种靠谱的解决方案:
方案一:用专业SQL解析库(推荐)
Java生态里有专门的SQL解析库可以处理这种场景,比如JSqlParser,它能把SQL解析成抽象语法树(AST),让你精准遍历、修改每个条件节点,完全不用担心括号嵌套或者逻辑运算符优先级的问题。
示例代码(JSqlParser)
首先引入Maven依赖(用最新稳定版即可):
<dependency> <groupId>com.github.jsqlparser</groupId> <artifactId>jsqlparser</artifactId> <version>4.7</version> </dependency>
然后编写处理逻辑:
import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.LongValue; import net.sf.jsqlparser.expression.operators.relational.EqualsTo; import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.select.SelectBody; import net.sf.jsqlparser.util.deparser.ExpressionDeParser; import net.sf.jsqlparser.util.deparser.SelectDeParser; import java.util.HashMap; import java.util.Map; public class SqlVariableReplacer { public static void main(String[] args) throws JSQLParserException { String originalSql = "Select * From customer c inner join address a on c.id = a.customer_id where c.id > {var1} AND (c.name LIKE {var2} OR a.city = {var3})"; Map<String, Object> variables = new HashMap<>(); variables.put("var1", 2); variables.put("var2", "*ALL"); variables.put("var3", "'aa'"); // 解析SQL为语法树 Select select = (Select) CCJSqlParserUtil.parse(originalSql); SelectBody selectBody = select.getSelectBody(); // 自定义表达式处理器,遍历并替换目标条件 ExpressionDeParser expressionDeParser = new ExpressionDeParser() { @Override public void visit(Expression expr) { String exprStr = expr.toString(); for (Map.Entry<String, Object> entry : variables.entrySet()) { String varKey = "{" + entry.getKey() + "}"; if (exprStr.contains(varKey)) { Object value = entry.getValue(); if ("*ALL".equals(value)) { // 替换成1=1 super.visit(new EqualsTo(new LongValue(1), new LongValue(1))); return; } else { // 替换为变量实际值 String replacedExpr = exprStr.replace(varKey, value.toString()); try { super.visit(CCJSqlParserUtil.parseExpression(replacedExpr)); return; } catch (JSQLParserException e) { e.printStackTrace(); } } } } // 非目标条件,继续遍历子节点 super.visit(expr); } }; // 生成处理后的SQL StringBuffer buffer = new StringBuffer(); SelectDeParser selectDeParser = new SelectDeParser(expressionDeParser, buffer); expressionDeParser.setSelectVisitor(selectDeParser); expressionDeParser.setBuffer(buffer); selectBody.accept(selectDeParser); System.out.println(buffer.toString()); // 输出结果:SELECT * FROM customer c INNER JOIN address a ON c.id = a.customer_id WHERE c.id > 2 AND (1=1 OR a.city = 'aa') } }
这个方案的核心是通过AST遍历每个独立的条件表达式,精准判断是否包含目标变量,再根据变量值决定替换逻辑,完全不会破坏括号内的逻辑结构。
方案二:手动递归解析条件表达式(不依赖第三方库)
如果不想引入外部库,可以自己实现一个简单的递归解析器,核心思路是把条件表达式拆解成树形结构:
- 以
AND/OR为节点,递归处理左右子表达式 - 遇到括号时,优先解析括号内的子表达式
- 对每个叶子节点(单个条件,比如
c.id > {var1})进行变量检查和替换
举个简化的逻辑框架:
public class SimpleSqlProcessor { // 递归处理条件字符串 public static String processCondition(String condition, Map<String, Object> variables) { // 先处理最内层括号的子条件 int start = condition.indexOf('('); if (start != -1) { int end = findMatchingBracket(condition, start); String inner = condition.substring(start + 1, end); String processedInner = processCondition(inner, variables); condition = condition.substring(0, start) + processedInner + condition.substring(end + 1); return processCondition(condition, variables); } // 拆分OR条件(优先处理低优先级运算符) String[] orParts = splitOnOperator(condition, "OR"); for (int i = 0; i < orParts.length; i++) { // 拆分AND条件 String[] andParts = splitOnOperator(orParts[i], "AND"); for (int j = 0; j < andParts.length; j++) { String part = andParts[j].trim(); // 检查并替换变量 for (Map.Entry<String, Object> entry : variables.entrySet()) { String varKey = "{" + entry.getKey() + "}"; if (part.contains(varKey)) { Object value = entry.getValue(); andParts[j] = "*ALL".equals(value) ? "1=1" : part.replace(varKey, value.toString()); } } } orParts[i] = String.join(" AND ", andParts); } return String.join(" OR ", orParts); } // 找到匹配的右括号 private static int findMatchingBracket(String str, int start) { int count = 1; for (int i = start + 1; i < str.length(); i++) { if (str.charAt(i) == '(') count++; else if (str.charAt(i) == ')') count--; if (count == 0) return i; } return -1; // 实际场景需添加格式异常处理 } // 按逻辑运算符拆分,忽略括号内的运算符 private static String[] splitOnOperator(String str, String operator) { // 这里需要实现完整的括号层级判断逻辑,避免拆分括号内的运算符 // 以下是简化示例,实际需完善 return str.split("\\s+" + operator + "\\s+"); } }
这个方法需要自己处理很多细节(比如括号匹配、运算符优先级、空格处理),适合简单场景,复杂SQL容易出问题,所以还是推荐用专业库。
为什么不推荐正则表达式?
简单正则无法处理嵌套括号的场景,比如你示例中的(c.name LIKE {var2} OR a.city = {var3}),如果用AND|OR拆分,会把括号内的OR也拆出来,导致替换逻辑混乱。虽然可以写复杂的正则来匹配括号,但维护成本极高,而且很容易漏处理边缘情况(比如多层嵌套括号、带注释的SQL)。
内容的提问来源于stack exchange,提问作者Lusi




