首先搞清楚那些地方需要打印日志?通过对日志的观察,如下几个位置需要打日志:
- 在创建
prepareStatement时,打印执行的 SQL 语句; - 访问数据库时,打印参数的类型和值
- 查询出结构后,打印结果数据条数
因 此 在 日 志 模 块 中 有 BaseJdbcLogger 、 ConnectionLogger 、 PreparedStatementLogger 和 ResultSetLogge通过动态代理负责在不同的位置打印日志;几个相关类的类图如下:
Mybatis 中如何优雅的增强日志功能?
BaseJdbcLogger:所有日志增强的抽象基类,用于记录 JDBC 那些方法需要增强,保存运行期间 sql 参数信息;
ConnectionLogger:负责打印连接信息和 SQL 语句。通过动态代理,对 connection 进行增强,如果是调用 prepareStatement、prepareCall、createStatement 的方法,打印要执行的 sql 语句并返回 prepareStatement 的代理对象(PreparedStatementLogger),让prepareStatement 也具备日志能力,打印参数;
PreparedStatementLogger:对 prepareStatement 对象增强,增强的点如下:
- 增强
PreparedStatement的 setxxx 方法将参数设置到columnMap、columnNames、columnValues,为打印参数做好准备; - 增强
PreparedStatement的 execute 相关方法,当方法执行时,通过动态代理打印参数,返回动态代理能力的 resultSet; - 如果是查询,增强
PreparedStatement的getResultSet方法,返回动态代理能力的resultSet;如果是更新,直接打印影响的行数
ResultSetLogge:负责打印数据结果信息;
最后一个问题:上面讲这么多,都是日志功能的实现,那日志功能是怎么加入主体功能的?
答:既然在 Mybatis 中 Executor 才是访问数据库的组件,日志功能是在 Executor 中被嵌入的,具体代码在
org.apache.ibatis.executor.SimpleExecutor.prepareStatement(StatementHandler, Log)方法中,如下图所示: