jfinal中如何使用过滤器监控Druid监听SQL执行?
摘要:最开始我想做的是通过拦截器拦截SQL执行,但是经过测试发现,过滤器至少可以监听每一个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_executeQuery
statementExecuteQueryBefore
statementExecuteQueryAfter
查看父级代码,发现其执行逻辑是,首先执行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;
}
如此,便让输出结果为:
statementExecuteQueryBefore
statement_executeQuery
statementExecuteQueryAfter
我们可以在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修改逻辑,比如添加数据权限等等。
相关文章
- 性能监控-TP理解
- 【华为云技术分享】华为云文档数据库服务DDS监控告警全新优化
- shell脚本实现Hbase服务的监控报警和自动拉起
- 轻松保障万级实例,vivo服务端监控体系建设实践
- VMware VCSA 6.7监控和告警
- Centos 7.6搭建LAMP,部署zabbix监控环境
- vue.js 3.2.22:用useIntersectionObserver监控多行数据的可见性(@vueuse/core@7.0.3)
- centos8平台使用nethogs基于进程监控网络流量
- zabbix 通过自定义key完成网卡监控
- 使用阿里云容器监控服务与第三方监控框架集成搭建自己的容器看板
- Qt编写安防视频监控系统38-onvif校时
- Atitit 线程池任务数量监控 Atitit 找出网络info图片图像算法 D:0workspaceatiplat_imgsrccomattilaximgpicClassifie
- node-exporter安装和详解-适用于物理机虚拟机的监控采集
- 010-JDK可视化监控工具-VisualVM
- y18.第一章 Ceph企业级存储实战进阶 -- ceph dashboard 及监控(十八)
- ML之ME/LF:机器学习之风控业务中常用模型监控指标CSI(特征稳定性指标)的简介、使用方法、案例应用之详细攻略
- 解密 Oracle 数据库 SQL 执行历史:掌握多种实现方式,轻松实现 SQL 监控与性能优化