当前位置:网站首页>jfinal中如何使用过滤器监控Druid监听SQL执行?
jfinal中如何使用过滤器监控Druid监听SQL执行?
2022-06-29 21:35:00 【华为云开发者联盟】
摘要:最开始我想做的是通过拦截器拦截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_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修改逻辑,比如添加数据权限等等。
边栏推荐
- Houdini graphic notes: VAT (3.0) import ue4/5 setup wizard [official document translation]
- cout 不明确问题
- 小型图书馆项目总结
- Viewing technological changes through Huawei Corps (V): smart Park
- PostgreSQL weekly news - June 22
- Shutter bottomnavigationbar with page switching example
- 什么是 SYN 洪水攻击?如何防护?
- Go standard library context package: data, cancellation signal, deadline and other related operations between a single request and multiple goroutines and the request domain
- Varnish 503 no backend connection – varnish health check
- How to integrate MES system with ERP? This article tells you the answer
猜你喜欢

PostgreSQL weekly news - June 22

ASP动态创建表格 Table

Verilog realizes serial communication and sends it to the nixie tube

yolov6训练自己的数据记录+yolov5对比测试

Water polo chart - using dynamic ripples to show percentages

Win10添加ssh公钥

CLI tool foundation of ros2 robot f1tenth

How can colleges and universities build future oriented smart campus based on cloud native? Full stack cloud native vs traditional technology architecture

高校如何基于云原生构建面向未来的智慧校园?全栈云原生VS传统技术架构

【ROS进阶篇】第三讲 ROS文件系统与分布式通信
随机推荐
STM32最小系统搭建(原理图)
Weibo comments on high availability and high performance computing architecture
Vipshop product details API interface (item_get- get vipshop product details interface), vipshop details API interface
Yolov6 training your own data record +yolov5 comparison test
Report delivery engineer
Desai wisdom number - other charts (basic sunrise chart): high frequency words in graduation speech
[force deduction 10 days SQL introduction] day7+8 calculation function
Final training simple address book c language
[cloud native] use of Nacos taskmanager task management
Live broadcast preview | PostgreSQL kernel Interpretation Series Lecture 1: overview of PostgreSQL system
Varnish 503 no backend connection – varnish health check
PostgreSQL每周新聞—6月22日
As for the domestic Kirin system running QT, it can be run on the command line but cannot be run by double clicking (no response)
About Effect Size
Matlab adds noise / disturbance to data
STM32 minimum system construction (schematic diagram)
JD alliance API - universal chain transfer interface - Jingpin library interface - Interface Customization
彩涂钢板密封板申请BS 476-3如何备样?
Topic39——78. subset
不同系统下的文件层级符号小结