Mybatis之插件开发

关于mybatis插件开发,其实通俗一点就是mybatis拦截器

主要用于在sql执行前拦截sql进行相应的处理
常用地方:
1、公共字段统一赋值,mybatis-plus中的自动填充注解实现其实就是这个原理
2、mybatis分页,也是通过这种方式植入xml分页参数的
3、性能监控,对于SQL语句执行的性能监控,可以通过拦截Executor类的update, query等方法,用日志记录每个方法执行的时间

支持拦截的方法:
1、执行器Executor(update、query、commit、rollback等方法)
2、参数处理器ParameterHandler(getParameterObject、setParameters方法)
3、结果集处理器ResultSetHandler(handleResultSets、handleOutputParameters等方法)
4、SQL语法构建器StatementHandler(prepare、parameterize、batch、update、query等方法)
序号接口方法描述
1Executorupdate、query、flushStatements、commit、rollback、getTransaction、close、isClosed拦截执行器的方法
2ParameterHandlergetParameterObject、setParameters拦截参数的处理
3ResultSetHandlerhandleResultSets、handleCursorResultSets、handleOutputParameters拦截结果集的处理
4StatementHandlerprepare、parameterize、batch、update、query拦截Sql语法构建的处理

拦截阶段

那么这些类上的方法都是在什么阶段被拦截的呢?为理解这个问题,我们先看段简单的代码(摘自mybatis源码中的单元测试SqlSessionTest类),来了解下典型的mybatis执行流程,如下代码所示:

在这里插入图片描述
以上代码主要完成以下功能:

读取mybatis的xml配置文件信息
通过SqlSessionFactoryBuilder创建SqlSessionFactory对象
通过SqlSessionFactory获取SqlSession对象
执行SqlSession对象的selectList方法,查询结果
关闭SqlSession

@Intercepts(org.apache.ibatis.plugin.Intercepts)和签名注解@Signature(org.apache.ibatis.plugin.Signature),这两个注解用来配置拦截器要拦截的接口的方法。
@Intercepts注解中的属性是一个@Signature(签名)数组,可以在同一个拦截器中同时拦截不同的接口和方法。

以拦截ResultSetHandler接口的handleResultSets方法为例,配置签名如下:

@Intercepts({
@Signature(
type= ResultSetHandler.class,
method = “handleResultSets”,
args = {Statement.class})
})

@Signature注解包含以下三个属性:

type:设置拦截的接口,可选值是前面提到的4个接口。
method:设置拦截接口中的方法名,可选值是前面4个接口对应的方法,需要和接口匹配。
args:设置拦截方法的参数类型数组,通过方法名和参数类型可以确定唯一一个方法。

由于MyBatis代码具体实现的原因,可以被拦截的4个接口中的方法并不是都可以被拦截的。下面将针对这4种接口,讲可以被拦截的方法以及方法被调用的位置和对应的拦截器签名依次列举出来。

Executor接口

Executor是Mybatis的内部执行器。它负责调用StatementHandler操作数据库,并把结果集通过 ResultSetHandler进行自动映射。另外,它还处理了二级缓存的操作。从这里可以看出,我们也是可以通过插件来实现自定义的二级缓存的

1、update
	//该方法会在所有的INSERT、UPDATE、DELETE执行时被调用,因此如果想要拦截这3类操作,可以拦截该方法。接口方法对应的签名如下:
 @Signature(
type= Executor.class,method = "update",args = {MappedStatement.class, Object.class})

2、query
	// 该方法会在所有SELECT查询方法执行时被调用。通过这个接口参数可以获取很多有用的信息,因此这是最常被拦截的一个方法。使用该方法需要注意的是,虽然接口中还有一个参数更多的同名接口,但由于MyBatis的设计原因,这个参数多的接口不能被拦截。接口方法对应的签名如下:
@Signature(
type= Executor.class,method = "query",args = {MappedStatement.class, Object.class,RowBounds.class, ResultHandler.class})

3、flushStatements
	//该方法只在通过SqlSession方法调用flushStatements方法或执行的接口方法中带有@Flush注解时才被调用,接口方法对应的签名如下:
	@Signature(
type= Executor.class,method = "flushStatements",args = {})

4、commit
	//该方法只在通过SqlSession方法调用commit方法时才被调用,接口方法对应的签名如下:
	@Signature(
type= Executor.class,method = "commit",args = {boolean.class})

5、rollback
	//该方法只在通过SqlSession方法调用rollback方法时才被调用,接口方法对应的签名如下:
	@Signature(
type= Executor.class,method = "rollback",args = {boolean.class})

6、getTransaction
	//该方法只在通过SqlSession方法获取数据库连接时才被调用,接口方法对应的签名如下:
	@Signature(
type= Executor.class,method = "getTransaction",args = {})

7、close
	//该方法只在延迟加载获取新的Executor后才会被执行,接口方法对应的签名如下:
	@Signature(
type= Executor.class,method = "close",args = {boolean.class})

8、isClosed
	//方法只在延迟加载执行查询方法前被执行,接口方法对应的签名如下:
	@Signature(
type= Executor.class,method = "isClosed",args = {})

ParameterHandler接口

ParameterHandler是Mybatis实现Sql入参设置的对象。
这里,使用插件可以改变我们Sql的参数默认设置。

1、getParameterObject
	//该方法只在执行存储过程处理出参的时候被调用,接口方法对应的签名如下:
	@Signature(
type= ParameterHandler.class,method = "getParameterObject",args = {})

2、setParameters
	//该方法在所有数据库方法设置SQL参数时被调用,接口方法对应的签名如下:
	@Signature(
type= ParameterHandler.class,method = "setParameters",args = {PreparedStatement.class})

ResultSetHandler接口
ResultSetHandler是Mybatis把ResultSet集合映射成POJO的接口对象。
我们可以定义插件对Mybatis的结果集自动映射进行修改。

1、handleResultSets
	//该方法会在除存储过程及返回值类型为Cursor<T> (org.apache.ibatis.cursor.Cursor<T>)以外的查询方法中被调用,接口方法对应的签名如下:
	@Signature(
type= ResultSetHandler.class,method = "handleResultSets",args = {Statement.class})

2、handleCursorResultSets
	//该方法是在3.4.0版本中新增加的,只会在返回值类型为Cursor<T> 的查询方法中被调用,接口方法对应的签名如下:
	@Signature(
type= ResultSetHandler.class,method = "handleCursorResultSets",args = {Statement.class})

3、handleOutputParameters
	//该方法只在使用存储过程处理出参时被调用,接口方法对应的签名如下:
	@Signature(
type= ResultSetHandler.class,method = "handleOutputParameters",args = {CallableStatement.class})

ResultSetHandler接口的第一个方法对于拦截处理MyBatis的查询结果非常有用,并且由于这个接口被调用的位置在处理二级缓存之前,因此通过这种方式处理的结果可以执行二级缓存。

StatementHandler接口

StatementHandler是Mybatis直接和数据库执行sql脚本的对象。
另外它也实现了Mybatis的一级缓存。这里,我们可以使用插件来实现对一级缓存的操作(禁用等等)。

1、prepare
	//该方法会在数据库执行前被调用,优先于当前接口中的其他方法而被执行,接口方法对应的签名如下:
	@Signature(
type= StatementHandler.class,method = "prepare",args = {Collection.class, Integer.class})

2、parameterize
	//该方法在prepare方法之后执行,用于处理参数信息,接口方法对应的签名如下:
	@Signature(
type= StatementHandler.class,method = "parameterize",args = {Statement.class})

3、batch
	//在全局设置配置defaultExecutorType="BATCH"时,执行数据操纵才会调用该方法,接口方法对应的签名如下:
	@Signature(
type= StatementHandler.class,method = "batch",args = {Statement.class})

4、query
	//执行SELECT方法时调用,接口方法对应的签名如下:
	@Signature(
type= StatementHandler.class,method = "query",args = {Statement.class, ResultHandler.class})

5、queryCursor
	//该方法是在3.4.0版本中新增加的,只会在返回值类型为Cursor<T> 的查询中被调用,接口方法对应的签名如下:
	@Signature(
type= StatementHandler.class,method = "queryCursor",args = {Statement.class})

代码案例

invocation 说明
@Override
public Object intercept(Invocation invocation) throws Throwable{
	//getTarget()方法可以获取当前被拦截的对象
	Object target=invocation.getTarget();
	
	//使用getMethod()可以获取当前被拦截的方法
	Method method=invocation.getMethod();
	
	//使用getArgs()方法可以返回被拦截方法中的参数
	Object[] args=invocation.getArgs();
	
	//通过调用invocation.proceed();可以执行被拦截对象真正的方法,proceed()方法实际上执行了method.invoke(target,args)方法上面的代码中没有做任何特殊处理,直接返回了执行的结果
	Object result=invocation.proceed();
	
	return result;
}


@Override
public Object intercept(Invocation invocation) throws Throwable {
    StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
    MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY,
            SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
    //先拦截到RoutingStatementHandler,里面有个StatementHandler类型的delegate变量,其实现类是BaseStatementHandler,
    // 然后就到BaseStatementHandler的成员变量mappedStatement
    MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
    //sql语句类型 select、delete、insert、update
    String sqlCommandType = mappedStatement.getSqlCommandType().toString();
    BoundSql boundSql = statementHandler.getBoundSql();
    //获取到原始sql语句
    StringBuffer sb = new StringBuffer(boundSql.getSql());

    if(query.equals(sqlCommandType)){
        Class<?> classType = Class.forName(mappedStatement.getId().substring(0, mappedStatement.getId().lastIndexOf(".")));
        String mName = mappedStatement.getId().substring(mappedStatement.getId().lastIndexOf(".") + 1, mappedStatement.getId().length());
        for (Method method : classType.getDeclaredMethods()) {
        	// 获取需要拦截的方法注解
            InterceptAnnotation interceptorAnnotation = method.getAnnotation(InterceptAnnotation.class);
            // 是否开启拦截
            if(interceptorAnnotation!=null && interceptorAnnotation.flag()){
                if(mName.equals(method.getName()) ){
                    sb.append(" where 1=1");
                }
            }
        }
    }
    // 通过反射修改sql语句
    Field field = boundSql.getClass().getDeclaredField("sql");
    field.setAccessible(true);
    field.set(boundSql, sb.toString());
    return invocation.proceed();
}

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

}

@Override
public void setProperties(Properties properties) {

}
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 答:MyBatis Generator插件是用于自动生成MyBatis的Mapper接口和XML文件的工具,可以根据数据库表结构自动生成对应的Java代码和SQL语句。开发插件需要熟悉Java和MyBatis框架的使用,可以通过实现MyBatis Generator提供的接口来扩展其功能。 ### 回答2: MyBatis Generator是一个用于生成MyBatis的持久层代码的插件。通过生成代码,开发人员可以快速创建和维护数据库表对应的实体类、Mapper接口和XML文件。 MyBatis Generator插件开发涉及以下几个方面的内容: 1. 插件架构:MyBatis Generator插件采用插件化的设计,开发人员可以通过实现插件接口来自定义代码生成过程中的行为。插件的核心接口是插件类和插件运行时类,其中插件类负责定义插件的配置参数和生成代码的散列计算,插件运行时类则负责实际的代码生成逻辑。 2. 生成策略:插件开发需要定义生成代码的策略。这包括决定生成哪些表的代码,生成的文件路径和命名规则,以及生成代码的格式等。可以通过实现自定义的生成策略类来完成这些操作。 3. 脚本引擎:MyBatis Generator插件支持使用各种脚本引擎(如Velocity、Freemarker等)来生成代码。插件开发需要选择并集成合适的脚本引擎,并编写脚本模板来生成代码。 4. 扩展点:MyBatis Generator插件提供了一些扩展点,以便开发人员可以在生成代码的不同阶段干涉生成过程。通过实现扩展点接口,可以在生成前后插入自定义逻辑,例如修改表名、字段名,增加自定义注释等。 5. 测试和调试:插件开发涉及到代码生成的过程,因此对于插件逻辑和生成效果的测试和调试非常重要。可以通过创建测试用例,模拟MyBatis Generator的运行环境,验证自定义插件的正确性和生成结果。 总之,MyBatis Generator插件开发需要理解插件的架构,定义生成策略,集成脚本引擎,实现扩展点,并进行测试和调试。开发人员可以根据实际需求和项目特点,自定义插件来满足业务需求。 ### 回答3: MyBatis Generator插件是用于生成MyBatis持久层代码的工具。开发一个MyBatis Generator插件可以根据项目的需求自定义生成的代码。 首先,你需要了解MyBatis Generator的工作原理和基本结构。MyBatis Generator通过读取数据库的表结构信息和配置文件,根据模板文件生成相应的Java类和XML文件。插件开发就是在这个基础上增加一些自定义的功能。 插件开发的主要步骤如下: 1. 实现Plugin接口:插件需要实现MyBatis Generator提供的Plugin接口,这个接口包含了插件需要实现的几个方法,如:初始化方法、处理生成的Java类和XML文件的方法等。 2. 注册插件:在MyBatis Generator的配置文件中,需要将插件注册到<plugins>标签中,这样才能使用插件中的功能。注册时需要指定插件的全限定名。 3. 添加自定义功能:在插件的实现类中,根据需求添加自定义的功能,例如:生成特定的注释、添加自定义的方法等。可以通过解析数据库表结构和配置文件来判断哪些表或列需要进行特殊处理。 4. 生成代码:最后,通过执行MyBatis Generator的命令,根据配置文件和注册的插件生成对应的Java类和XML文件。生成的代码会根据插件的实现进行相应的处理。 总结起来,开发MyBatis Generator插件需要了解MyBatis Generator的基本原理和工作方式,然后通过实现Plugin接口和添加自定义功能来达到定制化的代码生成目的。插件开发可以根据具体项目的需求进行扩展和定制,提高代码生成的效率和质量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值