java实现sql解析器 JSQLParser

java实现sql解析器 JSQLParser

前言

JSQLParser是基于JavaCC构建的sql语句解析器。它以可遍历的Java类层次结构来转换SQL。而且它不仅限于一个数据库,而且还支持 Oracle, SqlServer, MySQL, PostgreSQL等许多数据库。另外 自 5.0 版本起,JSQLParser 依赖于 Java 11。所以我们需要根据项目依赖环境选取对应的版本。

使用场景

在面对一些内网环境的项目,我们无法访问数据库的情况下,需要快速查一些客户需要的信息时,我们可以在项目中集成JSQLParser 只需要调用接口,就可以可前端配合将数据展示在页面上了。

各种RDBMS 不支持的语法

JSQLParser是一款与关系型数据库(RDBMS) 无关的解析器,专注于符合SQL:2016标准的查询以及"四大数据库"(Oracle,MS SQL Server, Postgres, Mysql/MariaDB)。需要编写可移植且符合标准的SQL。

1.不支持Oracel PL/SQL 块

复制代码
DECLARE
    num NUMBER;
BEGIN
    num := 10;
    dbms_output.put_line('The number is ' || num);
END;
  1. Oracle INSERT ALL...不受支持

    INSERT ALL
    INTO mytable (column1, column2, column_n) VALUES (expr1, expr2, expr_n)
    INTO mytable (column1, column2, column_n) VALUES (expr1, expr2, expr_n)
    INTO mytable (column1, column2, column_n) VALUES (expr1, expr2, expr_n)
    SELECT * FROM dual;

  2. DDL 语句

    虽然JSQLParser为 DDL 语句提供了大量通用支持,但某些 RDBMS 特定语法(尤其是关于索引、编码、压缩)可能不受支持。

  3. 区间运算符

    不支持任何类似DAY HOUR MINUTE SECOND [TO HOUR MINUTE SECOND]的内容。

    values cast ((time '12:03:34' - time '11:57:23') minute to second as varchar(8));

快速上手

1.添加依赖(如果我们项目中引入了 mybatis-plus 就不需要添加该依赖了)

复制代码
<dependency>
     <groupId>com.github.jsqlparser</groupId>
     <artifactId>jsqlparser</artifactId>
     <version>5.1</version>
</dependency>

2.代码实现

复制代码
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        CCJSqlParserManager ccjSqlParserManager = new CCJSqlParserManager();
        Statement parse =null;
        try{
            parse = ccjSqlParserManager.parse(new StringReader(dto.getSql()));
        } catch (Exception e) {
            log.error("SQL执行错误:{}", e.getMessage());
//            return e.getMessage();
        }
        try {
            ExecutionSqlVO vo = new ExecutionSqlVO();
            if (parse instanceof Select) {
                List<Map<String, Object>> maps = SqlRunner.db().selectList(dto.getSql());
                Map<String, Object> sqlData = new HashMap<>();
                sqlData.put("head", CollUtil.isEmpty(maps)? Lists.newArrayList():maps.get(0).keySet());
                sqlData.put("data", maps);
                vo.setResult(sqlData);
                vo.setType(Select.class.getSimpleName());
            } else if (parse instanceof Update) {
                Update updateSql = (Update) parse;
                Expression where = updateSql.getWhere();
                if (where == null) {
                    throw new ServiceException("禁止执行没有条件的SQL");
                }
                boolean update = SqlRunner.db().update(dto.getSql());
                if(!update){
                    vo.setResult("执行失败");
                }else {
                    vo.setResult("执行成功");
                }
                vo.setType(Update.class.getSimpleName());
                return vo;
            } else if (parse instanceof Insert) {
                boolean insert = SqlRunner.db().insert(dto.getSql());
                if(!insert){
                    vo.setResult("执行失败");
                }else {
                    vo.setResult("执行成功");
                }
                vo.setType(Insert.class.getSimpleName());
            } else if (parse instanceof Delete) {
                Delete deleteSql = (Delete) parse;
                Expression where = deleteSql.getWhere();
                if (where == null) {
                    throw new ServiceException("禁止执行没有条件的SQL");
                }
                boolean delete = SqlRunner.db().delete(dto.getSql());
                if(!delete){
                    vo.setResult("执行失败");
                }else {
                    vo.setResult("执行成功");
                }
                vo.setType(Delete.class.getSimpleName());
            } else if (parse instanceof CreateTable) {
                jdbcTemplate.execute(dto.getSql());
                vo.setResult(String.format("执行成功"));
                vo.setType(Delete.class.getSimpleName());
            } else {
                throw new ServiceException("不支持的SQL类型");
            }
            return vo;
        } catch (Exception se) {
//            return se.getMessage();
        }

        return  null;
    }

方法测试