• 你所不知道的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

  • 相关阅读:
    [JS Compose] 7. Ensure failsafe combination using monoids
    [Functional Programming] Monad
    [Algorithm] Count Negative Integers in Row/Column-Wise Sorted Matrix
    [React] Use the useReducer Hook and Dispatch Actions to Update State (useReducer, useMemo, useEffect)
    [Functional Programming] Pointy Functor Factory
    [Functional Programming] Async IO Functor
    [Functional Programming] Write simple Semigroups type
    [置顶] 阿里IOS面试题之多线程选用NSOperation or GCD
    JAVA实现字符串反转,借助字符数组实现
    菜鸟级SQL Server21天自学通(文档+视频)
  • 原文地址:https://www.cnblogs.com/linkstar/p/6039513.html
Copyright © 2020-2023  润新知