最新代码在这里:https://blog.csdn.net/m0_54892309/article/details/129615905
增加了对于嵌套SQL语句的解析,并改进了相关代码~~~
最近在搞一个公司自研的数据中台项目,许多模块都有解析sql的需求。于是乎,开发一个能完美解析sql语句的工具类已经是迫在眉睫了!
到网上百度了两下,便发现了JSQLParser这个免费好用的工具类,相信很多朋友早就在用了吧~~~
话不多说,先来了解下JSQLParser里的两个主要工具类吧。
工具类 | 功能 | |
1 | CCJSqlParserUtil | 只能解析简单sql语句 |
2 | CCJSqlParserManager | 正确语法的sql都能解析 |
可以发现,CCJSqlParserUtil这个东西虽然简单好用功能强大精确无误(省略1000字),但是只能解析单表查询的简单sql,也就是说对于有子查询的sql是会直接报错的。
CCJSqlParserManager才是符合业务需求的真正好用的工具类,尽管它用起来确实麻烦,各种Expression表达式的解析,还有visit方法的重写,都是需要深刻理解才能用好的。
关于JSQLParser的基本语法网上都有,这里就不在赘述了。 在学习使用的过程中,我发现使用CCJSqlParserManager这个类去解析复杂sql时,无法正确解析出所有的表别名(也可能是我没理解到位...😅)。重写JSQLParser的visit方法应该可以实现表别名的解析,我这里就用自己比较能接受的方式来了。
实战环节:
maven依赖
com.github.jsqlparser jsqlparser4.4
实体类
NormalSqlStructureDto.class
/** SQL语句 */ private String sql; /** 表名 */ private ListtableNames; /** 检索项 */ private List selectItems; /** 字段和表的映射关系 */ private List colMappings;
ColMappingDto.class
/** 字段名 */ private String name; /** 字段别名 */ private String alias; /** 关联表 */ private Object table; private String type;
主要代码
public class JsqlParserUtil { public static void main(String[] args) throws JSQLParserException { // 输入一个sql String sql = "select t11.*,t1.* \n" + "from original_data.edu_college_student As t1\n" + "JOIN original_data.edu_college_test_score t11\n" + "on t1.s_id = t11.s_id \n" + "where 1=1 \n"; NormalSqlStructureDto normalSqlStructureDto = getStructure(sql.replace("\r", " ").replace("\n", " "), true); normalSqlStructureDto.getTableNames().forEach(System.out::println); System.out.println("==============================================="); normalSqlStructureDto.getSelectItems().forEach(System.out::println); System.out.println("end"); } /** * 构建表名和表别名的对应关系 * * @param tableMapping * @param sql * @param tblAlias */ private static void buildTblMapping(MaptableMapping, String sql, String tblAlias) { if (StringUtils.isNotEmpty(tblAlias)) { if (CollectionUtils.isEmpty(tableMapping) || Objects.isNull(tableMapping.get(tblAlias))) { sql = sql.replaceAll("(?i)\\s+as\\s+", " "); String regex = "(from|join)\\s+(\\w+\\.)?\\w+\\s+".concat(tblAlias).concat("\\s+"); Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); Matcher m = p.matcher(sql.replaceAll("[\n\r]", " ")); String replaceReg = "(?i)(from|join|" + tblAlias + ")"; while (m.find()) { tableMapping.put(tblAlias, m.group(0).replaceAll(replaceReg, "").trim()); } } } /** * 解析sql结构 * * @param sql * @param isAlias true|false 是否使用别称
eg. 【s_id as id】 => 【id】
* @return * @throws ServiceException * @throws JSQLParserException */ public static NormalSqlStructureDto getStructure(String sql, boolean isAlias) throws ServiceException, JSQLParserException { NormalSqlStructureDto normalSqlStructureDto = new NormalSqlStructureDto(); if (StringUtils.isEmpty(sql)) { throw new ServiceException("请先输入SQL语句"); } normalSqlStructureDto.setSql(sql); sql = sql.replaceAll("(\\$\\{\\w*\\})|(\\{\\{\\w+\\}\\})", "''"); // 1.解析表名 CCJSqlParserManager parserManager = new CCJSqlParserManager(); // 解析SQL为Statement对象 Statement statement = parserManager.parse(new StringReader(sql)); // 创建表名发现者对象 TablesNamesFinder tablesNamesFinder = new TablesNamesFinder(); // 获取到表名列表 ListtableNameList = tablesNamesFinder.getTableList(statement); normalSqlStructureDto.setTableNames(tableNameList); // 表别名映射 Map tableMapping = new HashMap<>(); tableNameList.forEach(i -> tableMapping.put(i, i)); // 字段和表的映射 List colMappingList = new ArrayList<>(); // 2.解析查询元素 列,函数等 Select select = (Select) CCJSqlParserUtil.parse(sql); PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); // FromItem fromItem = plainSelect.getFromItem(); // System.out.println(JSON.toJSON(fromItem).toString()); // fromItem.getAlias(); List selectItems = plainSelect.getSelectItems(); List columnList = new ArrayList<>(); if (!CollectionUtils.isEmpty(selectItems)) { for (SelectItem selectItem : selectItems) { ColMappingDto colMapping = new ColMappingDto(); String columnName = ""; String tblAlias = ""; try { if (selectItem instanceof SelectExpressionItem) { SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem; Alias alias = selectExpressionItem.getAlias(); Expression expression = selectExpressionItem.getExpression(); // FIXME: 2023/3/9 Column col = ((Column) expression); Table colTbl = col.getTable(); if (Objects.nonNull(colTbl)) { tblAlias = colTbl.getName(); } buildTblMapping(tableMapping, sql, tblAlias); if (!isAlias) { columnName = selectItem.toString(); } else 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("`", ""); colMapping.setName(col.getColumnName()); if (Objects.nonNull(alias) && StringUtils.isNotEmpty(alias.getName())) { colMapping.setAlias(alias.getName()); } colMapping.setTable(tableMapping.get(tblAlias)); } else if (selectItem instanceof AllTableColumns) { AllTableColumns allTableColumns = (AllTableColumns) selectItem; columnName = allTableColumns.toString(); if (columnName.indexOf(".") > -1) { tblAlias = columnName.substring(0, columnName.indexOf(".")).trim(); buildTblMapping(tableMapping, sql, tblAlias); colMapping.setTable(tableMapping.get(tblAlias)); } else { colMapping.setTable(tableNameList); } colMapping.setName(columnName); } else if (selectItem.toString().equals("*")) { columnName = selectItem.toString(); colMapping.setName(columnName); colMapping.setTable(tableNameList); } else { columnName = selectItem.toString(); colMapping.setName(columnName); colMapping.setType("varchar"); } } catch (Exception e) { columnName = selectItem.toString(); colMapping.setName(columnName); colMapping.setType("varchar"); colMapping.setTable(null); } columnList.add(columnName); colMappingList.add(colMapping); } normalSqlStructureDto.setSelectItems(columnList); normalSqlStructureDto.setColMappings(colMappingList); } return normalSqlStructureDto; } }
参考:https://blog.csdn.net/qq_41541619/article/details/104576427这篇博客,讲得非常详细。