制作网站用什么软件,购物网站答辩ppt怎么做,外贸阿里巴巴国际站,知乎关键词排名简述
Java解析SQL语句有很多工具都可以做到#xff0c;比如Mybatis、Druid、目前用来用去最全面的仍然是Jsqlparser#xff0c;它是一个Github上的开源项目#xff0c;JSqlParser是一个用于解析SQL语句的Java库#xff0c;它可以帮助开发者分析和操作SQL语句的结构。无论是…简述
Java解析SQL语句有很多工具都可以做到比如Mybatis、Druid、目前用来用去最全面的仍然是Jsqlparser它是一个Github上的开源项目JSqlParser是一个用于解析SQL语句的Java库它可以帮助开发者分析和操作SQL语句的结构。无论是从事数据库开发、SQL性能优化还是需要解析SQL语句以进行其他操作JSqlParser都能提供强大的支持
特点 支持多种SQL方言JSqlParser支持多种数据库的SQL方言包括MySQL、Oracle、PostgreSQL等这使得在不同数据库之间进行SQL语句解析变得更加方便。 灵活的操作JSqlParser以多种方式操作SQL语句例如修改查询条件、提取表名、列名等甚至整个SQL语句中使用到的函数从而满足各种需求。 易于集成JSqlParser可以轻松集成到您的Java项目中只需将其作为依赖项添加到项目中即可。 社区支持JSqlParser拥有一个活跃的社区许多开发者为其提供贡献使得这个工具不断得到改进和优化它的主要操刀人manticore-projects (github.com)也非常负责并愿意解答各种问题和参与讨论
环境准备
将Jsqlparser直接添加到项目中
Maven
dependencygroupIdcom.github.jsqlparser/groupIdartifactIdjsqlparser/artifactIdversion4.9/version
/dependencyGradle
implementation(com.github.jsqlparser:jsqlparser:4.9)快速使用
使用原则
假设现在有一条简单的SQL语句需要拿来解析首先应该保证这个SQL在结构上是没有问题的最好是放在数据库中可以直接运行的不夹杂不应该的标点符号那样解析起来才不会出错
使用案例
以下是一个简单的SQL语句并且这句SQL没有违反原则是一条基本可以正常运行的SQL语句
SELECT id,name,nickname,age,job,department FROM staff_member WHERE nickname 刘解析SQL语句
接下来使用Jsqlparser去解析语句其中第二行则是最基本的将SQL语句字符串拿来解析如果这句SQL语句违反了原则例如存在特殊标点符号或者不符合SQL语句那么在第二行就会产生异常
String sql SELECT id,name,nickname,age,job,department FROM staff_member WHERE nickname 刘;
// Parse SQL
Statement statement CCJSqlParserUtil.parse(sql);
Select selectStatement (Select) statement;
log.info( JsqlParser SQL: {}, selectStatement.toString());正常情况下将得到一个包含各种属性的statement这意味着一条SQL成功被解析并被赋予到一个对象的各个属性中 认识Statement
熟悉JDBC的程序员一般都知道Statement其实就是语句的意思不过在Jsqlparser中Statement已经面向对象被设计成了一个interface之所以设计成interface大概都可以猜到因为Jsqlparser既然要去解析SQL那必然要对SQL语句做区分到底是Select、还是Insert、还是Delete、甚至是Create而Jsqlparser对每种语句都做了一个封装它们都继承了Statement 所以一条SQL语句根据不同情况都有适配的对象例如Select语句对应着net.sf.jsqlparser.statement.select.Select对象而Insert也有自己的对象所以我们都可以通过将Statement强转为它所对应的对象来获取或改变其中的属性这也是解析SQL的一大目的
其实在Jsqlparser成功解析SQL语句之后statement就已经有了它的类型
String sql SELECT id,name,nickname,age,job,department FROM staff_member WHERE nickname 刘;// Parse SQLStatement statement CCJSqlParserUtil.parse(sql);if(statement instanceof Select){Select selectStatement (Select) statement;log.info( JsqlParser SQL: {}, selectStatement.toString());
}
if(statement instanceof Insert){Insert insertStatement (Insert) statement;log.info( JsqlParser SQL: {}, insertStatement.toString());
}
if(statement instanceof Update){Update updateStatement (Update) statement;log.info( JsqlParser SQL: {}, updateStatement.toString());
}
if (statement instanceof Delete) {Delete deleteStatement (Delete) statement;log.info( JsqlParser SQL: {}, statement.toString());
}分析语句
查询语句
在statement成功解析SQL语句之后通过PlainSelect就可以拿到SQL语句中的各个元素
String sql SELECT id,name,nickname,age,job,department FROM staff_member WHERE nickname 刘;
// Parse SQL
Statement statement CCJSqlParserUtil.parse(sql);
if(statement instanceof Select){Select selectStatement (Select) statement;log.info( JsqlParser SQL: {}, selectStatement.toString());PlainSelect plainSelect selectStatement.getPlainSelect();log.info( FromItem: {}, plainSelect.getFromItem());log.info( SelectItem: {},plainSelect.getSelectItems());log.info( Where: {},plainSelect.getWhere());
}运行结果 JsqlParser SQL: SELECT id, name, nickname, age, job, department FROM staff_member WHERE nickname 刘FromItem: staff_memberSelectItem: [id, name, nickname, age, job, department]Where: nickname 刘PlainSelect常用方法 获取和设置表From子句: FromItem getFromItem(): 获取FROM子句中的表或子查询。void setFromItem(FromItem fromItem): 设置FROM子句中的表或子查询。 获取和设置选择项SelectItems: ListSelectItem getSelectItems(): 获取SELECT子句中的选择项列表。void setSelectItems(ListSelectItem selectItems): 设置SELECT子句中的选择项列表。 获取和设置WHERE子句: Expression getWhere(): 获取WHERE子句的条件表达式。void setWhere(Expression where): 设置WHERE子句的条件表达式。 获取和设置GROUP BY子句: ListExpression getGroupByColumnReferences(): 获取GROUP BY子句中的列引用列表。void setGroupByColumnReferences(ListExpression groupByColumnReferences): 设置GROUP BY子句中的列引用列表。 获取和设置ORDER BY子句: ListOrderByElement getOrderByElements(): 获取ORDER BY子句中的排序元素列表。void setOrderByElements(ListOrderByElement orderByElements): 设置ORDER BY子句中的排序元素列表。 获取和设置LIMIT子句: Limit getLimit(): 获取LIMIT子句。void setLimit(Limit limit): 设置LIMIT子句。 获取和设置DISTINCT关键字: boolean isDistinct(): 检查SELECT语句是否使用了DISTINCT关键字。void setDistinct(boolean distinct): 设置SELECT语句是否使用DISTINCT关键字。 获取和设置INTO子句用于SELECT INTO语句: SubSelect getIntoTables(): 获取INTO子句中的表。void setIntoTables(SubSelect intoTables): 设置INTO子句中的表。 获取和设置HAVING子句: Expression getHaving(): 获取HAVING子句的条件表达式。void setHaving(Expression having): 设置HAVING子句的条件表达式。 获取和设置别名: String getAlias(): 获取SELECT语句的别名。void setAlias(String alias): 设置SELECT语句的别名。 获取和设置子查询SubSelect: SubSelect getSubSelect(): 获取子查询。void setSubSelect(SubSelect subSelect): 设置子查询。 获取和设置联合查询Union: ListPlainSelect getUnion(): 获取联合查询的SELECT语句列表。void setUnion(ListPlainSelect union): 设置联合查询的SELECT语句列表。
新增语句
新增语句和查询语句一样只不过由于Insert没有Select语句那么复杂所以Jsqlparsert并没有专门设计一个类似PlainSelect extend Select这样一个类而是直接通过Insert对象就可以获取和操作Insert语句中的内容
String sql INSERT INTO employees (employee_id, employee_name, department) VALUES (1, John Doe, Human Resources);
// Parse SQL
Statement statement CCJSqlParserUtil.parse(sql);
if (statement instanceof Insert) {Insert insertStatement (Insert) statement;log.info( JsqlParser SQL: {}, insertStatement.toString());log.info( Table: {}, insertStatement.getTable());log.info( Columns: {}, insertStatement.getColumns());log.info( ItemsList: {}, insertStatement.getValues());
}运行结果 JsqlParser SQL: INSERT INTO employees (employee_id, employee_name, department) VALUES (1, John Doe, Human Resources) Table: employees Columns: employee_id, employee_name, department ItemsList: VALUES (1, John Doe, Human Resources)Insert常用方法
Table getTable(): 获取插入语句中的目标表。ListColumn getColumns(): 获取插入语句中要插入的列的列表。ItemsList getValues(): 获取插入语句中的值列表可以是单个值或者子查询。String getPrefix(): 获取INSERT关键字前的前缀如INSERT INTO或者INSERT IGNORE。void setTable(Table table): 设置插入语句中的目标表。void setColumns(ListColumn columns): 设置插入语句中要插入的列的列表。void setValues(ItemsList values): 设置插入语句中的值列表。void setPrefix(String prefix): 设置INSERT关键字前的前缀。
更新语句
Update和Insert是一样的内容相对于Select较为简单通过Update对象即可获得相关内容
String sql UPDATE employees SET department Human Resources WHERE employee_id 1;
// Parse SQL
Statement statement CCJSqlParserUtil.parse(sql);
if (statement instanceof Update) {Update updateStatement (Update) statement;log.info( JsqlParser SQL: {}, updateStatement.toString());Table table updateStatement.getTable();log.info(Table Name: {}, table.getName());log.info( Columns: {}, updateStatement.getColumns());// 获取更新项ListUpdateSet updateSets updateStatement.getUpdateSets();for (UpdateSet updateSet : updateSets) {for (Expression expression : updateSet.getColumns()) {log.info( Expression: {}, expression.toString());}}log.info( ItemsList: {}, updateStatement.getExpressions());Expression where updateStatement.getWhere();log.info( Where: {}, where.toString());
}运行结果 JsqlParser SQL: UPDATE employees SET department Human Resources WHERE employee_id 1
Table Name: employeesColumns: departmentExpression: departmentItemsList: Human ResourcesWhere: employee_id 1删除语句 String sql DELETE FROM table_name WHERE condition;Statement statement CCJSqlParserUtil.parse(sql);if (statement instanceof Delete) {Delete deleteStatement (Delete) statement;// 获取要删除的表Table table deleteStatement.getTable();System.out.println(Table Name: table.getName());// 获取WHERE条件Expression where deleteStatement.getWhere();System.out.println(Where Condition: where.toString());}运行结果
Table Name: table_name
Where Condition: condition从SQL语句中提取表名
Statement statement CCJSqlParserUtil.parse(SELECT * FROM MY_TABLE1);
Select selectStatement (Select) statement;
TablesNamesFinder tablesNamesFinder new TablesNamesFinder();
ListString tableList tablesNamesFinder.getTableList(selectStatement);最终tableList里将存入所有给出的SQL语句中的表名以上案例只有一个表名
为SQL语句各个字段表达式添加别名
String sql SELECT id,name,nickname,age,job,department FROM staff_member WHERE nickname 刘;
// Parse SQL
Statement statement CCJSqlParserUtil.parse(sql);
if(statement instanceof Select ){Select selectStatement (Select) statement;final AddAliasesVisitor instance new AddAliasesVisitor();instance.setPrefix(t);selectStatement.accept(instance);log.info( JSqlParser finalSQL: {}, selectStatement);
}动态加字段加表达式加条件
使用SelectUtils为一个Select语句增加查询的字段
Select select (Select) CCJSqlParserUtil.parse(select mydate from mytable);
SelectUtils.addExpression(select, new Column(mylocation));增加一个表达式
Select select (Select) CCJSqlParserUtil.parse(select a from mytable);
SelectUtils.addExpression(select, new Column(b));
assertEquals(SELECT a, b FROM mytable, select.toString());Addition add new Addition();
add.setLeftExpression(new LongValue(5));
add.setRightExpression(new LongValue(6));
SelectUtils.addExpression(select, add);assertEquals(SELECT a, b, 5 6 FROM mytable, select.toString());增加一个Join
动态添加Join可以为Join增加表达式以及设置Join的表并且通过setLeft()、setRight()、setInner()可以设置join的方向最终它会生成对应的SQL语句
Select select (Select) CCJSqlParserUtil.parse(select a from mytable);
final EqualsTo equalsTo new EqualsTo();
equalsTo.setLeftExpression(new Column(a));
equalsTo.setRightExpression(new Column(b));
Join addJoin SelectUtils.addJoin(select, new Table(mytable2), equalsTo);
addJoin.setLeft(true);
assertEquals(SELECT a FROM mytable LEFT JOIN mytable2 ON a b, select.toString());用SelectUtils构建一个SQL语句
下面是SelectUtils里面的一些方法可以看到不光是为查询语句增加表达式、Join和分组其次还可以使用build等方法去构建一个SQL语句 这里是一个案例构建了一个查询语句其中也使用到了addGroupBy
Select select SelectUtils.buildSelectFromTableAndExpressions(new Table(mytable),new Column(a), new Column(b));
SelectUtils.addExpression(select, new Column(c));
final EqualsTo equalsTo new EqualsTo();
equalsTo.setLeftExpression(new Column(id));
equalsTo.setRightExpression(new Column(1));
SelectUtils.addGroupBy(select, new Column(d));
log.info( JsqlParser Build SQL: {}, select.toString());输出结果 JsqlParser Build SQL: SELECT a, b, c FROM mytable GROUP BY d简短的总结
上面的代码虽然不少但实际上真正需要熟悉的只有一个就是直接调用CCJSqlParserUtil.parse(sql);去获得Statement然后通过Statement去操作和获取解析后的SQL中的内容非常简单方便
实际应用场景
说了那么多JSQLPARSER的使用或许很多朋友并不能联想到有哪些具体可以用到它的地方实际上想要开发一个优秀的软件产品那么细节是少不了的SQL是BS软件的本质之一那么针对SQL我们能做的还有很多以下列举几个常见的场景 SQL审计和分析: 审计SQL语句检查是否包含潜在的安全漏洞如SQL注入。分析SQL语句的性能检查是否存在可以优化的查询条件。 数据库迁移和同步: 在迁移数据库时使用JSqlParser解析源数据库的SQL语句并生成目标数据库的相应语句。数据库同步工具可以使用JSqlParser来解析和生成SQL语句以实现数据的同步。 动态SQL生成: 应用程序需要生成动态SQL语句以执行不同的操作JSqlParser可以用来解析这些动态生成的SQL语句。 SQL测试和验证: 在开发过程中使用JSqlParser来验证SQL语句的正确性。单元测试中使用JSqlParser来解析和执行测试用例中的SQL语句。 SQL注入防护: 在应用程序中使用JSqlParser来解析和分析用户输入的SQL查询以防止SQL注入攻击。 数据库管理工具: 数据库管理工具可以使用JSqlParser来解析和显示SQL语句的结构帮助开发者理解查询的逻辑。 代码生成: 在生成数据库访问层代码时使用JSqlParser来解析SQL语句并生成相应的数据访问对象DAO或查询对象DTO。 SQL格式化: 使用JSqlParser来格式化SQL语句使其更易于阅读和理解。 SQL优化: 通过分析SQL语句的结构可以提出性能优化建议。 数据处理工具: 在数据处理和转换工具中使用JSqlParser来解析和生成SQL语句以实现数据的导入和导出。 在SpringbootMybaits中使用
如果使用纯原生Mybatis那么我们需要手动在maven中加入Jsqlparser的支持但如果使用Mybatis plus那么就无需自己再引用Mybaits plus自带Jsqlparser
上面举的很多例子都很简单拿一个SQL语句解析而已这种情况是手动化的通常见于单元测试等情况但如果在项目中想要通过被动的方式让项目自己去解析SQL语句就需要分析项目的具体情况例如在Mybatis中通过Interceptor就可以获得到项目中真正去执行的SQL语句详见Mybatis 的 Interceptor拦截器 与 JSqlparser 结合解析SQL 使SpringBoot项目多数据库兼容的尝试_mybatis设置jsqlparser-CSDN博客
通过Mybatis的拦截器我们拿到了项目执行的SQL语句再通过Jsqlparser去解析并做一定的处理例如以上提到的那些实际应用场景
高级特性很实用
Jsqlparser在解析SQL语句的过程中每一个节点都会被解析成一个叫SimpleNode的对象它包含着各个节点的属性这仿佛就像Dom4j解析XML的时候所有的元素都视为Node一样解析之后的内容都是节点而循环这些节点Jsqlparser给出了相应的方法提供了用于遍历节点的接口CCJSqlParserVisitor而它的默认实现则是CCJSqlParserDefaultVisitor在这里创建一个自己的类并通过继承 CCJSqlParserDefaultVisitor 重写它的visit 方法便可以实现自己的策略更加方便的去操作解析内容
public class SQLModifier extends CCJSqlParserDefaultVisitor {Overridepublic Object visit(SimpleNode node, Object data) {Object value node.jjtGetValue();switch (node.getId()) {case CCJSqlParserTreeConstants.JJTTABLENAME:break;case CCJSqlParserTreeConstants.JJTCOLUMN:break;case CCJSqlParserTreeConstants.JJTFUNCTION:break;default:break;}return super.visit(node, data);}
}调用自定义的Visitor
String originalSql select * from user where id 1;
CCJSqlParser parser CCJSqlParserUtil.newParser(originalSql);
Statement statement parser.Statement();
parser.getASTRoot().jjtAccept(sqlTestModifier, null);以上代码做了一个自定义的visitor重写的visit方法中可以看到形参SimpleNode而调用这个自定义的Visitor之后语句则会被拆解依次进入到visit方法中通过node.jjtGetValue可以获得节点信息而node.getId()实则是获取节点的类型而Switch-case中的常量分别代表了在解析SQL语句时生成的抽象语法树AST abstract syntax tree中不同类型的节点每个节点对应一个特定的SQL构造如SELECT、FROM、WHERE等。下面是对这些常量代表的SQL构造的简要说明
JJTSTATEMENT: 代表一个SQL语句。JJTVOID: 可能代表一个空语句或者不返回结果的语句。JJTBLOCK: 代表一个语句块可能包含多个语句。JJTSTATEMENTS: 代表一个包含多个语句的列表。JJTCOLUMN: 代表一个列名。JJTTABLENAME: 代表一个表名。JJTSELECT: 代表一个SELECT查询。JJTPARENTHESEDSELECT: 代表被括号包围的SELECT查询。JJTLATERALVIEW: 代表LATERAL VIEW子句常用于Hive SQL。JJTFORCLAUSE: 代表FOR子句。JJTLATERALSUBSELECT: 代表LATERAL子查询。JJTPLAINSELECT: 代表一个简单的SELECT查询不包含UNION等。JJTSETOPERATIONLIST: 代表一个集合操作列表比如UNION, EXCEPT, INTERSECT。JJTWITHITEM: 代表WITH子句中的单个项。JJTSELECTITEM: 代表SELECT子句中的一个项可能是列名、表达式等。JJTJOINEREXPRESSION: 代表JOIN操作的表达式。JJTLIMITWITHOFFSET: 代表LIMIT和OFFSET子句。JJTPLAINLIMIT: 代表一个简单的LIMIT子句。JJTEXPRESSION: 代表一个表达式。JJTREGULARCONDITION: 代表一个常规条件如WHERE子句中的条件。JJTINEXPRESSION: 代表IN表达式。JJTLIKEEXPRESSION: 代表LIKE表达式。JJTSIMILARTOEXPRESSION: 代表SIMILAR TO表达式。JJTISDISTINCTEXPRESSION: 代表IS DISTINCT FROM表达式。JJTEXPRESSIONLIST: 代表一个表达式列表。JJTPRIMARYEXPRESSION: 代表一个主要表达式。JJTCONNECTBYROOTOPERATOR: 代表CONNECT BY ROOT操作符。JJTCASEWHENEXPRESSION: 代表CASE WHEN表达式。JJTFUNCTION: 代表一个函数调用。JJTSEQUENCE: 代表一个序列。JJTSYNONYM: 代表一个同义词。
Visit常见应用场景
目前我们知道通过Mybatis 的 interceptor可以拦截到所有执行的SQL语句而在 自定义的interceptor中调用自定义的visit就可以对项目中所有运行的SQL做一个拦截并处理那么具体可以做哪些骚操作呢 SQL语句重写在某些数据库系统中为了优化性能或满足特定的需求可能需要重写SQL语句。通过自定义访问者可以在ASTabstract syntax tree层面进行这些操作 元数据提取自定义访问者可以用来提取SQL语句中的元数据比如查询涉及的所有表名、列名、函数等这些信息可以用于构建数据库的概要图或进行数据治理。 数据屏蔽在需要对敏感数据进行屏蔽的应用中可以通过自定义访问者来识别并修改涉及敏感数据的查询以确保在查询结果中不会暴露敏感信息。 动态查询构建在需要动态构建SQL查询的应用中可以通过自定义访问者来解析模板SQL语句并根据实际参数动态替换模板中的占位符从而构建出完整的SQL语句。 缓存策略决策根据SQL查询的特征可以通过自定义访问者来判断查询结果是否适合缓存以及应该使用什么样的缓存策略。
总结
Jsqlparser非常容易上手使用而它也解决了解析SQL语句的问题结合Springboot 和 mybatis可以设计自定义插件就像Mybatis plus的分页插件那样可以开发自己系统需求的业务处理功能方便项目业务的时间甚至可以拿来提高效率毕竟总有一些时候对SQL的解析是绕不开的。