Druid SQL解析原理以及使用(二)

1、介绍

本篇主要介绍Druid采用访问者模式解析SQL,访问者模式,是行为型设计模式之一。访问者模式是一种将数据操作与数据结构分离的设计模式,它可以算是 23 中设计模式中最复杂的一个,但它的使用频率并不是很高,这里关于访问者模式不做过多解释,有兴趣的童靴可以去网上查阅。


2、visitor模式

访问者模式中,接口中定义对象元素方法,每一个元素对应一个方法,供访问者访问,Druid visitor顶层结构,选取部分接口中方法

import com.alibaba.druid.sql.ast.SQLArgument;
import com.alibaba.druid.sql.ast.SQLArrayDataType;
import com.alibaba.druid.sql.ast.SQLCommentHint;
import com.alibaba.druid.sql.ast.SQLDataType;
import com.alibaba.druid.sql.ast.SQLDeclareItem;
import com.alibaba.druid.sql.ast.SQLKeep;
import com.alibaba.druid.sql.ast.SQLLimit;
public interface SQLASTVisitor {

    void endVisit(SQLAllColumnExpr x);

    void endVisit(SQLBetweenExpr x);

    void endVisit(SQLBinaryOpExpr x);
  
    boolean visit(SQLDeleteStatement x);
  
    boolean visit(SQLInsertStatement x);
  
    boolean visit(SQLBetweenExpr x);
   
    .....
      
}
 

SQLASTVisitor接口中对SQL ast语法树中所有元素提供了方法,从SQLASTVisitor接口引入的类可以看到ast语法树

select * from t where id between 'x' and 'y'

使用visitor模式访问,则会自动访问boolean visit(SQLBetweenExpr x);

druid对visitor提供了默认实现SQLASTVisitorAdapter,这样我们实现自定义visitor时,不用实现所有方法,只需要继承adapter,重写方法就可以

public class SQLASTVisitorAdapter implements SQLASTVisitor {
    protected int features;

    public void endVisit(SQLAllColumnExpr x) {
    }

    public void endVisit(SQLBetweenExpr x) {
    }

    public void endVisit(SQLBinaryOpExpr x) {
    }

    public void endVisit(SQLCaseExpr x) {
    }

    public void endVisit(SQLCaseExpr.Item x) {
    }
}

Druid已经对不同的数据库实现了默认的visitor,对于符合SQL92标准的SQL,基本不用重写visitor,利用已经实现的就可以获取条件等解析结果,如MySql,仅展示部分代码,具体可以看Druid源码

public class MySqlSchemaStatVisitor extends SchemaStatVisitor implements MySqlASTVisitor {

    public MySqlSchemaStatVisitor() {
        super (JdbcConstants.MYSQL);
    }

    public boolean visit(SQLSelectStatement x) {
        if (repository != null
                && x.getParent() == null) {
            repository.resolve(x);
        }

        return true;
    }
}

从visitor中可以获取解析结果


3、自定义实现visitor

当默认实现的visitor不满足我们需求时,可以自定义实现visitor,三种方式实现自定义visitor

  • 实现原生接口
  • extends visitor适配SQLASTVisitorAdapter,重写方法
  • extends 实现类,重写方法

下面主要将实现通过extends visitor适配SQLASTVisitorAdapter实现visitor

举个简单栗子,比如我们现在的需求是检查SQL中是否含有group by 关键字,如果按照语法树AST解析的话,处理起来很费周折,这块可以用自定义visitor实现

/**
 * @author Qi.qingshan
 * @date 2020/4/25
 */
public class SQLGroupbyVisitor extends SQLASTVisitorAdapter {

    private boolean hasGroupby = false;
  
    @Override
    public boolean visit(SQLSelectGroupByClause x) {
        this.hasGroupby = true;
        return false;
    }

    public boolean isHasGroupby() {
        return hasGroupby;
    }
}

看起来很简单吧,如果要是自己通过遍历语法树AST,处理起来很费周折,写个Test测试下

    @Test
    public void testParse() {
        SQLGroupbyVisitor visitor = new SQLGroupbyVisitor();
        String sql = "select sum(id),name from t_order group by name";
        SQLStatement sqlStatement = parse(sql);
        sqlStatement.accept(visitor);
        Assert.assertEquals(true, visitor.isHasGroupby());
    }


    private static SQLStatement parse(final String sql) throws SQLParseException {
        try {
            SQLStatementParser parser = new SQLStatementParser(sql, "mysql");
            return parser.parseStatement(true);
        } catch (Throwable e) {
            throw new RuntimeException("SQL parse exception ", e);
        }
    }

上次有个CSDN上读者留言说需要解析SQL中字段的别名,通过解析AST语法树的方式处理起来很麻烦,有没有什么好的办法,这里可以通过visitor的方式去实现,重写对应的方法即可,将解析结果存储在visitor中,对外提供访问方法即可,关于自定义实现visitor就介绍这么多,觉得有用,点个赞再走_,有问题可以留言。

你可能感兴趣的:(sql)