Fork me on GitHub

你所不知道的mybatis居然也有拦截器

对于mybatis的拦截器这个想法我来自于三个地方

也就是下面这个三个地方是可以使用的,其他的情况需要开发人员根据实际情况来使用。

1、对于分页的查询,我们可以对于分页的方法采用比较规范的命名,然后根据这个命名来拦截需要分页查询的sql然后把分页的总数,分页数,页码数,页码总数等放在一个对象中返回去,这样分页只要调用dao的一个方法即可。

2、读写分离,我们可以在sql执行之前,获取sql是不是查询方法,然后根据这个条件去控制需要访问的数据源。

3、需要统计分析sql的执行时间(这边要说的是这里的执行包含了网络带宽,因为不是在mysql执行前后做的拦截,所以这里的sql并不只是sql在数据库真正执行的时间,要比实际长)

 

如何实现这样一个拦截器

首先mybatis官方早就想到我们开发会有这样的需求,所以开放了一个org.apache.ibatis.plugin.Interceptor这样一个接口。

我们只要实现这个接口并且加上注解然后重写intercept方法。

最后如果你使用的是mybatis.xml也就是mybatis本身单独的配置,你可以需要在这里配置相应的拦截器名字等。

如果你使用的是spring管理的mybatis,那么你需要在spring配置文件里面配置注册相应的拦截器。

 

代码实现

下面对于3,也就是实现统计sql执行时间,简单摘录一下实现代码。

还有两种开发可以根据自己的想法去实现和摸索。

package com.ssm;

import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Properties;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.apache.log4j.Logger;
  
@Intercepts({  
    @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),  
    @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,  
            RowBounds.class, ResultHandler.class }) })  
public class SqlStatementInterceptor implements Interceptor{  
      
    private static Logger logger = Logger.getLogger(SqlStatementInterceptor.class);  
  
    @SuppressWarnings("unused")  
    private Properties properties;  
      
    @Override  
    public Object intercept(Invocation arg0) throws Throwable {  
        MappedStatement mappedStatement = (MappedStatement) arg0.getArgs()[0];  
        Object parameter = null;  
        if (arg0.getArgs().length > 1) {  
            parameter = arg0.getArgs()[1];  
        }  
        String sqlId = mappedStatement.getId();  
        BoundSql boundSql = mappedStatement.getBoundSql(parameter);  
        Configuration configuration = mappedStatement.getConfiguration();  
          
        Object returnValue = null;  
        long start = System.currentTimeMillis();  
        returnValue = arg0.proceed();  
        long end = System.currentTimeMillis();  
        long time = (end - start);  
  
        if (time > 1) {  
            String sql = getSql(configuration, boundSql, sqlId, time);  
            logger.error(sql);
        }  
          
        return returnValue;  
    }  
      
    public static String getSql(Configuration configuration, BoundSql boundSql, String sqlId, long time) {  
        String sql = showSql(configuration, boundSql);  
        StringBuilder str = new StringBuilder(100);  
        str.append(sqlId);  
        str.append(":");  
        str.append(sql);  
        str.append(":");  
        str.append(time);  
        str.append("ms");  
        return str.toString();  
    }  
      
    public static String showSql(Configuration configuration, BoundSql boundSql) {  
        Object parameterObject = boundSql.getParameterObject();  
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();  
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");  
        if (parameterMappings.size() > 0 && parameterObject != null) {  
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();  
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {  
                sql = sql.replaceFirst("\\?", getParameterValue(parameterObject));  
   
            } else {  
                MetaObject metaObject = configuration.newMetaObject(parameterObject);  
                for (ParameterMapping parameterMapping : parameterMappings) {  
                    String propertyName = parameterMapping.getProperty();  
                    if (metaObject.hasGetter(propertyName)) {  
                        Object obj = metaObject.getValue(propertyName);  
                        sql = sql.replaceFirst("\\?", getParameterValue(obj));  
                    } else if (boundSql.hasAdditionalParameter(propertyName)) {  
                        Object obj = boundSql.getAdditionalParameter(propertyName);  
                        sql = sql.replaceFirst("\\?", getParameterValue(obj));  
                    }  
                }  
            }  
        }  
        return sql;  
    }  
      
    private static String getParameterValue(Object obj) {  
        String value = null;  
        if (obj instanceof String) {  
            value = "'" + obj.toString() + "'";  
        } else if (obj instanceof Date) {  
            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);  
            value = "'" + formatter.format(new Date()) + "'";  
        } else {  
            if (obj != null) {  
                value = obj.toString();  
            } else {  
                value = "";  
            }  
   
        }  
        return value;  
    }  
  
    @Override  
    public Object plugin(Object arg0) {  
        return Plugin.wrap(arg0, this);  
    }  
  
    @Override  
    public void setProperties(Properties arg0) {  
        this.properties = arg0;  
    }  
  
}

下面是spring中的配置,如果你是单独配置mybatis配置文件的话,你需要查询一下如何配置

<!-- 配置mybitasSqlSessionFactoryBean -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="typeAliasesPackage" value="com/ssm/entity"></property>
        <property name="mapperLocations" value="classpath*:com/ssm/dao/sqlxml/*.xml"></property>
        
        
        
        <property name="plugins">
            <array>
                <bean class="com.ssm.SqlStatementInterceptor">
                    <property name="properties">
                        <value>
                            property-key=property-value
                        </value>
                    </property>
                </bean>
            </array>
        </property>
    </bean>

会在log日志中输出最后执行的sql和sqlID和sql执行的时间。

 

 

参考资料:

分页实现(本人没有亲自实现,因为现实情况还没有这样特别需要这样的业务逻辑):

http://www.cnblogs.com/jethypc/p/5149183.html

执行时间统计实现(我修改了其中spring的配置,不知为何下面博主的配置并没有用):

http://blog.csdn.net/tq02h2a/article/details/50772652

读写分离实现(其中的第四种方案就是利用了拦截器):

http://www.jianshu.com/p/2222257f96d3

posted @ 2016-11-07 16:46  LinkinStar  阅读(8035)  评论(1编辑  收藏  举报