MyBatis插件开发

来源:互联网 发布:网络进度计划编制软件 编辑:程序博客网 时间:2024/06/18 18:03

MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
Executor (update, query, flushStatements, commit, rollback,getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
常用的插件有分页插件com.github.pagehelper.PageInterceptor。下面自定义个打印sql的插件。
通过对 MyBatis org.apache.ibatis.executor.statement.StatementHandler 中的prepare 方法进行拦截即可。
prepare 方法签名如下:

Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;

自定义类实现org.apache.ibatis.plugin.Interceptor接口,Interceptor接口代码如下:

public interface Interceptor {  Object intercept(Invocation invocation) throws Throwable;  Object plugin(Object target);  void setProperties(Properties properties);}

每一个拦截器都必须实现上面的三个方法,其中:
1) Object intercept(Invocation invocation)是实现拦截逻辑的地方,内部要通过invocation.proceed()调用下一个拦截器拦截目标方法。
2) Object plugin(Object target) 就是用当前这个拦截器生成对目标target的代理,实际是通过Plugin.wrap(target,this) 来完成的,把目标target和拦截器this传给了包装函数。
3) setProperties(Properties properties)用于设置额外的参数,参数配置在拦截器的Properties节点里。

注解里描述的是指定拦截方法的签名 [type,method,args] (即对哪种对象的哪种方法进行拦截),它在拦截前用于决断。
示例拦截器代码如下:

package com.cuisea.mybatis;import org.apache.ibatis.executor.statement.StatementHandler;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.plugin.*;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.sql.Connection;import java.util.Properties;/** * Mybatis支持对Executor、StatementHandler、PameterHandler和ResultSetHandler 接口进行拦截,也就是说会对这4种对象进行代理 * MyBatis插件 打印sql  * Created by cuisea on 2017/7/18. */@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class}) })public class SQLStatsInterceptor implements Interceptor{    private final Logger logger = LoggerFactory.getLogger(this.getClass());    @Override    public Object intercept(Invocation invocation) throws Throwable {        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();        BoundSql boundSql = statementHandler.getBoundSql();        String sql = boundSql.getSql();        logger.info("mybatis intercept sql:{}", sql);        return invocation.proceed();    }    @Override    public Object plugin(Object o) {        logger.info("mybatis intercept plugin "+o.getClass().getName());        return Plugin.wrap(o, this);    }    @Override    public void setProperties(Properties properties) {        String dialect = properties.getProperty("dialect");        logger.info("mybatis intercept dialect:{}", dialect);    }}

下面就要配置插件,有两种配置方式:
第一种:在MyBatis的配置文件mybatis-conf.xml的plugins节点下配置

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration>    <plugins>        <plugin interceptor="com.cuisea.mybatis.SQLStatsInterceptor">            <property name="dialect" value="mysql"></property>        </plugin>    </plugins></configuration>

第二种:在spring配置文件spring-mybatis.xml中配置

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xmlns:context="http://www.springframework.org/schema/context"       xmlns:tx="http://www.springframework.org/schema/tx"       xmlns:aop="http://www.springframework.org/schema/aop"       xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd       http://www.springframework.org/schema/context       http://www.springframework.org/schema/context/spring-context-4.0.xsd       http://www.springframework.org/schema/tx       http://www.springframework.org/schema/tx/spring-tx-4.0.xsd       http://www.springframework.org/schema/aop       http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">    <!-- 扫描service包下所有使用注解的类型 -->    <context:component-scan base-package="com.cuisea.service"/>    <!-- 配置数据库相关参数properties的属性:${url} -->    <context:property-placeholder location="classpath:jdbc.properties" ignore-resource-not-found="true"/>    <!-- 数据库连接池c3p0-->    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">        <property name="driverClass" value="${jdbc.driver}"/>        <property name="jdbcUrl" value="${jdbc.url}"/>        <property name="user" value="${jdbc.username}"/>        <property name="password" value="${jdbc.password}"/>        <property name="maxPoolSize" value="${c3p0.maxPoolSize}"/>        <property name="minPoolSize" value="${c3p0.minPoolSize}"/>        <property name="autoCommitOnClose" value="${c3p0.autoCommitOnClose}"/>        <property name="checkoutTimeout" value="${c3p0.checkoutTimeout}"/>        <property name="acquireRetryAttempts" value="${c3p0.acquireRetryAttempts}"/>    </bean>    <!-- 配置SqlSessionFactory对象 -->    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">        <!-- 注入数据库连接池 -->        <property name="dataSource" ref="dataSource"/>        <property name="configLocation" value="classpath:mybatis-config.xml" />        <!-- 扫描model包 使用别名,这样在mapper文件中可以省略包名;否则parameterType,resultType必须使用类的全限定名 -->        <property name="typeAliasesPackage" value="com.cuisea.model"/>        <!-- 扫描sql配置文件:mapper需要的xml文件 -->        <property name="mapperLocations" value="classpath:mapper/*.xml"/>        <property name="plugins">            <array>                <bean class="com.cuisea.mybatis.SQLStatsInterceptor">                    <property name="properties">                        <!-- config params as the following -->                        <value>                            dialect=mysql                        </value>                    </property>                </bean>            </array>        </property>    </bean>    <!-- 配置扫描Dao接口包,动态实现Dao接口,注入到spring容器中 -->    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">        <!-- 注入sqlSessionFactory -->        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>        <!-- 给出需要扫描Dao接口包 -->        <property name="basePackage" value="com.cuisea.dao"/>    </bean>    <!-- 配置事务管理器 -->    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <!-- 注入数据库连接池 -->        <property name="dataSource" ref="dataSource"/>    </bean>    <!-- 配置基于注解的声明式事务 -->    <tx:annotation-driven transaction-manager="transactionManager"/></beans>

测试运行结果如下:
这里写图片描述
第一种配置方式通过org.apache.ibatis.builder.xml.XMLConfigBuilder解析xml文件将自定义plugin加载到org.apache.ibatis.session.Configuration实例的interceptorChain字段中

private void pluginElement(XNode parent) throws Exception {    if (parent != null) {      for (XNode child : parent.getChildren()) {        String interceptor = child.getStringAttribute("interceptor");        Properties properties = child.getChildrenAsProperties();        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();        interceptorInstance.setProperties(properties);        configuration.addInterceptor(interceptorInstance);      }    }  }

第二种方式是在实例化org.mybatis.spring.SqlSessionFactoryBean的时候添加进去的,protected SqlSessionFactory buildSqlSessionFactory()的代码片段如下:

if (!isEmpty(this.plugins)) {      for (Interceptor plugin : this.plugins) {        configuration.addInterceptor(plugin);        if (LOGGER.isDebugEnabled()) {          LOGGER.debug("Registered plugin: '" + plugin + "'");        }      }    }

参考资料:http://blog.csdn.net/top_code/article/details/55520948

原创粉丝点击