`
zzc1684
  • 浏览: 1230792 次
  • 性别: Icon_minigender_1
  • 来自: 广州
文章分类
社区版块
存档分类
最新评论

在mybatis执行SQL语句之前进行拦击处理

阅读更多

 

比较适用于在分页时候进行拦截。对分页的SQL语句通过封装处理,处理成不同的分页sql。

实用性比较强。

 

    import java.sql.Connection;  
    import java.sql.PreparedStatement;  
    import java.sql.ResultSet;  
    import java.sql.SQLException;  
    import java.util.List;  
    import java.util.Properties;  
      
    import org.apache.ibatis.executor.parameter.ParameterHandler;  
    import org.apache.ibatis.executor.statement.RoutingStatementHandler;  
    import org.apache.ibatis.executor.statement.StatementHandler;  
    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.scripting.defaults.DefaultParameterHandler;  
      
    import com.yidao.utils.Page;  
    import com.yidao.utils.ReflectHelper;  
      
    /**  
     *  
     * 分页拦截器,用于拦截需要进行分页查询的操作,然后对其进行分页处理。  
     * 利用拦截器实现Mybatis分页的原理:  
     * 要利用JDBC对数据库进行操作就必须要有一个对应的Statement对象,Mybatis在执行Sql语句前就会产生一个包含Sql语句的Statement对象,而且对应的Sql语句  
     * 是在Statement之前产生的,所以我们就可以在它生成Statement之前对用来生成Statement的Sql语句下手。在Mybatis中Statement语句是通过RoutingStatementHandler对象的  
     * prepare方法生成的。所以利用拦截器实现Mybatis分页的一个思路就是拦截StatementHandler接口的prepare方法,然后在拦截器方法中把Sql语句改成对应的分页查询Sql语句,之后再调用  
     * StatementHandler对象的prepare方法,即调用invocation.proceed()。  
     * 对于分页而言,在拦截器里面我们还需要做的一个操作就是统计满足当前条件的记录一共有多少,这是通过获取到了原始的Sql语句后,把它改为对应的统计语句再利用Mybatis封装好的参数和设  
     * 置参数的功能把Sql语句中的参数进行替换,之后再执行查询记录数的Sql语句进行总记录数的统计。  
     *  
     */    
    @Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})  
    public class PageInterceptor implements Interceptor {  
        private String dialect = ""; //数据库方言    
        private String pageSqlId = ""; //mapper.xml中需要拦截的ID(正则匹配)    
            
        public Object intercept(Invocation invocation) throws Throwable {  
            //对于StatementHandler其实只有两个实现类,一个是RoutingStatementHandler,另一个是抽象类BaseStatementHandler,    
            //BaseStatementHandler有三个子类,分别是SimpleStatementHandler,PreparedStatementHandler和CallableStatementHandler,    
            //SimpleStatementHandler是用于处理Statement的,PreparedStatementHandler是处理PreparedStatement的,而CallableStatementHandler是    
            //处理CallableStatement的。Mybatis在进行Sql语句处理的时候都是建立的RoutingStatementHandler,而在RoutingStatementHandler里面拥有一个    
            //StatementHandler类型的delegate属性,RoutingStatementHandler会依据Statement的不同建立对应的BaseStatementHandler,即SimpleStatementHandler、    
            //PreparedStatementHandler或CallableStatementHandler,在RoutingStatementHandler里面所有StatementHandler接口方法的实现都是调用的delegate对应的方法。    
            //我们在PageInterceptor类上已经用@Signature标记了该Interceptor只拦截StatementHandler接口的prepare方法,又因为Mybatis只有在建立RoutingStatementHandler的时候    
            //是通过Interceptor的plugin方法进行包裹的,所以我们这里拦截到的目标对象肯定是RoutingStatementHandler对象。  
            if(invocation.getTarget() instanceof RoutingStatementHandler){    
                RoutingStatementHandler statementHandler = (RoutingStatementHandler)invocation.getTarget();    
                StatementHandler delegate = (StatementHandler) ReflectHelper.getFieldValue(statementHandler, "delegate");    
                BoundSql boundSql = delegate.getBoundSql();  
                Object obj = boundSql.getParameterObject();  
                if (obj instanceof Page<?>) {    
                    Page<?> page = (Page<?>) obj;    
                    //通过反射获取delegate父类BaseStatementHandler的mappedStatement属性    
                    MappedStatement mappedStatement = (MappedStatement)ReflectHelper.getFieldValue(delegate, "mappedStatement");    
                    //拦截到的prepare方法参数是一个Connection对象    
                    Connection connection = (Connection)invocation.getArgs()[0];    
                    //获取当前要执行的Sql语句,也就是我们直接在Mapper映射语句中写的Sql语句    
                    String sql = boundSql.getSql();    
                    //给当前的page参数对象设置总记录数    
                    this.setTotalRecord(page,    
                           mappedStatement, connection);    
                    //获取分页Sql语句    
                    String pageSql = this.getPageSql(page, sql);    
                    //利用反射设置当前BoundSql对应的sql属性为我们建立好的分页Sql语句    
                    ReflectHelper.setFieldValue(boundSql, "sql", pageSql);    
                }   
            }    
            return invocation.proceed();    
        }  
          
        /**  
         * 给当前的参数对象page设置总记录数  
         *  
         * @param page Mapper映射语句对应的参数对象  
         * @param mappedStatement Mapper映射语句  
         * @param connection 当前的数据库连接  
         */    
        private void setTotalRecord(Page<?> page,    
               MappedStatement mappedStatement, Connection connection) {    
           //获取对应的BoundSql,这个BoundSql其实跟我们利用StatementHandler获取到的BoundSql是同一个对象。    
           //delegate里面的boundSql也是通过mappedStatement.getBoundSql(paramObj)方法获取到的。    
           BoundSql boundSql = mappedStatement.getBoundSql(page);    
           //获取到我们自己写在Mapper映射语句中对应的Sql语句    
           String sql = boundSql.getSql();    
           //通过查询Sql语句获取到对应的计算总记录数的sql语句    
           String countSql = this.getCountSql(sql);    
           //通过BoundSql获取对应的参数映射    
           List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();    
           //利用Configuration、查询记录数的Sql语句countSql、参数映射关系parameterMappings和参数对象page建立查询记录数对应的BoundSql对象。    
           BoundSql countBoundSql = new BoundSql(mappedStatement.getConfiguration(), countSql, parameterMappings, page);    
           //通过mappedStatement、参数对象page和BoundSql对象countBoundSql建立一个用于设定参数的ParameterHandler对象    
           ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, page, countBoundSql);    
           //通过connection建立一个countSql对应的PreparedStatement对象。    
           PreparedStatement pstmt = null;    
           ResultSet rs = null;    
           try {    
               pstmt = connection.prepareStatement(countSql);    
               //通过parameterHandler给PreparedStatement对象设置参数    
               parameterHandler.setParameters(pstmt);    
               //之后就是执行获取总记录数的Sql语句和获取结果了。    
               rs = pstmt.executeQuery();    
               if (rs.next()) {    
                  int totalRecord = rs.getInt(1);    
                  //给当前的参数page对象设置总记录数    
                  page.setTotalRecord(totalRecord);    
               }    
           } catch (SQLException e) {    
               e.printStackTrace();    
           } finally {    
               try {    
                  if (rs != null)    
                      rs.close();    
                   if (pstmt != null)    
                      pstmt.close();    
               } catch (SQLException e) {    
                  e.printStackTrace();    
               }    
           }    
        }    
          
        /**  
         * 根据原Sql语句获取对应的查询总记录数的Sql语句  
         * @param sql  
         * @return  
         */    
        private String getCountSql(String sql) {    
           int index = sql.indexOf("from");    
           return "select count(*) " + sql.substring(index);    
        }    
          
        /**  
         * 根据page对象获取对应的分页查询Sql语句,这里只做了两种数据库类型,Mysql和Oracle  
         * 其它的数据库都 没有进行分页  
         *  
         * @param page 分页对象  
         * @param sql 原sql语句  
         * @return  
         */    
        private String getPageSql(Page<?> page, String sql) {    
           StringBuffer sqlBuffer = new StringBuffer(sql);    
           if ("mysql".equalsIgnoreCase(dialect)) {    
               return getMysqlPageSql(page, sqlBuffer);    
           } else if ("oracle".equalsIgnoreCase(dialect)) {    
               return getOraclePageSql(page, sqlBuffer);    
           }    
           return sqlBuffer.toString();    
        }    
          
        /**  
        * 获取Mysql数据库的分页查询语句  
        * @param page 分页对象  
        * @param sqlBuffer 包含原sql语句的StringBuffer对象  
        * @return Mysql数据库分页语句  
        */    
       private String getMysqlPageSql(Page<?> page, StringBuffer sqlBuffer) {    
          //计算第一条记录的位置,Mysql中记录的位置是从0开始的。    
    //     System.out.println("page:"+page.getPage()+"-------"+page.getRows());  
          int offset = (page.getPage() - 1) * page.getRows();    
          sqlBuffer.append(" limit ").append(offset).append(",").append(page.getRows());    
          return sqlBuffer.toString();    
       }    
          
       /**  
        * 获取Oracle数据库的分页查询语句  
        * @param page 分页对象  
        * @param sqlBuffer 包含原sql语句的StringBuffer对象  
        * @return Oracle数据库的分页查询语句  
        */    
       private String getOraclePageSql(Page<?> page, StringBuffer sqlBuffer) {    
          //计算第一条记录的位置,Oracle分页是通过rownum进行的,而rownum是从1开始的    
          int offset = (page.getPage() - 1) * page.getRows() + 1;    
          sqlBuffer.insert(0, "select u.*, rownum r from (").append(") u where rownum < ").append(offset + page.getRows());    
          sqlBuffer.insert(0, "select * from (").append(") where r >= ").append(offset);    
          //上面的Sql语句拼接之后大概是这个样子:    
          //select * from (select u.*, rownum r from (select * from t_user) u where rownum < 31) where r >= 16    
          return sqlBuffer.toString();    
       }    
         
            
        /**  
         * 拦截器对应的封装原始对象的方法  
         */          
        public Object plugin(Object arg0) {    
            // TODO Auto-generated method stub    
            if (arg0 instanceof StatementHandler) {    
                return Plugin.wrap(arg0, this);    
            } else {    
                return arg0;    
            }   
        }    
        
        /**  
         * 设置注册拦截器时设定的属性  
         */   
        public void setProperties(Properties p) {  
              
        }  
      
        public String getDialect() {  
            return dialect;  
        }  
      
        public void setDialect(String dialect) {  
            this.dialect = dialect;  
        }  
      
        public String getPageSqlId() {  
            return pageSqlId;  
        }  
      
        public void setPageSqlId(String pageSqlId) {  
            this.pageSqlId = pageSqlId;  
        }  
          
    }  

 

xml配置

    <!-- MyBatis 接口编程配置  -->  
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
            <!-- basePackage指定要扫描的包,在此包之下的映射器都会被搜索到,可指定多个包,包与包之间用逗号或分号分隔-->  
            <property name="basePackage" value="com.yidao.mybatis.dao" />  
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />  
        </bean>  
          
        <!-- MyBatis 分页拦截器-->  
        <bean id="paginationInterceptor" class="com.mybatis.interceptor.PageInterceptor">  
            <property name="dialect" value="mysql"/>   
            <!-- 拦截Mapper.xml文件中,id包含query字符的语句 -->   
            <property name="pageSqlId" value=".*query$"/>  
        </bean>   

 

 

分享到:
评论

相关推荐

    在mybatis执行SQL语句之前进行拦击处理实例

    在MyBatis执行SQL语句之前进行拦截处理实例,需要通过Interceptor接口,并在intercept方法中对原始的SQL语句进行修改,以便对其进行分页处理。同时,我们需要在Mapper.xml文件中,配置好对应的拦截器,以便MyBatis...

    Mybatis执行SQL语句的方式

    Mybatis 提供了一个基于注解的方式来执行 SQL 语句,我们可以在接口中使用@Select 注解来定义 SQL 语句。例如: ```java public interface UserMapper { @Select("SELECT * FROM table01 WHERE id = #{id}") User ...

    mybatis直接执行sql语句后续之一

    这篇博客“mybatis直接执行sql语句后续之一”可能探讨了如何在MyBatis中高效且有效地执行SQL操作。下面我们将深入探讨MyBatis的SQL执行机制及相关知识点。 首先,MyBatis的核心组件是SqlSessionFactory,它是创建...

    mybatis动态SQL语句

    if 、where、set、trim、choose 、foreach等在mybatis中的具体用法,有具体实例可供参考,玩转mybatis

    原样输出mybatis的sql执行语句(mysql和oracle都可用).zip

    本文将深入探讨如何在SpringBoot(整合MyBatis)和传统的SSM(Spring、SpringMVC、MyBatis)项目中,原样输出MyBatis的SQL执行语句,以便于开发者直观地检查SQL语句是否正确和高效,从而进行优化和调试。此方法对...

    详解MyBatis直接执行SQL查询及数据批量插入

    在本文中,我们将深入探讨如何使用MyBatis直接执行SQL查询以及如何进行数据的批量插入。 **一、直接执行SQL查询** 在MyBatis中,你可以通过Mapper接口和XML配置文件来直接执行自定义的SQL查询。下面是一个简单的...

    ideal mybatis打印sql插件

    把 mybatis 输出的sql日志还原成完整的sql语句。 将日志输出的sql语句中的问号 ? 替换成真正的参数值。 通过 "Tools -&gt; MyBatis Log Plugin" 菜单或快捷键 "Ctrl+Shift+Alt+O" 启用。 点击窗口左边的 "Filter" ...

    mybatis+spring 框架中配置日志中显示sql语句

    在MyBatis与Spring整合的框架中,为了便于调试和性能优化,我们常常需要在日志中打印出执行的SQL语句。以下是如何在这样的环境中配置日志来显示SQL语句的详细步骤。 首先,我们需要了解MyBatis的日志实现。MyBatis...

    mybatis慢SQL插件

    拦截器监控慢SQL并将完整的可执行的SQL语句打印在日志文件中,复制该SQL语句即可在数据库工具中执行。 使用方法: 找到你springboot项目中的配置文件,增加如下配置即可 application.yml 配置如下: sql: slow...

    Mybatis案例一所用建表语句

    最后,在服务层(Service)或控制层(Controller)调用这个`createTable`方法,Mybatis会执行对应的SQL,完成建表操作。 总结一下,Mybatis案例一所涉及的知识点主要包括: 1. Mybatis的基本架构:SQL映射文件、...

    mybatis 动态sql及参数传递

    在实际开发过程中,我们往往需要编写复杂的SQL语句,拼接稍有不注意就会导致错误,Mybatis给开发者提供了动态SQL,大大降低了拼接SQL导致的错误。 动态标签 if标签 if标签通常用那个胡where语句,update语句,insert...

    idea插件mybaits log 打印sql语句

    MyBatis是一个流行的Java持久层框架,它简化了数据库操作,而这个插件则增强了MyBatis的调试能力,尤其是在处理复杂的动态SQL和多数据源时,能够提供清晰的SQL执行日志。 在实际使用中,该插件可能会提供以下功能:...

    从mybatis日志中解析出可执行的sql语句

    适用场景:生产环境从sql日志获取可执行sql语句 使用方法:从日志中获取完整的sql片段(可以前后多复制一部分,确保准确性),打开html文件,粘贴到输入框中点击解析sql,获得可执行的sql

    Mybatis日志SQL解析工具

    该工具可以将mybatis输出的sql日志提取出来,并将其格式化为可以直接执行的sql语句,节约开发人员时间

    MyBatis执行SQL并将结果映射成Java对象.docx

    映射文件(通常是XML或注解形式)则包含了具体的SQL语句和结果映射,使得MyBatis能够根据这些信息执行SQL并返回结果。 在MyBatis中,SqlSessionFactory是一个关键组件,它是通过解析配置文件创建的。...

    MyBatis 3 _ SQL 语句构建器1

    MyBatis 3 的 SQL 语句构建器显著提高了开发效率,尤其是在处理动态 SQL 的情况下。通过这样的方式,开发者能够更方便地构建、维护和调试 SQL 语句,同时减少了硬编码 SQL 语句带来的潜在错误。此外,它还支持预编译...

    mybatis自动sql生成插件源码

    在MyBatis中,拦截器用于在特定的执行点插入自定义行为,比如在SQL语句执行前或后。`AutoMapperInterceptor`实现了`org.apache.ibatis.plugin.Interceptor`接口,具备拦截执行方法的能力。它会检测到对Mapper接口的...

    mybatis-sql-dialect

    通过使用SQL方言包,MyBatis能够更好地适应各种数据库,如MySQL、Oracle和DB2,使得在切换数据库时无需对SQL语句进行大量修改。 1. **MyBatis框架概述** MyBatis是一个轻量级的ORM(对象关系映射)框架,它消除了...

    mybatis拦截器修改执行sql语句

    1.网上搜索了很多,几乎都是能修改sql, 但是修改后的sql不生效,还是执行原来的sql. 2.这个版本亲测可以生效。 3.支持分页查询

    Mybatis中SQL语句的编写.pdf

    在MyBatis框架中,SQL语句的编写是一项核心任务。MyBatis作为一款优秀的持久层框架,支持多种方式来定义SQL语句,包括XML配置文件和注解等方式。本文档主要介绍在XML配置文件中如何编写SQL语句,以及相关的属性配置...

Global site tag (gtag.js) - Google Analytics