文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2023-05-31 16:08

关注

比较适用于在分页时候进行拦截。对分页的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;    @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();    }          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();        }      }    }           private String getCountSql(String sql) {      int index = sql.indexOf("from");      return "select count(*) " + sql.substring(index);    }           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();    }           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();    }           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;   }    } 

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 资料下载
  • 历年真题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯