一、Mybatis拦截器介绍
Mybatis拦截器设计的思路是为了供用户灵活的实现自己的逻辑,而不动mybatis固有的逻辑,简而言之就是如果Mybatis是一只虾,我们要做的是将虾肉掏空,放入自己喜欢吃的东西进去,而依旧保持吓得壳身。通过Mybatis拦截器我们能拦截某些方法的调用,我们可以选择在这些被拦截方法执行前后加上我们自己的逻辑,达到丰富方法的效果;也可以在执行这些方法的时候拦截住,转而实现自己设计的方法,而最后不执行被拦截的方法。Mybatis中有很多核心对象,大致如下:
SqlSession | 负责和数据库进行交互,实现完成常对数据操作的增删改查功能 |
Executor | Mybatis执行器,调度的核心,负责SQL语句的生成和维护 |
StatementHandler | 封装并操作Jdbc statement,例如设置参数,将statement结果集转化为List集合 |
ParameterHandler | 负责将用户传递的参数转换为Jdbc statement所需要的参数进行传递 |
ResultSetHandler | 负责将Jdbc返回的ResultSet结果集对象转换为List类型的集合 |
TypeHandler | 负责将java数据类型和Jdbc数据类型之间的转换和映射 |
MappedStatement | MappedStatement维护了一条mapper.xml文件里面 select 、update、delete、insert节点的封装 |
Configuration | MyBatis所有的配置信息都维持在Configuration对象之中 |
BoundSql | 表示动态生成的SQL语句以及相应的参数信息 |
Mybatis拦截器并不能拦截mybatis核心对象中所有方法,相反的,MyBatis拦截器可以拦截四个对象中方法,分别是:Executor、StatementHandler、ParameterHandler 和ResultSetHandler。
1. Executor
Executor是mybatis核心接口,数据库增删改语句是通过Executor接口的update方法进行的,查询是通过query方法进行,代码如下:
public interface Executor { /* * 执行update/insert/delete * */ int update(MappedStatement ms , Object paramter)throws SQLException; /* * 执行查询,先在缓存中查找 * Mybatis提供了一个简单的逻辑分页使用类RowBounds(物理分页当然就是我们在sql语句中指定limit和offset值),在DefaultSqlSession提供的某些查询接口中我们可以看到RowBounds是作为参数用来进行分页的 * */ <E> List<E> query(MappedStatement ms , Object paramter , RowBounds rowBounds , ResultSetHandler resultSetHandler , CacheKey cacheKey , BoundSql boundSql)throws SQLException; /* * 执行查询 * */ <E> List<E> query(MappedStatement ms , Object paramter , ResultSetHandler resultSetHandler)throws SQLException; /* * 执行查询,结果放在Cursor里面 * */ <E> Cursor<E> queryCursor(MappedStatement ms , Object paramter)throws SQLException; }
2. ParameterHandler
ParameterHandler用来设置参数规则,当StatementHandler使用prepare()方法后,接下来就是使用它来设置参数。所以如果有对参数做自定义逻辑处理的时候,可以通过拦截ParameterHandler来实现。ParameterHandler里面可以拦截的方法解释如下:
public interface ParameterHandler { /* * 设置参数规则的时候使用——PreparedStatement * */ void setParamters(PreparedStatement ps) throws SQLException; }
3. StatementHandler
StatementHandler负责处理Mybatis与JDBC之间Statement的交互。
public interface StatementHandler { /* * 从连接中获取一个Statement * */ Statement prepare(Connection conn , Integer transactionTimeout)throws SQLException; /* * 设置statement执行里需要的参数 * */ void paramterize(Statement statement)throws SQLException; /** * 批量 */ void batch(Statement statement) throws SQLException; /** * 更新:update/insert/delete语句 */ int update(Statement statement) throws SQLException; /** * 执行查询 */ <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException; <E> Cursor<E> queryCursor(Statement statement) throws SQLException; }
一般只拦截prepare方法。
在Mybatis里面RoutingStatementHandler是SimpleStatementHandler(对应Statement)、PreparedStatementHandler(对应PreparedStatement)、CallableStatementHandler(对应CallableStatement)的路由类,所有需要拦截StatementHandler里面的方法的时候,对RoutingStatementHandler做拦截处理就可以了,如下的写法可以过滤掉一些不必要的拦截类。
@Intercepts({ @Signature( type = Statement.class , method = "prepare", args = {Connection.class , Integer.class} ) }) public class TableShardInterceptor implements Interceptor { /* * mybatis运行时要执行的拦截方法 * */ @Override public Object intercept(Invocation invocation ) throws Throwable{ if(invocation.getTarget()instanceof RoutingStatementHandler){ //to do 自己的逻辑 } return invocation.proceed(); } /* * target:拦截对象 * * */ @Override public Object plugin(Object target){ // 当目标类是StatementHandler类型时,才包装目标类,否者直接返回目标本身,减少目标被代理的次数 return (target instanceof RoutingStatementHandler)? Plugin.wrap(target,this):target; } //实现插件参数传递 @Override public void setProperties(Properties properties){ } }
4. ResultSetHandler
ResultSetHandler用于对查询到的结果做处理。所以如果你有需求需要对返回结果做特殊处理的情况下可以去拦截ResultSetHandler的处理。ResultSetHandler里面常用拦截方法如下:
public interface ResultSetHandler { /* * 将statement执行后产生的结果集(可能有多个结果集)映射为结果表 * */ <E> List<E> handResultSets(Statement stmt)throws SQLException; <E> List<E> handleCursorResultSet(Statement stmt)throws SQLException; /* * 处理存储过程执行后的输出参数 * */ void handleOutputParameters(CallableStatement cs) throws SQLException; }
至此,拦截器介绍由此结束。
部分内容参考自:https://blog.csdn.net/wuyuxing24/article/details/89343951