Druid连接池工具公式化SQL附踩坑记录

时间:2024-01-23 19:14:32

1. 需求

使用Druid连接池工具格式化sql用于回显时候美观展示

2. 代码示例

2.1 依赖

<dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.2.6</version>
</dependency>

2.2 ParseUtils

import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.postgresql.visitor.PGOutputVisitor;
import com.alibaba.druid.sql.parser.SQLParserUtils;
import com.alibaba.druid.sql.parser.SQLStatementParser;
import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor;

import java.util.List;

/**
 * @author pp_lan
 * @date 2024/1/15
 */
public class ParseUtils {

    private ParseUtils() {
    }

    public static String format(String sql, String dbTypeStr) {
        DbType dbType = DbType.of(dbTypeStr);
        if (dbType == null) {
            throw new RuntimeException("不支持的数据库类型");
        }
        List<SQLStatement> statementList = toStatementList(sql, dbType);
        String result = sqlToString(statementList, dbTypeStr, null);
        return result;
    }

    public static List<SQLStatement> toStatementList(String sql, DbType dbType) {
        SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, dbType);
        return parser.parseStatementList();
    }

    public static String sqlToString(List<SQLStatement> statementList, String dbType, List<Object> parameters) {
        StringBuilder sb = new StringBuilder();
        SQLASTOutputVisitor visitor;
        switch (dbType) {
            case "postgresql":
                visitor = new PGOutputVisitor(sb);
                break;
            case "mysql":
                visitor = new MySqlOutputVisitor(sb);
                break;
            default:
                visitor = new SQLASTOutputVisitor(sb);
        }

        visitor.setParameters(parameters);

        for (SQLStatement statement : statementList) {
            statement.accept(visitor);
        }

        return sb.toString();
    }

    public static void main(String[] args) {
        // 使用::将数值转为varchar
        try {
            System.out.println("[使用::转换]");
            String sql = "select sum(in_use) :: varchar from t_user";
            System.out.println(format(sql, DbType.postgresql.name()));
        } catch (Exception e) {
            System.out.println("使用::转换异常");
        }
        // 使用cast将数值转为varchar
        try {
            System.out.println("[使用cast转换]");
            String newSql = "select cast(sum(in_use) as varchar) from t_user";
            System.out.println(format(newSql, DbType.postgresql.name()));
        } catch (Exception e) {
            System.out.println("使用cast转换异常");
        }
    }

}

3. 运行结果

[使用::转换]
SELECT sum(in_use)::varchar
FROM t_user
[使用cast转换]
SELECT CAST(sum(in_use) AS varchar)
FROM t_user

4. 踩坑记录

4.1 描述

之前我使用的是druid为1.2.4版本,在解析pg库sql时候,发现执行结果如下:

[使用::转换]
使用::转换异常
[使用cast转换]
SELECT CAST(sum(in_use) AS varchar)
FROM t_user

可以发现,转换函数::在解析时候异常了

4.2 解决方法

切换高版本1.2.6及以上,可以正常解析。

4.3 总结

druid的1.2.6以下版本对于包含数值使用::转换为varchar的场景不支持,会导致解析报错。可以使用以下方法解决:

  • 升级druid版本到1.2.6及以上
  • 在格式化的方法上加上try-catch,格式化异常的时候使用原来的sql用于回显