比较适用于在分页时候进行拦截。对分页的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
软考中级精品资料免费领
- 历年真题答案解析
- 备考技巧名师总结
- 高频考点精准押题
- 资料下载
- 历年真题
193.9 KB下载数265
191.63 KB下载数245
143.91 KB下载数1142
183.71 KB下载数642
644.84 KB下载数2755
相关文章
发现更多好内容猜你喜欢
AI推送时光机在mybatis执行SQL语句之前进行拦击处理实例
后端开发2023-05-31
咦!没有更多了?去看看其它编程学习网 内容吧