druid解析sql:从sql语句中获取表名、字段名

从sql语句中解析出设计的表名、字段名再配合mybatis拦截器可以帮助我们做很多的事情。比如:

  • 拦截恶意sql,全表select、update、delete
  • 拦截垃圾sql,如关联了多张表、sql没有使用分区键(如果有分区表)
  • 动态表名替换,方便做自动分表
  • 多租户的数据隔离,禁止访问和操作他人数据(还需要配合用户鉴权)

市面上有一些sql解析器比如:ANTLRJSqlParserdruid。不过这次我们主要将如何使用duird来解析sql,毕竟从官网上的介绍来说druid的解析性能非常高,完全可以使用在生产环境之中。

实操

SELECT语句

我们需要使用到druid提供的工具类SQLUtils.parseStatements将sql解析出来。如果你传入了多个sql(用“;”分割),那么返回值中List会存在多个SQLStatement,每个都对应其中的一个sql

1
2
3
4
5
// 可以替换成druid支持的其他数据库
final String dbType = JdbcConstants.MYSQL;
//下面都将使用该sql作为例子不再重复。
String sql = "select id,username,pwd from t_user where id =1 And username='Bob';";
List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
1.获取select表名

使用getTables方法将会获得一个Mapkey为表名,value则是sql的操作类型(select、update、delete等)。假设这个地方是一个多表查询,那么得到的Map将会有多个键值对。

1
2
3
MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor();
statement.accept(visitor);
Map<TableStat.Name, TableStat> getTables =visitor.getTables(); //{"t_user":"select"}
2.获取条件字段

同样的我们再调用visitor的相关方法,我们会得到一个条件的集合。在这个集合里相关的字段和值都能获取到。

1
List<TableStat.Condition>  conditionList = visitor.getConditions();

visitor.getConditions()

3.获取查询字段

visitor.getColumns如预期所料返回了select字段的集合。

1
Collection<TableStat.Column> columnList = visitor.getColumns();

visitor.getColumns()

UPDATE语句

获取更新的值
1
2
3
4
String updateSql = "update t_user set pwd='123456' where id =2 And username='George';";
List<SQLStatement> stmtList = SQLUtils.parseStatements(updateSql, dbType);
MySqlUpdateStatement updateStatement = (MySqlUpdateStatement) stmtList.get(0);
List<SQLUpdateSetItem> updateList = updateStatement.getItems();

updateStatement.getItems()

INSERT语句

获取插入的值
1
2
3
4
String insertSql = "insert into t_user (id,username,pwd) values(1,'bob','123456');";
List<SQLStatement> stmtList = SQLUtils.parseStatements(insertSql, dbType);
MySqlInsertStatement insertStatement = (MySqlInsertStatement) stmtList.get(0);
List<SQLInsertStatement.ValuesClause> insertList = insertStatement.getValuesList();

insertStatement.getValues()

实现原理

知其然还要知其所以然,光会用当然是不够的,我们还必须知道他的大概原理。druid解析sql语句大概会分为这3个步骤:

  1. 根据数据库类型将sql语句解析成ATS(抽象语法树)
  2. 根据你想要做的事情来使用相应的visitor
那么问题来了,什么是ATS(抽象语法树)?

ATS并不是解析sql才会用到,在很多编程语言中执行都会使用ATS来解释源代码,然后才能分析源代码是什么意思。比如下面这个js代码只是一个简单的赋值操作。

1
foo = 'hello world'

那么解析成语法树大概是这个样子:

语法树

解析sql和这个原理相同。

visitor设计模式

上面进行代码实操的时候,我们经常用到visitor,这个就是一种叫访问者设计模式。这个设计模式适用于数据结构不变但是计算方法会变的场景。那么结合上面的知识我们可以知道分析出来的抽象语法树这个结构是不变的,变的是不同的数据库(mysql、oracle等)和操作类型(select、update等),此时用访问者设计模式是再适合不过了!

原文链接:https://www.jdkdownload.com/druid_parse_sql.html