物理分页

      这次我们运用Mybatis拦截器来实现物理分页,后面会运用动态sql来实现,或者运用Map/CollectionUtils/StringUtils编写工具类来实现。oracle是运用的rownum,mysql是运用的limit offset,pagesize。代码中有大量注释,可以参考Mybatis基本原理一起阅读。后面,我们会根据一些实际开发需要,把物理分页功能的代码封装成jar包,以后直接调用就好了,比如Mybatis+Spring3的运行环境,可以采用Mybatis动态sql来实现分页,也可以采用Mybatis插件。

1、项目备份。一定要勤于备份。我们永远不知道我们什么时候,会把早上还跑得通的一个项目,到晚上都还找不到哪里被玩坏了。是的,我们在编写控制层的时候,交互使用了struts2和springmvc,想看看他们之间的差异,然后,就迷糊了,由于忘记备份struts2版本的,到了晚上就只好还原到昨天的去了。很晕很晕。

2、Mybatis插件类,实现拦截器,实现sql重写。每一个拦截器必须实现三个方法。

  1 package com.dyl.util;
  2 
  3 import java.lang.reflect.Field;
  4 import java.sql.Connection;
  5 import java.sql.PreparedStatement;
  6 import java.sql.ResultSet;
  7 import java.sql.SQLException;
  8 import java.util.List;
  9 import java.util.Map;
 10 import java.util.Properties;
 11 
 12 import javax.xml.bind.PropertyException;
 13 
 14 import org.apache.ibatis.builder.xml.dynamic.ForEachSqlNode;
 15 import org.apache.ibatis.executor.ErrorContext;
 16 import org.apache.ibatis.executor.ExecutorException;
 17 import org.apache.ibatis.executor.statement.BaseStatementHandler;
 18 import org.apache.ibatis.executor.statement.RoutingStatementHandler;
 19 import org.apache.ibatis.executor.statement.StatementHandler;
 20 import org.apache.ibatis.mapping.BoundSql;
 21 import org.apache.ibatis.mapping.MappedStatement;
 22 import org.apache.ibatis.mapping.ParameterMapping;
 23 import org.apache.ibatis.mapping.ParameterMode;
 24 import org.apache.ibatis.plugin.Interceptor;
 25 import org.apache.ibatis.plugin.Intercepts;
 26 import org.apache.ibatis.plugin.Invocation;
 27 import org.apache.ibatis.plugin.Plugin;
 28 import org.apache.ibatis.plugin.Signature;
 29 import org.apache.ibatis.reflection.MetaObject;
 30 import org.apache.ibatis.reflection.property.PropertyTokenizer;
 31 import org.apache.ibatis.session.Configuration;
 32 import org.apache.ibatis.type.TypeHandler;
 33 import org.apache.ibatis.type.TypeHandlerRegistry;
 34 /**
 35  * 拦截器签名
 36  * 要拦截的目标类型是StatementHandler(注意:type只能配置成接口类型),拦截的方法是,名称为prepare,参数为Connection类型的方法。
 37  * @author dyl
 38  * @date 2014-7-12
 39  */
 40 @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
 41 public class PagePlugin implements Interceptor {
 42     /**
 43      * Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的)。
 44      * 
 45      * 每一个拦截器都必须实现的三个方法。
 46      * (1)Object intercept(Invocation ivk),实现拦截逻辑的地方,内部要通过invocation.proceed()显式地推进责任链前进,
 47      *  也就是调用下一个拦截器拦截目标方法。
 48      * (2)Object plugin(Object target),用当前这个拦截器生成对目标target的代理,实际是通过Plugin.wrap(target,this)来完成的,
 49      *  把目标target和拦截器this传给了包装函数。
 50      * (3)void setProperties(Properties p),设置额外的参数,参数配置在拦截器的Properties节点里。
 51      * 
 52      * Mybatis配置的插件,运行时发生。
 53      * (1)所有可能被拦截的处理类都会生成一个代理。
 54      * (2)处理类代理在执行对应方法时,判断要不要执行插件中的拦截方法。
 55      * (3)执行插接中的拦截方法后,推进目标的执行。
 56      */
 57     private static String dialect = "";
 58     private static String pageSqlId = "";
 59 /*
 60  * intercept的实现
 61  * 实现拦截逻辑的地方,内部要通过invocation.proceed()显式地推进责任链前进,也就是调用下一个拦截器拦截目标方法。
 62  * @see org.apache.ibatis.plugin.Interceptor#intercept(org.apache.ibatis.plugin.Invocation)
 63  */
 64     @SuppressWarnings("unchecked")
 65     public Object intercept(Invocation ivk) throws Throwable {
 66         /**
 67          * StatementHandler的默认实现类是RoutingStatementHandler,因此拦截的实际对象是它。
 68          * RoutingStatementHandler的主要功能是分发,它根据配置Statement类型创建真正执行数据库操作的StatementHandler,并将其保存到delegate属性里。
 69          * 由于delegate是一个私有属性并且没有提供访问它的方法,因此需要借助ReflectHelper的帮忙。
 70          * 通过ReflectHelper的封装后我们可以轻易的获得想要的属性。
 71          */
 72         if (ivk.getTarget() instanceof RoutingStatementHandler) {
 73             RoutingStatementHandler statementHandler = (RoutingStatementHandler) ivk.getTarget();
 74             BaseStatementHandler delegate = (BaseStatementHandler) ReflectHelper.getValueByFieldName(statementHandler, "delegate");
 75             MappedStatement mappedStatement = (MappedStatement) ReflectHelper.getValueByFieldName(delegate, "mappedStatement");
 76             
 77             // 只重写需要分页的sql语句。通过MappedStatement的ID匹配,默认重写以Page结尾的MappedStatement的sql。
 78             if (mappedStatement.getId().matches(pageSqlId)) {
 79                 BoundSql boundSql = delegate.getBoundSql();
 80                 Object parameterObject = boundSql.getParameterObject();
 81                 if (parameterObject == null) {
 82                     throw new NullPointerException("parameterObject error");
 83                 } else {
 84                     Connection connection = (Connection) ivk.getArgs()[0];
 85                     String sql = boundSql.getSql();
 86                     // 记录总记录数
 87                     String countSql = "select count(0) from (" + sql + ") myCount";
 88                     // System.out.println("总数sql 语句:" + countSql);
 89                     PreparedStatement countStmt = connection.prepareStatement(countSql);
 90                     BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql,
 91                             boundSql.getParameterMappings(), parameterObject);
 92                     // 重设分页参数里的总页数等
 93                     setParameters(countStmt, mappedStatement, countBS, parameterObject);
 94                     
 95                     ResultSet rs = countStmt.executeQuery();
 96                     int count = 0;
 97                     if (rs.next()) {
 98                         count = rs.getInt(1);
 99                     }
100                     rs.close();
101                     countStmt.close();
102                     
103                     // 分页参数作为参数对象parameterObject的一个属性
104                     PageInfo page = null;
105                     if (parameterObject instanceof PageInfo) {
106                         page = (PageInfo) parameterObject;
107                         page.setTotalResult(count);
108                     } else if (parameterObject instanceof Map) {
109                         Map<String, Object> map = (Map<String, Object>) parameterObject;
110                         page = (PageInfo) map.get("page");
111                         if (page == null)
112                             page = new PageInfo();
113                         page.setTotalResult(count);
114                     } else {
115                         Field pageField = ReflectHelper.getFieldByFieldName(parameterObject, "page");
116                         if (pageField != null) {
117                             page = (PageInfo) ReflectHelper.getValueByFieldName(parameterObject, "page");
118                             if (page == null)
119                                 page = new PageInfo();
120                             page.setTotalResult(count);
121                             ReflectHelper.setValueByFieldName(parameterObject, "page", page);
122                         } else {
123                             throw new NoSuchFieldException(parameterObject.getClass().getName());
124                         }
125                     }
126                     // 重写sql
127                     String pageSql = generatePageSql(sql, page);
128                     System.out.println("page sql:" + pageSql);
129                     ReflectHelper.setValueByFieldName(boundSql, "sql", pageSql);
130                 }
131             }
132         }
133         // 将执行权交给下一个拦截器,完成调用链的推进。
134         return ivk.proceed();
135     }
136     /**
137      * 重设分页参数里的总页数等
138      * @param ps
139      * @param mappedStatement
140      * @param boundSql
141      * @param parameterObject
142      * @throws SQLException
143      */
144     private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql, Object parameterObject) 
145             throws SQLException {
146         ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
147         List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
148         if (parameterMappings != null) {
149             Configuration configuration = mappedStatement.getConfiguration();
150             TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
151             MetaObject metaObject = parameterObject == null ? null : configuration.newMetaObject(parameterObject);
152             for (int i = 0; i < parameterMappings.size(); i++) {
153                 ParameterMapping parameterMapping = parameterMappings.get(i);
154                 if (parameterMapping.getMode() != ParameterMode.OUT) {
155                     Object value;
156                     String propertyName = parameterMapping.getProperty();
157                     PropertyTokenizer prop = new PropertyTokenizer(propertyName);
158                     if (parameterObject == null) {
159                         value = null;
160                     } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
161                         value = parameterObject;
162                     } else if (boundSql.hasAdditionalParameter(propertyName)) {
163                         value = boundSql.getAdditionalParameter(propertyName);
164                     } else if (propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX) 
165                             && boundSql.hasAdditionalParameter(prop.getName())) {
166                         value = boundSql.getAdditionalParameter(prop.getName());
167                         if (value != null) {
168                             value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));
169                         }
170                     } else {
171                         value = metaObject == null ? null : metaObject.getValue(propertyName);
172                     }
173                     TypeHandler typeHandler = parameterMapping.getTypeHandler();
174                     if (typeHandler == null) {
175                         throw new ExecutorException("There was no TypeHandler found for parameter " + propertyName + " of statement "
176                                         + mappedStatement.getId());
177                     }
178                     typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType());
179                 }
180             }
181         }
182     }
183     /**
184      * sql重写,在原始的sql语句上加入分页的参数,目前支持mysql和oracle两种数据库的分页。
185      * @param sql
186      * @param page
187      * @return
188      */
189     private String generatePageSql(String sql, PageInfo page) {
190         if (page != null && (dialect != null || !dialect.equals(""))) {
191             StringBuffer pageSql = new StringBuffer();
192             //StringBuilder pageSql = new StringBuilder();
193             if ("mysql".equals(dialect)) {
194                 //pageSql=generatePageSqlForMysql(sql,page);
195                 pageSql.append(sql);
196                 pageSql.append(" limit " + page.getCurrentResult() + "," + page.getShowCount());
197             } else if ("oracle".equals(dialect)) {
198                 //pageSql = generatePageSqlForOracle(sql, page);
199                 pageSql.append("select * from (select tmp_tb.*,ROWNUM row_id from (");
200                 pageSql.append(sql);
201                 pageSql.append(")  tmp_tb where ROWNUM<=");
202                 //pageSql.append(page.getCurrentResult() + page.getShowCount());
203                 pageSql.append(page.getCurrentPage()*page.getShowCount());
204                 pageSql.append(") where row_id>");
205                 //pageSql.append(page.getCurrentResult());
206                 pageSql.append((page.getCurrentPage() - 1) * page.getShowCount());
207             }
208             return pageSql.toString();
209         } else {
210             return sql;
211         }
212     }
213     /**
214      * mysql的分页实现
215      * @param sql
216      * @param page
217      * @return
218      */
219     public StringBuilder generatePageSqlForMysql(String sql, PageInfo page) {
220         StringBuilder pageSql = new StringBuilder(100);
221         String beginrow = String.valueOf((page.getCurrentPage() - 1) * page.getShowCount());
222         pageSql.append(sql);
223         pageSql.append(" limit " + beginrow + "," + page.getShowCount());
224         return pageSql;
225     }
226     /**
227      * oracle的分页实现
228      * @param sql
229      * @param page
230      * @return
231      */
232     public StringBuilder generatePageSqlForOracle(String sql, PageInfo page) {
233         StringBuilder pageSql = new StringBuilder(100);
234         String beginrow = String.valueOf((page.getCurrentPage() - 1) * page.getShowCount());
235         String endrow = String.valueOf(page.getCurrentPage() * page.getShowCount());
236         pageSql.append("select * from ( select temp.*, rownum row_id from ( ");
237         pageSql.append(sql);
238         pageSql.append(" ) temp where rownum <= ").append(endrow);
239         pageSql.append(") where row_id > ").append(beginrow);
240         return pageSql;
241     }
242     /**
243      * plugin的实现
244      * 用当前这个拦截器生成对目标target的代理,实际是通过Plugin.wrap(target,this)来完成的,把目标target和拦截器this传给了包装函数。
245      */
246     public Object plugin(Object target) {
247         // 当目标类是StatementHandler类型时,才包装目标类,否者直接返回目标本身,减少目标被代理的次数。
248         if (target instanceof StatementHandler) {
249             return Plugin.wrap(target, this);
250         } else {
251             return target;
252         }
253     }
254     /**
255      * 设置额外的参数,参数配置在拦截器的Properties节点里。
256      */
257     public void setProperties(Properties p) {
258         dialect = p.getProperty("dialect");
259         if (dialect == null || dialect.equals("")) {
260             try {
261                 throw new PropertyException("dialect property is not found!");
262             } catch (PropertyException e) {
263                 e.printStackTrace();
264             }
265         }
266         pageSqlId = p.getProperty("pageSqlId");
267         if (dialect == null || dialect.equals("")) {
268             try {
269                 throw new PropertyException("pageSqlId property is not found!");
270             } catch (PropertyException e) {
271                 e.printStackTrace();
272             }
273         }
274     }
275 }
View Code

相关文章:

  • 2021-08-06
  • 2021-12-06
  • 2021-11-25
  • 2022-02-06
  • 2021-12-16
  • 2021-07-03
  • 2022-01-23
  • 2022-12-23
猜你喜欢
  • 2021-05-26
  • 2021-11-14
  • 2021-10-03
  • 2021-08-31
  • 2021-11-29
  • 2022-03-03
  • 2022-01-31
相关资源
相似解决方案