Mybatis插件开发

释放双眼,带上耳机,听听看~!

前言

MyBatis开放用户实现自己的插件,从而对整个调用过程进行个性化扩展。

这是MyBatis整个调用流程的主要参与者。

我们可以对其中的一些过程进行拦截,添加自己的功能,比如重写Sql添加分页参数。

Mybatis插件开发

 

拦截的接口

MyBatis允许拦截的接口如下

Executor

public interface Executor {
    ResultHandler NO_RESULT_HANDLER = null;
    int update(MappedStatement var1, Object var2) throws SQLException;
    <E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6) throws SQLException;
    <E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;
    <E> Cursor<E> queryCursor(MappedStatement var1, Object var2, RowBounds var3) throws SQLException;
    List<BatchResult> flushStatements() throws SQLException;
    void commit(boolean var1) throws SQLException;
    void rollback(boolean var1) throws SQLException;
    CacheKey createCacheKey(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4);
    boolean isCached(MappedStatement var1, CacheKey var2);
    void clearLocalCache();
    void deferLoad(MappedStatement var1, MetaObject var2, String var3, CacheKey var4, Class<?> var5);
    Transaction getTransaction();
    void close(boolean var1);
    boolean isClosed();
    void setExecutorWrapper(Executor var1);
}

 

ParameterHandler

public interface ParameterHandler {
    Object getParameterObject();

    void setParameters(PreparedStatement var1) throws SQLException;
}

 

ResultSetHandler

public interface ResultSetHandler {
    <E> List<E> handleResultSets(Statement var1) throws SQLException;

    <E> Cursor<E> handleCursorResultSets(Statement var1) throws SQLException;

    void handleOutputParameters(CallableStatement var1) throws SQLException;
}

 

StatementHandler

public interface StatementHandler {
    Statement prepare(Connection var1, Integer var2) throws SQLException;
    void parameterize(Statement var1) throws SQLException;
    void batch(Statement var1) throws SQLException;
    int update(Statement var1) throws SQLException;
    <E> List<E> query(Statement var1, ResultHandler var2) throws SQLException;
    <E> Cursor<E> queryCursor(Statement var1) throws SQLException;
    BoundSql getBoundSql();
    ParameterHandler getParameterHandler();
}

 

只要拦截器定义了拦截的接口和方法,后续调用该方法时,将会被拦截。

拦截器实现

如果要实现自己的拦截器,需要实现接口Interceptor

@Component
@Slf4j
@Intercepts(@Signature(type = Executor.class,
        method =\"update\",
        args ={MappedStatement.class,Object.class} ))
public class MyIntercetor implements Interceptor {


    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        log.info(\"MyIntercetor ...\");

        Object result = invocation.proceed();

        log.info(\"result = \" + result);

        return result;
    }

    @Override
    public Object plugin(Object o) {
        return Plugin.wrap(o,this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}

 

 

1. 拦截方法配置

@Intercepts(@Signature(type = Executor.class,
        method =\"update\",
        args ={MappedStatement.class,Object.class} ))

 

我们知道Java中方法的签名包括所在的类,方法名称,入参。 

@Signature定义方法签名

type:拦截的接口,为上节定义的四个接口

method:拦截的接口方法

args:参数类型列表,需要和方法中定义的顺序一致。

 

2. intercept(Invocation invocation)

public class Invocation {
private final Object target;
private final Method method;
private final Object[] args;

public Invocation(Object target, Method method, Object[] args) {
this.target = target;
this.method = method;
this.args = args;
}

public Object getTarget() {
return this.target;
}

public Method getMethod() {
return this.method;
}

public Object[] getArgs() {
return this.args;
}

public Object proceed() throws InvocationTargetException, IllegalAccessException {
return this.method.invoke(this.target, this.args);
}
}

 

通过Invocation可以获取到被拦截的方法的调用对象,方法,参数。

proceed()用于继续执行并获得最终的结果。

这里使用了设计模式中的责任链模式。

 

3.这里不能返回null。

@Override
    public Object plugin(Object o) {
        return Plugin.wrap(o,this);
    }

 

 

4.拦截器在执行前输出\"MyIntercetor ...\",在数据库操作返回后输出\"result =xxx\"

       log.info(\"MyIntercetor ...\");

        Object result = invocation.proceed();

        log.info(\"result = \" + result);

 

插件实现完成!

 

测试

在Spring中引入很简单。

第一种方式:

创建拦截器的bean

@Slf4j
@Configuration
public class IntercetorConfiguration {

    @Bean
    public MyIntercetor myIntercetor(){
        return new MyIntercetor();
    }

}

 

第二种方式

手动往Configuration中添加拦截器。

@Slf4j
@Configuration
public class IntercetorConfiguration {
  @Autowired
    private List<SqlSessionFactory> sqlSessionFactoryList;

    @PostConstruct
    public void addPageInterceptor() {
        MyIntercetor interceptor = new MyIntercetor();

        Iterator var3 = this.sqlSessionFactoryList.iterator();

        while(var3.hasNext()) {
            SqlSessionFactory sqlSessionFactory = (SqlSessionFactory)var3.next();
            sqlSessionFactory.getConfiguration().addInterceptor(interceptor);
        }

    }
}

 

由于上面定义的拦截器是拦截Executor的update方法,所以在执行insert,update,delete的操作时,将会被拦截。

本例子使用insert来测试。具体代码查看:GitHub

2019-06-10 16:08:03.109  INFO 20410 --- [nio-8110-exec-1] c.m.user.dao.intercetor.MyIntercetor     : MyIntercetor ...

2019-06-10 16:08:03.166  INFO 20410 --- [nio-8110-exec-1] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
2019-06-10 16:08:03.267 DEBUG 20410 --- [nio-8110-exec-1] o.m.s.t.SpringManagedTransaction         : JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5cb1c36e] will not be managed by Spring
2019-06-10 16:08:03.274 DEBUG 20410 --- [nio-8110-exec-1] c.m.u.dao.mapper.UserMapper.insertList   : ==>  Preparing: insert into user (name) values (?) , (?) , (?) 
2019-06-10 16:08:03.307 DEBUG 20410 --- [nio-8110-exec-1] c.m.u.dao.mapper.UserMapper.insertList   : ==> Parameters: name:58(String), name:64(String), name:69(String)
2019-06-10 16:08:03.355 DEBUG 20410 --- [nio-8110-exec-1] c.m.u.dao.mapper.UserMapper.insertList   : <==    Updates: 3
2019-06-10 16:08:03.358 DEBUG 20410 --- [nio-8110-exec-1] c.m.u.d.m.U.insertList!selectKey         : ==>  Preparing: SELECT LAST_INSERT_ID() 
2019-06-10 16:08:03.358 DEBUG 20410 --- [nio-8110-exec-1] c.m.u.d.m.U.insertList!selectKey         : ==> Parameters: 
2019-06-10 16:08:03.380 DEBUG 20410 --- [nio-8110-exec-1] c.m.u.d.m.U.insertList!selectKey         : <==      Total: 1

2019-06-10 16:08:03.381 INFO 20410 --- [nio-8110-exec-1] c.m.user.dao.intercetor.MyIntercetor : result = 3

 

可以看到拦截器被调用了。

 

如果需要对查询进行拦截,可以拦截以下方法,比如通过MappedStatement修改sql语句。

<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6) throws SQLException;

<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;

 

 

结束!!!!!

给TA打赏
共{{data.count}}人
人已打赏
随笔日记

Docker 入门及安装[Docker 系列-1]

2020-11-9 5:19:26

随笔日记

开盘跌停!2000亿独角兽又破发了:去年疯抢如今全被套 腾讯阿里也不例外!

2020-11-9 5:19:28

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索