写点什么

jfinal 中如何使用过滤器监控 Druid 监听 SQL 执行?

  • 2022 年 6 月 29 日
  • 本文字数:3419 字

    阅读完需:约 11 分钟

jfinal中如何使用过滤器监控Druid监听SQL执行?

本文分享自华为云社区《jfinal中使用过滤器监控Druid的SQL执行【五月07】》,作者:KevinQ 。


最开始我想做的是通过拦截器拦截 SQL 执行,比如类似与 PageHelper 这种插件,通过拦截器或过滤器,手动修改 SQL 语句,以实现某些业务需求,比如执行分页,或者限制访问的数据权限等等。但是查到资料说过滤器不是干这个的,干这个的是数据库中间件干的事情,比如 MyCat 等。


但是经过测试发现,过滤器至少可以监听每一个 SQL 的执行与返回结果。因此,将这一次探索过程记录下来。

配置过滤器


在 jfinal 的启动配置类中,有一个函数 configPlugin(Plugins me)函数来配置插件,这个函数会在 jfinal 启动时调用,这个函数的参数是 Plugins me,这个参数是一个插件管理器,可以通过这个插件管理器来添加插件。


数据库插件 Druid 就是在该函数内添加的。


public void configPlugin(Plugins me) {    DruidPlugin druidPlugin = createDruidPlugin_holdoa();    druidPlugin.setPublicKey(p.get("publicKeydebug").trim());    wallFilter = new WallFilter();    wallFilter.setDbType("mysql");    druidPlugin_oa.addFilter(wallFilter);    druidPlugin_oa.addFilter(new StatFilter());    me.add(druidPlugin);}
复制代码


我们参考 WallFilter 以及 StatFilter 也创建一个过滤器类:


import com.alibaba.druid.filter.FilterEventAdapter;public class DataScopeFilter extends FilterEventAdapter {
}
复制代码


我们发现 FilterEventAdapter 中的方法大概有这几个:


public boolean statement_execute(FilterChain chain, StatementProxy statement, String sql) throws SQLException {...}protected void statementExecuteUpdateBefore(StatementProxy statement, String sql) {...}protected void statementExecuteUpdateAfter(StatementProxy statement, String sql, int updateCount) {...}protected void statementExecuteQueryBefore(StatementProxy statement, String sql) {...}protected void statementExecuteQueryAfter(StatementProxy statement, String sql, ResultSetProxy resultSet) {...}protected void statementExecuteBefore(StatementProxy statement, String sql) {...}protected void statementExecuteAfter(StatementProxy statement, String sql, boolean result) {...}
复制代码


我们复写这几个方法来看一下(排除 Update 方法,因为我们更关心查询语句)


package xxxx.xxxx;
import com.alibaba.druid.filter.FilterChain;import com.alibaba.druid.filter.FilterEventAdapter;import com.alibaba.druid.proxy.jdbc.ResultSetProxy;import com.alibaba.druid.proxy.jdbc.StatementProxy;import com.jfinal.kit.LogKit;import java.sql.SQLException;
public class DataScopeFilter extends FilterEventAdapter {
@Override public boolean statement_execute(FilterChain chain, StatementProxy statement, String sql) throws SQLException { LogKit.info("statement_execute"); return super.statement_execute(chain, statement, sql); }
@Override protected void statementExecuteQueryBefore(StatementProxy statement, String sql) { LogKit.info("statementExecuteQueryBefore"); super.statementExecuteQueryBefore(statement, sql); }
@Override protected void statementExecuteQueryAfter(StatementProxy statement, String sql, ResultSetProxy resultSet) { LogKit.info("statementExecuteQueryAfter"); super.statementExecuteQueryAfter(statement, sql, resultSet); }
@Override protected void statementExecuteBefore(StatementProxy statement, String sql) { LogKit.info("statementExecuteBefore"); super.statementExecuteBefore(statement, sql); }
@Override protected void statementExecuteAfter(StatementProxy statement, String sql, boolean result) { LogKit.info("statementExecuteAfter"); super.statementExecuteAfter(statement, sql, result); }
@Override public ResultSetProxy statement_executeQuery(FilterChain chain, StatementProxy statement, String sql) throws SQLException { LogKit.info("statement_executeQuery"); return super.statement_executeQuery(chain, statement, sql); }}
复制代码


然后再 config 配置类中添加过滤器:


druidPlugin.addFilter(new DataScopeFilter());
复制代码


发起其执行顺序为:


statement_executeQuerystatementExecuteQueryBeforestatementExecuteQueryAfter
复制代码


查看父级代码,发现其执行逻辑是,首先执行 statement_executeQuery,然后因为调用父级的方法,而父级方法体为:


@Override    public ResultSetProxy statement_executeQuery(FilterChain chain, StatementProxy statement, String sql)                                                                                                         throws SQLException {        statementExecuteQueryBefore(statement, sql);
try { ResultSetProxy resultSet = super.statement_executeQuery(chain, statement, sql);
if (resultSet != null) { statementExecuteQueryAfter(statement, sql, resultSet); resultSetOpenAfter(resultSet); }
return resultSet; } catch (SQLException error) { statement_executeErrorAfter(statement, sql, error); throw error; } catch (RuntimeException error) { statement_executeErrorAfter(statement, sql, error); throw error; } catch (Error error) { statement_executeErrorAfter(statement, sql, error); throw error; } }
复制代码


从而进一步触发 statementExecuteQueryBefore 方法与 statementExecuteQueryAfter 方法。

因此我们,修改 statement_executeQuery 方法:


 @Override    public ResultSetProxy statement_executeQuery(FilterChain chain, StatementProxy statement, String sql)            throws SQLException {
statementExecuteQueryBefore(statement, sql); ResultSetProxy result = chain.statement_executeQuery(statement, sql); statementExecuteQueryAfter(statement, sql, result); return result; }
复制代码


如此,便让输出结果为:


statementExecuteQueryBeforestatement_executeQuerystatementExecuteQueryAfter
复制代码


我们可以在 Before 或者 After 方法中添加一些逻辑,比如:记录 SQL 的实际执行人,操作时间,请求执行 SQL 的接口。

sql 被声明为 final 类型


发现执行的 SQL 在 Druid 中对应的类是:DruidPooledPreparedStatement,其类结构为:


public class DruidPooledPreparedStatement extends DruidPooledStatement implements PreparedStatement {
private final static Log LOG = LogFactory.getLog(DruidPooledPreparedStatement.class);
private final PreparedStatementHolder holder; private final PreparedStatement stmt; private final String sql;
....}
复制代码


这也就以为着,该类一旦创建,SQL 设置后就不允许再修改了,因此,我们需要修改 SQL 的话,就需要在 prepared 对象生成之前就修改到对应的执行 SQL。


在调试过程中,发现需要覆盖下面这个方法:


@Override    public PreparedStatementProxy connection_prepareStatement(FilterChain chain, ConnectionProxy connection, String sql)            throws SQLException {        // 可以达到修改SQL的目的        sql += " LIMIT 1";        PreparedStatementProxy statement = super.connection_prepareStatement(chain, connection, sql);
statementPrepareAfter(statement);
return statement; }
复制代码


我们可以在这里添加自定义的 SQL 修改逻辑,比如添加数据权限等等。


点击关注,第一时间了解华为云新鲜技术~

发布于: 刚刚阅读数: 4
用户头像

提供全面深入的云计算技术干货 2020.07.14 加入

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态,方便开发者快速成长与发展,欢迎提问、互动,多方位了解云计算! 传送门:https://bbs.huaweicloud.com/

评论

发布
暂无评论
jfinal中如何使用过滤器监控Druid监听SQL执行?_sql_华为云开发者联盟_InfoQ写作社区