mybatis常用分页插件,快速分页处理

在未分享整个查询分页的执行代码之前,先了解一下执行流程。

1.总体上是利用mybatis的插件拦截器,在sql执行之前拦截,为查询语句加上limit X X

2.用一个Page对象,贯穿整个执行流程,这个Page对象需要用Java编写前端分页组件

3.用一套比较完整的三层entity,dao,service支持这个分页架构

4.这个分页用到的一些辅助类

注:分享的内容较多,这边的话我就不把需要的jar一一列举,大家使用这个分页功能的时候缺少什么就去晚上找什么jar包即可,尽可能用maven包导入因为maven能减少版本冲突等比较好的优势。我只能说尽可能让大家快速使用这个比较好用的分页功能,如果讲得不明白,欢迎加我QQ一起探讨1063150576,。莫喷哈!还有就是文章篇幅可能会比较大,不过花点时间,把它看完并实践一下一定会收获良多。


第一步:既然主题是围绕怎么进行分页的,我们就从mybatis入手,首先,我们把mybatis相关的两个比较重要的配置文件拿出来做简要的理解,一个是mybatis-config.xml,另外一个是实体所对应的mapper配置文件,我会在配置文件上写好注释,大家一看就会明白。

mybatis-config.xml

  1. <!DOCTYPE configuration  
  2.     PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
  3.     "http://mybatis.org/dtd/mybatis-3-config.dtd">  
  4.   
  5. <configuration>  
  6.   
  7.     <!-- 全局参数 -->  
  8.     <settings>  
  9.         <!-- 使全局的映射器启用或禁用缓存。 -->  
  10.         <setting name="cacheEnabled" value="false"/>  
  11.           
  12.         <!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。 -->  
  13.         <setting name="lazyLoadingEnabled" value="true"/>  
  14.           
  15.         <!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载。 -->  
  16.         <setting name="aggressiveLazyLoading" value="true"/>  
  17.           
  18.         <!-- 是否允许单条sql 返回多个数据集  (取决于驱动的兼容性) default:true -->  
  19.         <setting name="multipleResultSetsEnabled" value="true"/>  
  20.           
  21.         <!-- 是否可以使用列的别名 (取决于驱动的兼容性) default:true -->  
  22.         <setting name="useColumnLabel" value="true"/>  
  23.           
  24.         <!-- 允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。  default:false  -->  
  25.         <setting name="useGeneratedKeys" value="false"/>  
  26.           
  27.         <!-- 指定 MyBatis 如何自动映射 数据基表的列 NONE:不隐射 PARTIAL:部分  FULL:全部  -->    
  28.         <setting name="autoMappingBehavior" value="PARTIAL"/>  
  29.           
  30.         <!-- 这是默认的执行类型  (SIMPLE: 简单; REUSE: 执行器可能重复使用prepared statements语句;BATCH: 执行器可以重复执行语句和批量更新)  -->  
  31.         <setting name="defaultExecutorType" value="SIMPLE"/>  
  32.           
  33.         <!-- 使用驼峰命名法转换字段。 -->  
  34.         <setting name="mapUnderscoreToCamelCase" value="true"/>  
  35.           
  36.         <!-- 设置本地缓存范围 session:就会有数据的共享  statement:语句范围 (这样就不会有数据的共享 ) defalut:session -->  
  37.         <setting name="localCacheScope" value="SESSION"/>  
  38.           
  39.         <!-- 设置但JDBC类型为空时,某些驱动程序 要指定值,default:OTHER,插入空值时不需要指定类型 -->  
  40.         <setting name="jdbcTypeForNull" value="NULL"/>  
  41.           
  42.         <setting name="logPrefix" value="dao."/>  
  43.   
  44.     </settings>  
  45.       
  46.   
  47.     <!--别名是一个较短的Java 类型的名称 -->  
  48.     <typeAliases>  
  49.         <typeAlias type="com.store.base.model.StoreUser"  
  50.             alias="User"></typeAlias>            
  51.         <typeAlias type="com.store.base.secondmodel.pratice.model.Product"  
  52.             alias="Product"></typeAlias>             
  53.         <typeAlias type="com.store.base.secondmodel.base.Page"  
  54.             alias="Page"></typeAlias>            
  55.     </typeAliases>  
  56.       
  57.     <!-- 插件配置,这边为mybatis配置分页拦截器,这个分页拦截器需要我们自己实现 -->  
  58.     <plugins>  
  59.         <plugin interceptor="com.store.base.secondmodel.base.pageinterceptor.PaginationInterceptor" />  
  60.     </plugins>  
  61.       
  62. </configuration>  
一个ProductMapper.xml作为测试对象,这个mapper文件就简单配置一个需要用到的查询语句
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >  
  3. <mapper namespace="com.store.base.secondmodel.pratice.dao.ProductDao" >  
  4.   
  5.     <sql id="baseColumns" >  
  6.         id, product_name as productName, product_no as productNo, price as price  
  7.     </sql>  
  8.   
  9.     <select id="findList" resultType="com.store.base.secondmodel.pratice.model.Product">  
  10.         select <include refid="baseColumns"/> from t_store_product  
  11.     </select>  
  12.   
  13. </mapper>  

第二步:接下去主要针对这个分页拦截器进行深入分析学习,主要有以下几个类和其对应接口

(1)BaseInterceptor 拦截器基础类

(2)PaginationInterceptor 我们要使用的分页插件类,继承上面基础类

(3)SQLHelper 主要是用来提前执行count语句,还有就是获取整个完整的分页语句

(4)Dialect,MysqlDialect,主要用来数据库是否支持limit语句,然后封装完整limit语句

以下是这几个类的分享展示

BaseInterceptor.java

  1. package com.store.base.secondmodel.base.pageinterceptor;  
  2.   
  3. import java.io.Serializable;  
  4. import java.util.Properties;  
  5.   
  6. import org.apache.ibatis.logging.Log;  
  7. import org.apache.ibatis.logging.LogFactory;  
  8. import org.apache.ibatis.plugin.Interceptor;  
  9.   
  10. import com.store.base.secondmodel.base.Global;  
  11. import com.store.base.secondmodel.base.Page;  
  12. import com.store.base.secondmodel.base.dialect.Dialect;  
  13. import com.store.base.secondmodel.base.dialect.MySQLDialect;  
  14. import com.store.base.util.Reflections;  
  15.   
  16.   
  17.   
  18. /** 
  19.  * Mybatis分页拦截器基类 
  20.  * @author yiyong_wu 
  21.  * 
  22.  */  
  23. public abstract class BaseInterceptor implements Interceptor, Serializable {  
  24.   
  25.     private static final long serialVersionUID = 1L;  
  26.   
  27.     protected static final String PAGE = "page";  
  28.       
  29.     protected static final String DELEGATE = "delegate";  
  30.   
  31.     protected static final String MAPPED_STATEMENT = "mappedStatement";  
  32.   
  33.     protected Log log = LogFactory.getLog(this.getClass());  
  34.   
  35.     protected Dialect DIALECT;  
  36.       
  37.   
  38.     /** 
  39.      * 对参数进行转换和检查 
  40.      * @param parameterObject 参数对象 
  41.      * @param page            分页对象 
  42.      * @return 分页对象 
  43.      * @throws NoSuchFieldException 无法找到参数 
  44.      */  
  45.     @SuppressWarnings("unchecked")  
  46.     protected static Page<Object> convertParameter(Object parameterObject, Page<Object> page) {  
  47.         try{  
  48.             if (parameterObject instanceof Page) {  
  49.                 return (Page<Object>) parameterObject;  
  50.             } else {  
  51.                 return (Page<Object>)Reflections.getFieldValue(parameterObject, PAGE);  
  52.             }  
  53.         }catch (Exception e) {  
  54.             return null;  
  55.         }  
  56.     }  
  57.       
  58.     /** 
  59.      * 设置属性,支持自定义方言类和制定数据库的方式 
  60.      * <code>dialectClass</code>,自定义方言类。可以不配置这项 
  61.      * <ode>dbms</ode> 数据库类型,插件支持的数据库 
  62.      * <code>sqlPattern</code> 需要拦截的SQL ID 
  63.      * @param p 属性 
  64.      */  
  65.     protected void initProperties(Properties p) {  
  66.         Dialect dialect = null;  
  67.         String dbType = Global.getConfig("jdbc.type");  
  68.         if("mysql".equals(dbType)){  
  69.             dialect = new MySQLDialect();  
  70.         }  
  71.         if (dialect == null) {  
  72.             throw new RuntimeException("mybatis dialect error.");  
  73.         }  
  74.         DIALECT = dialect;  
  75.     }  
  76. }  

PaginationInterceptor.java 
  1. package com.store.base.secondmodel.base.pageinterceptor;  
  2.   
  3. import java.util.Properties;  
  4.   
  5. import org.apache.ibatis.executor.Executor;  
  6. import org.apache.ibatis.mapping.BoundSql;  
  7. import org.apache.ibatis.mapping.MappedStatement;  
  8. import org.apache.ibatis.mapping.SqlSource;  
  9. import org.apache.ibatis.plugin.Intercepts;  
  10. import org.apache.ibatis.plugin.Invocation;  
  11. import org.apache.ibatis.plugin.Plugin;  
  12. import org.apache.ibatis.plugin.Signature;  
  13. import org.apache.ibatis.reflection.MetaObject;  
  14. import org.apache.ibatis.session.ResultHandler;  
  15. import org.apache.ibatis.session.RowBounds;  
  16.   
  17. import com.store.base.secondmodel.base.Page;  
  18. import com.store.base.secondmodel.base.util.StringUtils;  
  19. import com.store.base.util.Reflections;  
  20.   
  21.   
  22. /** 
  23.  * 数据库分页插件,只拦截查询语句. 
  24.  * @author yiyong_wu 
  25.  *  
  26.  */  
  27. @Intercepts({ @Signature(type = Executor.class, method = "query", args = {  
  28.         MappedStatement.class, Object.class, RowBounds.class,  
  29.         ResultHandler.class }) })  
  30. public class PaginationInterceptor extends BaseInterceptor {  
  31.   
  32.     private static final long serialVersionUID = 1L;  
  33.   
  34.     @Override  
  35.     public Object intercept(Invocation invocation) throws Throwable {  
  36.         final MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];  
  37.           
  38.         Object parameter = invocation.getArgs()[1];  
  39.         BoundSql boundSql = mappedStatement.getBoundSql(parameter);  
  40.         Object parameterObject = boundSql.getParameterObject();  
  41.   
  42.         // 获取分页参数对象  
  43.         Page<Object> page = null;  
  44.         if (parameterObject != null) {  
  45.             page = convertParameter(parameterObject, page);  
  46.         }  
  47.   
  48.         // 如果设置了分页对象,则进行分页  
  49.         if (page != null && page.getPageSize() != -1) {  
  50.   
  51.             if (StringUtils.isBlank(boundSql.getSql())) {  
  52.                 return null;  
  53.             }  
  54.             String originalSql = boundSql.getSql().trim();  
  55.   
  56.             // 得到总记录数  
  57.             page.setCount(SQLHelper.getCount(originalSql, null,mappedStatement, parameterObject, boundSql, log));  
  58.             // 分页查询 本地化对象 修改数据库注意修改实现  
  59.             String pageSql = SQLHelper.generatePageSql(originalSql, page,DIALECT);  
  60.             invocation.getArgs()[2] = new RowBounds(RowBounds.NO_ROW_OFFSET,RowBounds.NO_ROW_LIMIT);  
  61.             BoundSql newBoundSql = new BoundSql(  
  62.                     mappedStatement.getConfiguration(), pageSql,  
  63.                     boundSql.getParameterMappings(),  
  64.                     boundSql.getParameterObject());  
  65.             // 解决MyBatis 分页foreach 参数失效 start  
  66.             if (Reflections.getFieldValue(boundSql, "metaParameters") != null) {  
  67.                 MetaObject mo = (MetaObject) Reflections.getFieldValue(  
  68.                         boundSql, "metaParameters");  
  69.                 Reflections.setFieldValue(newBoundSql, "metaParameters", mo);  
  70.             }  
  71.             // 解决MyBatis 分页foreach 参数失效 end  
  72.             MappedStatement newMs = copyFromMappedStatement(mappedStatement,new BoundSqlSqlSource(newBoundSql));  
  73.   
  74.             invocation.getArgs()[0] = newMs;  
  75.         }  
  76.         return invocation.proceed();  
  77.     }  
  78.   
  79.     @Override  
  80.     public Object plugin(Object target) {  
  81.         return Plugin.wrap(target, this);  
  82.     }  
  83.   
  84.     @Override  
  85.     public void setProperties(Properties properties) {  
  86.         super.initProperties(properties);  
  87.     }  
  88.   
  89.     private MappedStatement copyFromMappedStatement(MappedStatement ms,  
  90.             SqlSource newSqlSource) {  
  91.         MappedStatement.Builder builder = new MappedStatement.Builder(  
  92.                 ms.getConfiguration(), ms.getId(), newSqlSource,  
  93.                 ms.getSqlCommandType());  
  94.         builder.resource(ms.getResource());  
  95.         builder.fetchSize(ms.getFetchSize());  
  96.         builder.statementType(ms.getStatementType());  
  97.         builder.keyGenerator(ms.getKeyGenerator());  
  98.         if (ms.getKeyProperties() != null) {  
  99.             for (String keyProperty : ms.getKeyProperties()) {  
  100.                 builder.keyProperty(keyProperty);  
  101.             }  
  102.         }  
  103.         builder.timeout(ms.getTimeout());  
  104.         builder.parameterMap(ms.getParameterMap());  
  105.         builder.resultMaps(ms.getResultMaps());  
  106.         builder.cache(ms.getCache());  
  107.         return builder.build();  
  108.     }  
  109.   
  110.     public static class BoundSqlSqlSource implements SqlSource {  
  111.         BoundSql boundSql;  
  112.   
  113.         public BoundSqlSqlSource(BoundSql boundSql) {  
  114.             this.boundSql = boundSql;  
  115.         }  
  116.   
  117.         @Override  
  118.         public BoundSql getBoundSql(Object parameterObject) {  
  119.             return boundSql;  
  120.         }  
  121.     }  
  122.   
  123. }  
SQLHelper.java
  1. package com.store.base.secondmodel.base.pageinterceptor;  
  2.   
  3. import java.sql.Connection;  
  4. import java.sql.PreparedStatement;  
  5. import java.sql.ResultSet;  
  6. import java.sql.SQLException;  
  7. import java.util.List;  
  8. import java.util.regex.Matcher;  
  9. import java.util.regex.Pattern;  
  10.   
  11. import org.apache.ibatis.executor.ErrorContext;  
  12. import org.apache.ibatis.executor.ExecutorException;  
  13. import org.apache.ibatis.logging.Log;  
  14. import org.apache.ibatis.mapping.BoundSql;  
  15. import org.apache.ibatis.mapping.MappedStatement;  
  16. import org.apache.ibatis.mapping.ParameterMapping;  
  17. import org.apache.ibatis.mapping.ParameterMode;  
  18. import org.apache.ibatis.reflection.MetaObject;  
  19. import org.apache.ibatis.reflection.property.PropertyTokenizer;  
  20. import org.apache.ibatis.scripting.xmltags.ForEachSqlNode;  
  21. import org.apache.ibatis.session.Configuration;  
  22. import org.apache.ibatis.type.TypeHandler;  
  23. import org.apache.ibatis.type.TypeHandlerRegistry;  
  24.   
  25. import com.store.base.secondmodel.base.Global;  
  26. import com.store.base.secondmodel.base.Page;  
  27. import com.store.base.secondmodel.base.dialect.Dialect;  
  28. import com.store.base.secondmodel.base.util.StringUtils;  
  29. import com.store.base.util.Reflections;  
  30.   
  31.   
  32. /** 
  33.  * SQL工具类 
  34.  * @author yiyong_wu 
  35.  * 
  36.  */  
  37. public class SQLHelper {  
  38.       
  39.     /** 
  40.      * 默认私有构造函数 
  41.      */  
  42.     private SQLHelper() {  
  43.           
  44.     }  
  45.   
  46.     /** 
  47.      * 对SQL参数(?)设值,参考org.apache.ibatis.executor.parameter.DefaultParameterHandler 
  48.      * 
  49.      * @param ps              表示预编译的 SQL 语句的对象。 
  50.      * @param mappedStatement MappedStatement 
  51.      * @param boundSql        SQL 
  52.      * @param parameterObject 参数对象 
  53.      * @throws java.sql.SQLException 数据库异常 
  54.      */  
  55.     @SuppressWarnings("unchecked")  
  56.     public static void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql, Object parameterObject) throws SQLException {  
  57.         ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());  
  58.         List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();  
  59.         if (parameterMappings != null) {  
  60.             Configuration configuration = mappedStatement.getConfiguration();  
  61.             TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();  
  62.             MetaObject metaObject = parameterObject == null ? null :  
  63.                     configuration.newMetaObject(parameterObject);  
  64.             for (int i = 0; i < parameterMappings.size(); i++) {  
  65.                 ParameterMapping parameterMapping = parameterMappings.get(i);  
  66.                 if (parameterMapping.getMode() != ParameterMode.OUT) {  
  67.                     Object value;  
  68.                     String propertyName = parameterMapping.getProperty();  
  69.                     PropertyTokenizer prop = new PropertyTokenizer(propertyName);  
  70.                     if (parameterObject == null) {  
  71.                         value = null;  
  72.                     } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {  
  73.                         value = parameterObject;  
  74.                     } else if (boundSql.hasAdditionalParameter(propertyName)) {  
  75.                         value = boundSql.getAdditionalParameter(propertyName);  
  76.                     } else if (propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX) && boundSql.hasAdditionalParameter(prop.getName())) {  
  77.                         value = boundSql.getAdditionalParameter(prop.getName());  
  78.                         if (value != null) {  
  79.                             value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));  
  80.                         }  
  81.                     } else {  
  82.                         value = metaObject == null ? null : metaObject.getValue(propertyName);  
  83.                     }  
  84.                     @SuppressWarnings("rawtypes")  
  85.                     TypeHandler typeHandler = parameterMapping.getTypeHandler();  
  86.                     if (typeHandler == null) {  
  87.                         throw new ExecutorException("There was no TypeHandler found for parameter " + propertyName + " of statement " + mappedStatement.getId());  
  88.                     }  
  89.                     typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType());  
  90.                 }  
  91.             }  
  92.         }  
  93.     }  
  94.   
  95.     /** 
  96.      * 查询总纪录数 
  97.      * @param sql             SQL语句 
  98.      * @param connection      数据库连接 
  99.      * @param mappedStatement mapped 
  100.      * @param parameterObject 参数 
  101.      * @param boundSql        boundSql 
  102.      * @return 总记录数 
  103.      * @throws SQLException sql查询错误 
  104.      */  
  105.     public static int getCount(final String sql, final Connection connection,  
  106.                                 final MappedStatement mappedStatement, final Object parameterObject,  
  107.                                 final BoundSql boundSql, Log log) throws SQLException {  
  108.         String dbName = Global.getConfig("jdbc.type");  
  109.         final String countSql;  
  110.         if("oracle".equals(dbName)){  
  111.             countSql = "select count(1) from (" + sql + ") tmp_count";  
  112.         }else{  
  113.             countSql = "select count(1) from (" + removeOrders(sql) + ") tmp_count";  
  114.         }  
  115.         Connection conn = connection;  
  116.         PreparedStatement ps = null;  
  117.         ResultSet rs = null;  
  118.         try {  
  119.             if (log.isDebugEnabled()) {  
  120.                 log.debug("COUNT SQL: " + StringUtils.replaceEach(countSql, new String[]{"\n","\t"}, new String[]{" "," "}));  
  121.             }  
  122.             if (conn == null){  
  123.                 conn = mappedStatement.getConfiguration().getEnvironment().getDataSource().getConnection();  
  124.             }  
  125.             ps = conn.prepareStatement(countSql);  
  126.             BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql,  
  127.                     boundSql.getParameterMappings(), parameterObject);  
  128.             //解决MyBatis 分页foreach 参数失效 start  
  129.             if (Reflections.getFieldValue(boundSql, "metaParameters") != null) {  
  130.                 MetaObject mo = (MetaObject) Reflections.getFieldValue(boundSql, "metaParameters");  
  131.                 Reflections.setFieldValue(countBS, "metaParameters", mo);  
  132.             }  
  133.             //解决MyBatis 分页foreach 参数失效 end   
  134.             SQLHelper.setParameters(ps, mappedStatement, countBS, parameterObject);  
  135.             rs = ps.executeQuery();  
  136.             int count = 0;  
  137.             if (rs.next()) {  
  138.                 count = rs.getInt(1);  
  139.             }  
  140.             return count;  
  141.         } finally {  
  142.             if (rs != null) {  
  143.                 rs.close();  
  144.             }  
  145.             if (ps != null) {  
  146.                 ps.close();  
  147.             }  
  148.             if (conn != null) {  
  149.                 conn.close();  
  150.             }  
  151.         }  
  152.     }  
  153.   
  154.   
  155.     /** 
  156.      * 根据数据库方言,生成特定的分页sql 
  157.      * @param sql     Mapper中的Sql语句 
  158.      * @param page    分页对象 
  159.      * @param dialect 方言类型 
  160.      * @return 分页SQL 
  161.      */  
  162.     public static String generatePageSql(String sql, Page<Object> page, Dialect dialect) {  
  163.         if (dialect.supportsLimit()) {  
  164.             return dialect.getLimitString(sql, page.getFirstResult(), page.getMaxResults());  
  165.         } else {  
  166.             return sql;  
  167.         }  
  168.     }  
  169.       
  170.     /**  
  171.      * 去除qlString的select子句。  
  172.      * @param hql  
  173.      * @return  
  174.      */    
  175.     @SuppressWarnings("unused")  
  176.     private static String removeSelect(String qlString){    
  177.         int beginPos = qlString.toLowerCase().indexOf("from");    
  178.         return qlString.substring(beginPos);    
  179.     }    
  180.         
  181.     /**  
  182.      * 去除hql的orderBy子句。  
  183.      * @param hql  
  184.      * @return  
  185.      */    
  186.     private static String removeOrders(String qlString) {    
  187.         Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);    
  188.         Matcher m = p.matcher(qlString);    
  189.         StringBuffer sb = new StringBuffer();    
  190.         while (m.find()) {    
  191.             m.appendReplacement(sb, "");    
  192.         }  
  193.         m.appendTail(sb);  
  194.         return sb.toString();    
  195.     }  
  196. }  

Dialect.java 接口
  1. package com.store.base.secondmodel.base.dialect;  
  2.   
  3. /** 
  4.  * 类似hibernate的Dialect,但只精简出分页部分 
  5.  * @author yiyong_wu 
  6.  * 
  7.  */  
  8. public interface Dialect {  
  9.   
  10.     /** 
  11.      * 数据库本身是否支持分页当前的分页查询方式 
  12.      * 如果数据库不支持的话,则不进行数据库分页 
  13.      * 
  14.      * @return true:支持当前的分页查询方式 
  15.      */  
  16.     public boolean supportsLimit();  
  17.   
  18.     /** 
  19.      * 将sql转换为分页SQL,分别调用分页sql 
  20.      * 
  21.      * @param sql    SQL语句 
  22.      * @param offset 开始条数 
  23.      * @param limit  每页显示多少纪录条数 
  24.      * @return 分页查询的sql 
  25.      */  
  26.     public String getLimitString(String sql, int offset, int limit);  
  27.   
  28. }  

MySQLDialect.java
  1. package com.store.base.secondmodel.base.dialect;  
  2.   
  3. /** 
  4.  * Mysql方言的实现 
  5.  * @author yiyong_wu 
  6.  * 
  7.  */  
  8. public class MySQLDialect implements Dialect {  
  9.   
  10.     @Override  
  11.     public boolean supportsLimit() {  
  12.         return true;  
  13.     }  
  14.   
  15.     @Override  
  16.     public String getLimitString(String sql, int offset, int limit) {  
  17.         return getLimitString(sql, offset, Integer.toString(offset),Integer.toString(limit));  
  18.     }  
  19.   
  20.     /** 
  21.      * 将sql变成分页sql语句,提供将offset及limit使用占位符号(placeholder)替换. 
  22.      * <pre> 
  23.      * 如mysql 
  24.      * dialect.getLimitString("select * from user", 12, ":offset",0,":limit") 将返回 
  25.      * select * from user limit :offset,:limit 
  26.      * </pre> 
  27.      * 
  28.      * @param sql               实际SQL语句 
  29.      * @param offset            分页开始纪录条数 
  30.      * @param offsetPlaceholder 分页开始纪录条数-占位符号 
  31.      * @param limitPlaceholder  分页纪录条数占位符号 
  32.      * @return 包含占位符的分页sql 
  33.      */  
  34.     public String getLimitString(String sql, int offset, String offsetPlaceholder, String limitPlaceholder) {  
  35.         StringBuilder stringBuilder = new StringBuilder(sql);  
  36.         stringBuilder.append(" limit ");  
  37.         if (offset > 0) {  
  38.             stringBuilder.append(offsetPlaceholder).append(",").append(limitPlaceholder);  
  39.         } else {  
  40.             stringBuilder.append(limitPlaceholder);  
  41.         }  
  42.         return stringBuilder.toString();  
  43.     }  
  44. }  

差不多到这边已经把整块分页怎么实现的给分享完了,但是我们还有更重要的任务,想要整个东西跑起来,肯定还要有基础工作要做,接下去我们分析整套Page对象以及它所依据的三层架构,还是用product作为实体进行分析。一整套三层架构讲下来,收获肯定又满满的。我们依次从entity->dao->service的顺序讲下来。

首先,针对我们的实体得继承两个抽象实体类BaseEntity 与 DataEntity

BaseEntity.java 主要放置Page成员变量,继承它后就可以每个实体都拥有这个成员变量

  1. package com.store.base.secondmodel.base;  
  2.   
  3. import java.io.Serializable;  
  4. import java.util.Map;  
  5.   
  6. import javax.xml.bind.annotation.XmlTransient;  
  7.   
  8. import org.apache.commons.lang3.StringUtils;  
  9. import org.apache.commons.lang3.builder.ReflectionToStringBuilder;  
  10.   
  11. import com.fasterxml.jackson.annotation.JsonIgnore;  
  12. import com.google.common.collect.Maps;  
  13. import com.store.base.model.StoreUser;  
  14.   
  15. /** 
  16.  * 最顶层的Entity 
  17.  * @author yiyong_wu 
  18.  * 
  19.  * @param <T> 
  20.  */  
  21. public abstract class BaseEntity<T> implements Serializable {  
  22.   
  23.     private static final long serialVersionUID = 1L;  
  24.       
  25.     /** 
  26.      * 删除标记(0:正常;1:删除;2:审核;) 
  27.      */  
  28.     public static final String DEL_FLAG_NORMAL = "0";  
  29.     public static final String DEL_FLAG_DELETE = "1";  
  30.     public static final String DEL_FLAG_AUDIT = "2";  
  31.   
  32.     /** 
  33.      * 实体编号(唯一标识) 
  34.      */  
  35.     protected String id;  
  36.       
  37.     /** 
  38.      * 当前用户 
  39.      */  
  40.     protected StoreUser currentUser;  
  41.       
  42.     /** 
  43.      * 当前实体分页对象 
  44.      */  
  45.     protected Page<T> page;  
  46.       
  47.     /** 
  48.      * 自定义SQL(SQL标识,SQL内容) 
  49.      */  
  50.     private Map<String, String> sqlMap;  
  51.       
  52.     public BaseEntity() {  
  53.           
  54.     }  
  55.       
  56.     public BaseEntity(String id) {  
  57.         this();  
  58.         this.id = id;  
  59.     }  
  60.   
  61.     public String getId() {  
  62.         return id;  
  63.     }  
  64.   
  65.     public void setId(String id) {  
  66.         this.id = id;  
  67.     }  
  68.       
  69.     /** 
  70.      * 这个主要针对shiro执行插入更新的时候会调用,获取当前的用户 
  71.      * @return 
  72.      */  
  73.     @JsonIgnore  
  74.     @XmlTransient  
  75.     public StoreUser getCurrentUser() {  
  76.         if(currentUser == null){  
  77. //          currentUser = UserUtils.getUser();  
  78.         }  
  79.         return currentUser;  
  80.     }  
  81.       
  82.     public void setCurrentUser(StoreUser currentUser) {  
  83.         this.currentUser = currentUser;  
  84.     }  
  85.   
  86.     @JsonIgnore  
  87.     @XmlTransient  
  88.     public Page<T> getPage() {  
  89.         if (page == null){  
  90.             page = new Page<>();  
  91.         }  
  92.         return page;  
  93.     }  
  94.       
  95.     public Page<T> setPage(Page<T> page) {  
  96.         this.page = page;  
  97.         return page;  
  98.     }  
  99.   
  100.     @JsonIgnore  
  101.     @XmlTransient  
  102.     public Map<String, String> getSqlMap() {  
  103.         if (sqlMap == null){  
  104.             sqlMap = Maps.newHashMap();  
  105.         }  
  106.         return sqlMap;  
  107.     }  
  108.   
  109.     public void setSqlMap(Map<String, String> sqlMap) {  
  110.         this.sqlMap = sqlMap;  
  111.     }  
  112.       
  113.     /** 
  114.      * 插入之前执行方法,子类实现 
  115.      */  
  116.     public abstract void preInsert();  
  117.       
  118.     /** 
  119.      * 更新之前执行方法,子类实现 
  120.      */  
  121.     public abstract void preUpdate();  
  122.       
  123.     /** 
  124.      * 是否是新记录(默认:false),调用setIsNewRecord()设置新记录,使用自定义ID。 
  125.      * 设置为true后强制执行插入语句,ID不会自动生成,需从手动传入。 
  126.      * @return 
  127.      */  
  128.     public boolean getIsNewRecord() {  
  129.         return StringUtils.isBlank(getId());  
  130.     }  
  131.   
  132.     /** 
  133.      * 全局变量对象 
  134.      */  
  135.     @JsonIgnore  
  136.     public Global getGlobal() {  
  137.         return Global.getInstance();  
  138.     }  
  139.       
  140.     /** 
  141.      * 获取数据库名称 
  142.      */  
  143.     @JsonIgnore  
  144.     public String getDbName(){  
  145.         return Global.getConfig("jdbc.type");  
  146.     }  
  147.       
  148.     @Override  
  149.     public String toString() {  
  150.         return ReflectionToStringBuilder.toString(this);  
  151.     }  
  152.       
  153. }  
DataEntity.java,主要存储更新删除时间,创建用户,更新用户,逻辑删除标志等

  1. package com.store.base.secondmodel.base;  
  2.   
  3. import java.util.Date;  
  4.   
  5. import org.hibernate.validator.constraints.Length;  
  6.   
  7. import com.fasterxml.jackson.annotation.JsonFormat;  
  8. import com.fasterxml.jackson.annotation.JsonIgnore;  
  9. import com.store.base.model.StoreUser;  
  10.   
  11. /** 
  12.  * 数据Entity 
  13.  * @author yiyong_wu 
  14.  * 
  15.  * @param <T> 
  16.  */  
  17. public abstract class DataEntity<T> extends BaseEntity<T> {  
  18.   
  19.     private static final long serialVersionUID = 1L;  
  20.   
  21.     protected StoreUser createBy; // 创建者  
  22.     protected Date createDate; // 创建日期  
  23.     protected StoreUser updateBy; // 更新者  
  24.     protected Date updateDate; // 更新日期  
  25.     protected String delFlag; // 删除标记(0:正常;1:删除;2:审核)  
  26.   
  27.     public DataEntity() {  
  28.         super();  
  29.         this.delFlag = DEL_FLAG_NORMAL;  
  30.     }  
  31.   
  32.     public DataEntity(String id) {  
  33.         super(id);  
  34.     }  
  35.   
  36.     /** 
  37.      * 插入之前执行方法,需要手动调用 
  38.      */  
  39.     @Override  
  40.     public void preInsert() {  
  41.         // 不限制ID为UUID,调用setIsNewRecord()使用自定义ID  
  42. //      User user = UserUtils.getUser();  
  43. //      if (StringUtils.isNotBlank(user.getId())) {  
  44. //          this.updateBy = user;  
  45. //          this.createBy = user;  
  46. //      }  
  47.         this.updateDate = new Date();  
  48.         this.createDate = this.updateDate;  
  49.     }  
  50.   
  51.     /** 
  52.      * 更新之前执行方法,需要手动调用 
  53.      */  
  54.     @Override  
  55.     public void preUpdate() {  
  56. //      User user = UserUtils.getUser();  
  57. //      if (StringUtils.isNotBlank(user.getId())) {  
  58. //          this.updateBy = user;  
  59. //      }  
  60.         this.updateDate = new Date();  
  61.     }  
  62.   
  63. //  @JsonIgnore  
  64.     public StoreUser getCreateBy() {  
  65.         return createBy;  
  66.     }  
  67.   
  68.     public void setCreateBy(StoreUser createBy) {  
  69.         this.createBy = createBy;  
  70.     }  
  71.   
  72.     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")  
  73.     public Date getCreateDate() {  
  74.         return createDate;  
  75.     }  
  76.   
  77.     public void setCreateDate(Date createDate) {  
  78.         this.createDate = createDate;  
  79.     }  
  80.   
  81. //  @JsonIgnore  
  82.     public StoreUser getUpdateBy() {  
  83.         return updateBy;  
  84.     }  
  85.   
  86.     public void setUpdateBy(StoreUser updateBy) {  
  87.         this.updateBy = updateBy;  
  88.     }  
  89.   
  90.     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")  
  91.     public Date getUpdateDate() {  
  92.         return updateDate;  
  93.     }  
  94.   
  95.     public void setUpdateDate(Date updateDate) {  
  96.         this.updateDate = updateDate;  
  97.     }  
  98.   
  99.     @JsonIgnore  
  100.     @Length(min = 1, max = 1)  
  101.     public String getDelFlag() {  
  102.         return delFlag;  
  103.     }  
  104.   
  105.     public void setDelFlag(String delFlag) {  
  106.         this.delFlag = delFlag;  
  107.     }  
  108.   
  109. }  
Product.java 产品类
  1. package com.store.base.secondmodel.pratice.model;  
  2.   
  3. import com.store.base.secondmodel.base.DataEntity;  
  4.   
  5. /** 
  6.  *产品基础类 
  7.  *2016年10月11日 
  8.  *yiyong_wu 
  9.  */  
  10. public class Product extends DataEntity<Product>{  
  11.       
  12.     private static final long serialVersionUID = 1L;  
  13.   
  14.     private String productName;  
  15.       
  16.     private float price;  
  17.       
  18.     private String productNo;  
  19.   
  20.     public String getProductName() {  
  21.         return productName;  
  22.     }  
  23.   
  24.     public void setProductName(String productName) {  
  25.         this.productName = productName;  
  26.     }  
  27.   
  28.     public float getPrice() {  
  29.         return price;  
  30.     }  
  31.   
  32.     public void setPrice(float price) {  
  33.         this.price = price;  
  34.     }  
  35.   
  36.     public String getProductNo() {  
  37.         return productNo;  
  38.     }  
  39.   
  40.     public void setProductNo(String productNo) {  
  41.         this.productNo = productNo;  
  42.     }  
  43.       
  44. }  
怎么样,是不是看到很复杂的一个实体继承连关系,不过这有什么,越复杂就会越完整。接下来我就看看dao层,同样是三层,准备好接受洗礼吧

BaseDao.java  预留接口

  1. package com.store.base.secondmodel.base;  
  2.   
  3. /** 
  4.  * 最顶层的DAO接口 
  5.  * @author yiyong_wu 
  6.  * 
  7.  */  
  8. public interface BaseDao {  
  9.   
  10. }  
CrudDao.java  针对增删改查的一个dao接口层
  1. package com.store.base.secondmodel.base;  
  2.   
  3. import java.util.List;  
  4.   
  5. /** 
  6.  * 定义增删改查的DAO接口 
  7.  * @author yiyong_wu 
  8.  * 
  9.  * @param <T> 
  10.  */  
  11. public interface CrudDao<T> extends BaseDao {  
  12.   
  13.     /** 
  14.      * 获取单条数据 
  15.      * @param id 
  16.      * @return 
  17.      */  
  18.     public T get(String id);  
  19.       
  20.     /** 
  21.      * 获取单条数据 
  22.      * @param entity 
  23.      * @return 
  24.      */  
  25.     public T get(T entity);  
  26.       
  27.     /** 
  28.      * 查询数据列表,如果需要分页,请设置分页对象,如:entity.setPage(new Page<T>()); 
  29.      * @param entity 
  30.      * @return 
  31.      */  
  32.     public List<T> findList(T entity);  
  33.       
  34.     /** 
  35.      * 查询所有数据列表 
  36.      * @param entity 
  37.      * @return 
  38.      */  
  39.     public List<T> findAllList(T entity);  
  40.       
  41.     /** 
  42.      * 查询所有数据列表 
  43.      * @see public List<T> findAllList(T entity) 
  44.      * @return 
  45.       
  46.     public List<T> findAllList(); 
  47.     */  
  48.       
  49.     /** 
  50.      * 插入数据 
  51.      * @param entity 
  52.      * @return 
  53.      */  
  54.     public int insert(T entity);  
  55.       
  56.     /** 
  57.      * 更新数据 
  58.      * @param entity 
  59.      * @return 
  60.      */  
  61.     public int update(T entity);  
  62.       
  63.     /** 
  64.      * 删除数据(一般为逻辑删除,更新del_flag字段为1) 
  65.      * @param id 
  66.      * @see public int delete(T entity) 
  67.      * @return 
  68.      */  
  69.     public int delete(String id);  
  70.       
  71.     /** 
  72.      * 删除数据(一般为逻辑删除,更新del_flag字段为1) 
  73.      * @param entity 
  74.      * @return 
  75.      */  
  76.     public int delete(T entity);  
  77. }  
ProductDao.java  mybatis对应的接口mapper,同时也是dao实现,这边需要自定一个注解@MyBatisRepository
  1. package com.store.base.secondmodel.pratice.dao;  
  2.   
  3. import com.store.base.secondmodel.base.CrudDao;  
  4. import com.store.base.secondmodel.base.MyBatisRepository;  
  5. import com.store.base.secondmodel.pratice.model.Product;  
  6.   
  7. /** 
  8.  *TODO 
  9.  *2016年10月11日 
  10.  *yiyong_wu 
  11.  */  
  12. @MyBatisRepository  
  13. public interface ProductDao extends CrudDao<Product>{  
  14.   
  15. }  

自定义注解MyBatisRepository.java,跟自定义注解相关,这里就不做过多的解读,网上资料一堆
  1. package com.store.base.secondmodel.base;  
  2.   
  3. import java.lang.annotation.Documented;  
  4. import java.lang.annotation.Retention;  
  5. import java.lang.annotation.Target;  
  6. import java.lang.annotation.RetentionPolicy;  
  7. import java.lang.annotation.ElementType;  
  8.   
  9. import org.springframework.stereotype.Component;  
  10.   
  11. /** 
  12.  * 标识MyBatis的DAO,方便{@link org.mybatis.spring.mapper.MapperScannerConfigurer}的扫描。 
  13.  *  
  14.  * 请注意要在spring的配置文件中配置扫描该注解类的配置 
  15.  *   
  16.  *<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> 
  17.  *<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> 
  18.  *<property name="basePackage" value="com.store.base.secondmodel" /> 
  19.  *<property name="annotationClass" value="com.store.base.secondmodel.base.MyBatisRepository" /> 
  20.  *</bean> 
  21.  * @author yiyong_wu 
  22.  *  
  23.  */  
  24. @Retention(RetentionPolicy.RUNTIME)  
  25. @Target(ElementType.TYPE)  
  26. @Documented  
  27. @Component  
  28. public @interface MyBatisRepository {  
  29.     String value() default "";  
  30. }  
注意:跟ProductDao.java联系比较大的是ProductMapper.xml文件,大家可以看到上面那个配置文件的namespace是指向这个dao的路径的。

接下来我们就进入最后的service分析了,一样还是三层继承

BaseService.java

  1. package com.store.base.secondmodel.base;  
  2.   
  3. import org.slf4j.Logger;  
  4. import org.slf4j.LoggerFactory;  
  5. import org.springframework.transaction.annotation.Transactional;  
  6.   
  7. /** 
  8.  * Service的最顶层父类 
  9.  * @author yiyong_wu 
  10.  * 
  11.  */  
  12. @Transactional(readOnly = true)  
  13. public abstract class BaseService {  
  14.   
  15.     //日志记录用的  
  16.     protected Logger logger = LoggerFactory.getLogger(getClass());  
  17. }  

CrudService.java 增删改查相关的业务接口实现
  1. package com.store.base.secondmodel.base;  
  2.   
  3. import java.util.List;  
  4.   
  5. import org.springframework.beans.factory.annotation.Autowired;  
  6. import org.springframework.transaction.annotation.Transactional;  
  7.   
  8. /** 
  9.  * 增删改查Service基类 
  10.  * @author yiyong_wu 
  11.  * 
  12.  * @param <D> 
  13.  * @param <T> 
  14.  */  
  15. public abstract class CrudService<D extends CrudDao<T>, T extends DataEntity<T>>  
  16.         extends BaseService {  
  17.   
  18.     /** 
  19.      * 持久层对象 
  20.      */  
  21.     @Autowired  
  22.     protected D dao;  
  23.       
  24.     /** 
  25.      * 获取单条数据 
  26.      * @param id 
  27.      * @return 
  28.      */  
  29.     public T get(String id) {  
  30.         return dao.get(id);  
  31.     }  
  32.       
  33.     /** 
  34.      * 获取单条数据 
  35.      * @param entity 
  36.      * @return 
  37.      */  
  38.     public T get(T entity) {  
  39.         return dao.get(entity);  
  40.     }  
  41.       
  42.     /** 
  43.      * 查询列表数据 
  44.      * @param entity 
  45.      * @return 
  46.      */  
  47.     public List<T> findList(T entity) {  
  48.         return dao.findList(entity);  
  49.     }  
  50.       
  51.     /** 
  52.      * 查询分页数据 
  53.      * @param page 分页对象 
  54.      * @param entity 
  55.      * @return 
  56.      */  
  57.     public Page<T> findPage(Page<T> page, T entity) {  
  58.         entity.setPage(page);  
  59.         page.setList(dao.findList(entity));  
  60.         return page;  
  61.     }  
  62.   
  63.     /** 
  64.      * 保存数据(插入或更新) 
  65.      * @param entity 
  66.      */  
  67.     @Transactional(readOnly = false)  
  68.     public void save(T entity) {  
  69.         if (entity.getIsNewRecord()){  
  70.             entity.preInsert();  
  71.             dao.insert(entity);  
  72.         }else{  
  73.             entity.preUpdate();  
  74.             dao.update(entity);  
  75.         }  
  76.     }  
  77.       
  78.     /** 
  79.      * 删除数据 
  80.      * @param entity 
  81.      */  
  82.     @Transactional(readOnly = false)  
  83.     public void delete(T entity) {  
  84.         dao.delete(entity);  
  85.     }  
  86. }  
ProductService.java,去继承CrudService接口,注意起注入dao和实体类型的一种模式
  1. package com.store.base.secondmodel.pratice.service;  
  2.   
  3. import org.springframework.stereotype.Service;  
  4. import org.springframework.transaction.annotation.Transactional;  
  5.   
  6. import com.store.base.secondmodel.base.CrudService;  
  7. import com.store.base.secondmodel.pratice.dao.ProductDao;  
  8. import com.store.base.secondmodel.pratice.model.Product;  
  9.   
  10. /** 
  11.  *TODO 
  12.  *2016年10月11日 
  13.  *yiyong_wu 
  14.  */  
  15. @Service  
  16. @Transactional(readOnly = true)  
  17. public class ProductService extends CrudService<ProductDao,Product>{  
  18.   
  19. }  

我想看到这里的同志已经很不耐烦了。但是如果你错过接下去的一段,基本上刚才看的就快等于白看了,革命的胜利就在后半段,因为整个分页功能围绕的就是一个Page对象,重磅内容终于要出来了,当你把Page对象填充到刚才那个BaseEntity上的时候,你会发现一切就完整起来了,废话不多说,Page对象如下
  1. package com.store.base.secondmodel.base;  
  2.   
  3. import java.io.Serializable;  
  4. import java.util.ArrayList;  
  5. import java.util.List;  
  6. import java.util.regex.Pattern;  
  7.   
  8. import javax.servlet.http.HttpServletRequest;  
  9. import javax.servlet.http.HttpServletResponse;  
  10.   
  11. import com.fasterxml.jackson.annotation.JsonIgnore;  
  12. import com.store.base.secondmodel.base.util.CookieUtils;  
  13. import com.store.base.secondmodel.base.util.StringUtils;  
  14.   
  15. /** 
  16.  * 分页类 
  17.  * @author yiyong_wu 
  18.  * 
  19.  * @param <T> 
  20.  */  
  21. public class Page<T> implements Serializable{  
  22.   
  23.     private static final long serialVersionUID = 1L;  
  24.       
  25.     private int pageNo = 1// 当前页码  
  26.     private int pageSize = Integer.parseInt(Global.getConfig("page.pageSize")); // 页面大小,设置为“-1”表示不进行分页(分页无效)  
  27.       
  28.     private long count;// 总记录数,设置为“-1”表示不查询总数  
  29.       
  30.     private int first;// 首页索引  
  31.     private int last;// 尾页索引  
  32.     private int prev;// 上一页索引  
  33.     private int next;// 下一页索引  
  34.       
  35.     private boolean firstPage;//是否是第一页  
  36.     private boolean lastPage;//是否是最后一页  
  37.   
  38.     private int length = 6;// 显示页面长度  
  39.     private int slider = 1;// 前后显示页面长度  
  40.       
  41.     private List<T> list = new ArrayList<>();  
  42.       
  43.     private String orderBy = ""// 标准查询有效, 实例: updatedate desc, name asc  
  44.   
  45.     private String funcName = "page"// 设置点击页码调用的js函数名称,默认为page,在一页有多个分页对象时使用。  
  46.       
  47.     private String funcParam = ""// 函数的附加参数,第三个参数值。  
  48.       
  49.     private String message = ""// 设置提示消息,显示在“共n条”之后  
  50.       
  51.     public Page() {  
  52.         this.pageSize = -1;  
  53.     }  
  54.       
  55.     /** 
  56.      * 构造方法 
  57.      * @param request 传递 repage 参数,来记住页码 
  58.      * @param response 用于设置 Cookie,记住页码 
  59.      */  
  60.     public Page(HttpServletRequest request, HttpServletResponse response){  
  61.         this(request, response, -2);  
  62.     }  
  63.   
  64.     /** 
  65.      * 构造方法 
  66.      * @param request 传递 repage 参数,来记住页码 
  67.      * @param response 用于设置 Cookie,记住页码 
  68.      * @param defaultPageSize 默认分页大小,如果传递 -1 则为不分页,返回所有数据 
  69.      */  
  70.     public Page(HttpServletRequest request, HttpServletResponse response, int defaultPageSize){  
  71.         // 设置页码参数(传递repage参数,来记住页码)  
  72.         String no = request.getParameter("pageNo");  
  73.         if (StringUtils.isNumeric(no)){  
  74.             CookieUtils.setCookie(response, "pageNo", no);  
  75.             this.setPageNo(Integer.parseInt(no));  
  76.         }else if (request.getParameter("repage")!=null){  
  77.             no = CookieUtils.getCookie(request, "pageNo");  
  78.             if (StringUtils.isNumeric(no)){  
  79.                 this.setPageNo(Integer.parseInt(no));  
  80.             }  
  81.         }  
  82.         // 设置页面大小参数(传递repage参数,来记住页码大小)  
  83.         String size = request.getParameter("pageSize");  
  84.         if (StringUtils.isNumeric(size)){  
  85.             CookieUtils.setCookie(response, "pageSize", size);  
  86.             this.setPageSize(Integer.parseInt(size));  
  87.         }else if (request.getParameter("repage")!=null){  
  88.             no = CookieUtils.getCookie(request, "pageSize");  
  89.             if (StringUtils.isNumeric(size)){  
  90.                 this.setPageSize(Integer.parseInt(size));  
  91.             }  
  92.         }else if (defaultPageSize != -2){  
  93.             this.pageSize = defaultPageSize;  
  94.         }  
  95.         // 设置排序参数  
  96.         String orderBy = request.getParameter("orderBy");  
  97.         if (StringUtils.isNotBlank(orderBy)){  
  98.             this.setOrderBy(orderBy);  
  99.         }  
  100.     }  
  101.       
  102.     /** 
  103.      * 构造方法 
  104.      * @param pageNo 当前页码 
  105.      * @param pageSize 分页大小 
  106.      */  
  107.     public Page(int pageNo, int pageSize) {  
  108.         this(pageNo, pageSize, 0);  
  109.     }  
  110.       
  111.     /** 
  112.      * 构造方法 
  113.      * @param pageNo 当前页码 
  114.      * @param pageSize 分页大小 
  115.      * @param count 数据条数 
  116.      */  
  117.     public Page(int pageNo, int pageSize, long count) {  
  118.         this(pageNo, pageSize, count, new ArrayList<T>());  
  119.     }  
  120.       
  121.     /** 
  122.      * 构造方法 
  123.      * @param pageNo 当前页码 
  124.      * @param pageSize 分页大小 
  125.      * @param count 数据条数 
  126.      * @param list 本页数据对象列表 
  127.      */  
  128.     public Page(int pageNo, int pageSize, long count, List<T> list) {  
  129.         this.setCount(count);  
  130.         this.setPageNo(pageNo);  
  131.         this.pageSize = pageSize;  
  132.         this.list = list;  
  133.     }  
  134.       
  135.     /** 
  136.      * 初始化参数 
  137.      */  
  138.     public void initialize(){  
  139.                   
  140.         //1  
  141.         this.first = 1;  
  142.           
  143.         this.last = (int)(count / (this.pageSize < 1 ? 20 : this.pageSize) + first - 1);  
  144.           
  145.         if (this.count % this.pageSize != 0 || this.last == 0) {  
  146.             this.last++;  
  147.         }  
  148.   
  149.         if (this.last < this.first) {  
  150.             this.last = this.first;  
  151.         }  
  152.           
  153.         if (this.pageNo <= 1) {  
  154.             this.pageNo = this.first;  
  155.             this.firstPage=true;  
  156.         }  
  157.   
  158.         if (this.pageNo >= this.last) {  
  159.             this.pageNo = this.last;  
  160.             this.lastPage=true;  
  161.         }  
  162.   
  163.         if (this.pageNo < this.last - 1) {  
  164.             this.next = this.pageNo + 1;  
  165.         } else {  
  166.             this.next = this.last;  
  167.         }  
  168.   
  169.         if (this.pageNo > 1) {  
  170.             this.prev = this.pageNo - 1;  
  171.         } else {  
  172.             this.prev = this.first;  
  173.         }  
  174.           
  175.         //2  
  176.         if (this.pageNo < this.first) {// 如果当前页小于首页  
  177.             this.pageNo = this.first;  
  178.         }  
  179.   
  180.         if (this.pageNo > this.last) {// 如果当前页大于尾页  
  181.             this.pageNo = this.last;  
  182.         }  
  183.           
  184.     }  
  185.       
  186.     /** 
  187.      * 默认输出当前分页标签  
  188.      * <div class="page">${page}</div> 
  189.      */  
  190.     @Override  
  191.     public String toString() {  
  192.   
  193.         StringBuilder sb = new StringBuilder();  
  194.           
  195.         if (pageNo == first) {// 如果是首页  
  196.             sb.append("<li class=\"disabled\"><a href=\"javascript:\">« 上一页</a></li>\n");  
  197.         } else {  
  198.             sb.append("<li><a href=\"javascript:\" οnclick=\""+funcName+"("+prev+","+pageSize+",'"+funcParam+"');\">« 上一页</a></li>\n");  
  199.         }  
  200.   
  201.         int begin = pageNo - (length / 2);  
  202.   
  203.         if (begin < first) {  
  204.             begin = first;  
  205.         }  
  206.   
  207.         int end = begin + length - 1;  
  208.   
  209.         if (end >= last) {  
  210.             end = last;  
  211.             begin = end - length + 1;  
  212.             if (begin < first) {  
  213.                 begin = first;  
  214.             }  
  215.         }  
  216.   
  217.         if (begin > first) {  
  218.             int i = 0;  
  219.             for (i = first; i < first + slider && i < begin; i++) {  
  220.                 sb.append("<li><a href=\"javascript:\" οnclick=\""+funcName+"("+i+","+pageSize+",'"+funcParam+"');\">"  
  221.                         + (i + 1 - first) + "</a></li>\n");  
  222.             }  
  223.             if (i < begin) {  
  224.                 sb.append("<li class=\"disabled\"><a href=\"javascript:\">...</a></li>\n");  
  225.             }  
  226.         }  
  227.   
  228.         for (int i = begin; i <= end; i++) {  
  229.             if (i == pageNo) {  
  230.                 sb.append("<li class=\"active\"><a href=\"javascript:\">" + (i + 1 - first)  
  231.                         + "</a></li>\n");  
  232.             } else {  
  233.                 sb.append("<li><a href=\"javascript:\" οnclick=\""+funcName+"("+i+","+pageSize+",'"+funcParam+"');\">"  
  234.                         + (i + 1 - first) + "</a></li>\n");  
  235.             }  
  236.         }  
  237.   
  238.         if (last - end > slider) {  
  239.             sb.append("<li class=\"disabled\"><a href=\"javascript:\">...</a></li>\n");  
  240.             end = last - slider;  
  241.         }  
  242.   
  243.         for (int i = end + 1; i <= last; i++) {  
  244.             sb.append("<li><a href=\"javascript:\" οnclick=\""+funcName+"("+i+","+pageSize+",'"+funcParam+"');\">"  
  245.                     + (i + 1 - first) + "</a></li>\n");  
  246.         }  
  247.   
  248.         if (pageNo == last) {  
  249.             sb.append("<li class=\"disabled\"><a href=\"javascript:\">下一页 »</a></li>\n");  
  250.         } else {  
  251.             sb.append("<li><a href=\"javascript:\" οnclick=\""+funcName+"("+next+","+pageSize+",'"+funcParam+"');\">"  
  252.                     + "下一页 »</a></li>\n");  
  253.         }  
  254.   
  255.         return sb.toString();  
  256.     }  
  257.       
  258.     /** 
  259.      * 获取分页HTML代码 
  260.      * @return 
  261.      */  
  262.     public String getHtml(){  
  263.         return toString();  
  264.     }  
  265.       
  266.     /** 
  267.      * 获取设置总数 
  268.      * @return 
  269.      */  
  270.     public long getCount() {  
  271.         return count;  
  272.     }  
  273.   
  274.     /** 
  275.      * 设置数据总数 
  276.      * @param count 
  277.      */  
  278.     public void setCount(long count) {  
  279.         this.count = count;  
  280.         if (pageSize >= count){  
  281.             pageNo = 1;  
  282.         }  
  283.     }  
  284.       
  285.     /** 
  286.      * 获取当前页码 
  287.      * @return 
  288.      */  
  289.     public int getPageNo() {  
  290.         return pageNo;  
  291.     }  
  292.       
  293.     /** 
  294.      * 设置当前页码 
  295.      * @param pageNo 
  296.      */  
  297.     public void setPageNo(int pageNo) {  
  298.         this.pageNo = pageNo;  
  299.     }  
  300.       
  301.     /** 
  302.      * 获取页面大小 
  303.      * @return 
  304.      */  
  305.     public int getPageSize() {  
  306.         return pageSize;  
  307.     }  
  308.   
  309.     /** 
  310.      * 设置页面大小(最大500)// > 500 ? 500 : pageSize; 
  311.      * @param pageSize 
  312.      */  
  313.     public void setPageSize(int pageSize) {  
  314.         this.pageSize = pageSize <= 0 ? 10 : pageSize;  
  315.     }  
  316.   
  317.     /** 
  318.      * 首页索引 
  319.      * @return 
  320.      */  
  321.     @JsonIgnore  
  322.     public int getFirst() {  
  323.         return first;  
  324.     }  
  325.   
  326.     /** 
  327.      * 尾页索引 
  328.      * @return 
  329.      */  
  330.     @JsonIgnore  
  331.     public int getLast() {  
  332.         return last;  
  333.     }  
  334.       
  335.     /** 
  336.      * 获取页面总数 
  337.      * @return getLast(); 
  338.      */  
  339.     @JsonIgnore  
  340.     public int getTotalPage() {  
  341.         return getLast();  
  342.     }  
  343.   
  344.     /** 
  345.      * 是否为第一页 
  346.      * @return 
  347.      */  
  348.     @JsonIgnore  
  349.     public boolean isFirstPage() {  
  350.         return firstPage;  
  351.     }  
  352.   
  353.     /** 
  354.      * 是否为最后一页 
  355.      * @return 
  356.      */  
  357.     @JsonIgnore  
  358.     public boolean isLastPage() {  
  359.         return lastPage;  
  360.     }  
  361.       
  362.     /** 
  363.      * 上一页索引值 
  364.      * @return 
  365.      */  
  366.     @JsonIgnore  
  367.     public int getPrev() {  
  368.         if (isFirstPage()) {  
  369.             return pageNo;  
  370.         } else {  
  371.             return pageNo - 1;  
  372.         }  
  373.     }  
  374.   
  375.     /** 
  376.      * 下一页索引值 
  377.      * @return 
  378.      */  
  379.     @JsonIgnore  
  380.     public int getNext() {  
  381.         if (isLastPage()) {  
  382.             return pageNo;  
  383.         } else {  
  384.             return pageNo + 1;  
  385.         }  
  386.     }  
  387.       
  388.     /** 
  389.      * 获取本页数据对象列表 
  390.      * @return List<T> 
  391.      */  
  392.     public List<T> getList() {  
  393.         return list;  
  394.     }  
  395.   
  396.     /** 
  397.      * 设置本页数据对象列表 
  398.      * @param list 
  399.      */  
  400.     public Page<T> setList(List<T> list) {  
  401.         this.list = list;  
  402.         initialize();  
  403.         return this;  
  404.     }  
  405.   
  406.     /** 
  407.      * 获取查询排序字符串 
  408.      * @return 
  409.      */  
  410.     @JsonIgnore  
  411.     public String getOrderBy() {  
  412.         // SQL过滤,防止注入   
  413.         String reg = "(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|"  
  414.                     + "(\\b(select|update|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)";  
  415.         Pattern sqlPattern = Pattern.compile(reg, Pattern.CASE_INSENSITIVE);  
  416.         if (sqlPattern.matcher(orderBy).find()) {  
  417.             return "";  
  418.         }  
  419.         return orderBy;  
  420.     }  
  421.   
  422.     /** 
  423.      * 设置查询排序,标准查询有效, 实例: updatedate desc, name asc 
  424.      */  
  425.     public void setOrderBy(String orderBy) {  
  426.         this.orderBy = orderBy;  
  427.     }  
  428.   
  429.     /** 
  430.      * 获取点击页码调用的js函数名称 
  431.      * function ${page.funcName}(pageNo){location="${ctx}/list-${category.id}${urlSuffix}?pageNo="+i;} 
  432.      * @return 
  433.      */  
  434.     @JsonIgnore  
  435.     public String getFuncName() {  
  436.         return funcName;  
  437.     }  
  438.   
  439.     /** 
  440.      * 设置点击页码调用的js函数名称,默认为page,在一页有多个分页对象时使用。 
  441.      * @param funcName 默认为page 
  442.      */  
  443.     public void setFuncName(String funcName) {  
  444.         this.funcName = funcName;  
  445.     }  
  446.   
  447.     /** 
  448.      * 获取分页函数的附加参数 
  449.      * @return 
  450.      */  
  451.     @JsonIgnore  
  452.     public String getFuncParam() {  
  453.         return funcParam;  
  454.     }  
  455.   
  456.     /** 
  457.      * 设置分页函数的附加参数 
  458.      * @return 
  459.      */  
  460.     public void setFuncParam(String funcParam) {  
  461.         this.funcParam = funcParam;  
  462.     }  
  463.   
  464.     /** 
  465.      * 设置提示消息,显示在“共n条”之后 
  466.      * @param message 
  467.      */  
  468.     public void setMessage(String message) {  
  469.         this.message = message;  
  470.     }  
  471.       
  472.     /** 
  473.      * 分页是否有效 
  474.      * @return this.pageSize==-1 
  475.      */  
  476.     @JsonIgnore  
  477.     public boolean isDisabled() {  
  478.         return this.pageSize==-1;  
  479.     }  
  480.       
  481.     /** 
  482.      * 是否进行总数统计 
  483.      * @return this.count==-1 
  484.      */  
  485.     @JsonIgnore  
  486.     public boolean isNotCount() {  
  487.         return this.count==-1;  
  488.     }  
  489.       
  490.     /** 
  491.      * 获取 Hibernate FirstResult 
  492.      */  
  493.     public int getFirstResult(){  
  494.         int firstResult = (getPageNo() - 1) * getPageSize();  
  495.         if (firstResult >= getCount()) {  
  496.             firstResult = 0;  
  497.         }  
  498.         return firstResult;  
  499.     }  
  500.     /** 
  501.      * 获取 Hibernate MaxResults 
  502.      */  
  503.     public int getMaxResults(){  
  504.         return getPageSize();  
  505.     }  
  506.   
  507. }  
看完这个Page对象应该稍微有点感觉了吧,然后我在胡乱贴一些相关用到的工具类吧,工具类的话我只稍微提一下,具体大家可以弄到自己的代码上好好解读。

PropertiesLoader.java  用来获取resource文件夹下的常量配置文件

  1. package com.store.base.secondmodel.base.util;  
  2.   
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5. import java.util.NoSuchElementException;  
  6. import java.util.Properties;  
  7.   
  8. import org.apache.commons.io.IOUtils;  
  9. import org.slf4j.Logger;  
  10. import org.slf4j.LoggerFactory;  
  11. import org.springframework.core.io.DefaultResourceLoader;  
  12. import org.springframework.core.io.Resource;  
  13. import org.springframework.core.io.ResourceLoader;  
  14.   
  15. /** 
  16.  * Properties文件载入工具类. 可载入多个properties文件,  
  17.  * 相同的属性在最后载入的文件中的值将会覆盖之前的值,但以System的Property优先. 
  18.  * @author yiyong_wu 
  19.  * 
  20.  */  
  21. public class PropertiesLoader {  
  22.   
  23.     private static Logger logger = LoggerFactory.getLogger(PropertiesLoader.class);  
  24.   
  25.     private static ResourceLoader resourceLoader = new DefaultResourceLoader();  
  26.   
  27.     private final Properties properties;  
  28.   
  29.     public PropertiesLoader(String... resourcesPaths) {  
  30.         properties = loadProperties(resourcesPaths);  
  31.     }  
  32.   
  33.     public Properties getProperties() {  
  34.         return properties;  
  35.     }  
  36.   
  37.     /** 
  38.      * 取出Property,但以System的Property优先,取不到返回空字符串. 
  39.      */  
  40.     private String getValue(String key) {  
  41.         String systemProperty = System.getProperty(key);  
  42.         if (systemProperty != null) {  
  43.             return systemProperty;  
  44.         }  
  45.         if (properties.containsKey(key)) {  
  46.             return properties.getProperty(key);  
  47.         }  
  48.         return "";  
  49.     }  
  50.   
  51.     /** 
  52.      * 取出String类型的Property,但以System的Property优先,如果都为Null则抛出异常. 
  53.      */  
  54.     public String getProperty(String key) {  
  55.         String value = getValue(key);  
  56.         if (value == null) {  
  57.             throw new NoSuchElementException();  
  58.         }  
  59.         return value;  
  60.     }  
  61.   
  62.     /** 
  63.      * 取出String类型的Property,但以System的Property优先.如果都为Null则返回Default值. 
  64.      */  
  65.     public String getProperty(String key, String defaultValue) {  
  66.         String value = getValue(key);  
  67.         return value != null ? value : defaultValue;  
  68.     }  
  69.   
  70.     /** 
  71.      * 取出Integer类型的Property,但以System的Property优先.如果都为Null或内容错误则抛出异常. 
  72.      */  
  73.     public Integer getInteger(String key) {  
  74.         String value = getValue(key);  
  75.         if (value == null) {  
  76.             throw new NoSuchElementException();  
  77.         }  
  78.         return Integer.valueOf(value);  
  79.     }  
  80.   
  81.     /** 
  82.      * 取出Integer类型的Property,但以System的Property优先.如果都为Null则返回Default值,如果内容错误则抛出异常 
  83.      */  
  84.     public Integer getInteger(String key, Integer defaultValue) {  
  85.         String value = getValue(key);  
  86.         return value != null ? Integer.valueOf(value) : defaultValue;  
  87.     }  
  88.   
  89.     /** 
  90.      * 取出Double类型的Property,但以System的Property优先.如果都为Null或内容错误则抛出异常. 
  91.      */  
  92.     public Double getDouble(String key) {  
  93.         String value = getValue(key);  
  94.         if (value == null) {  
  95.             throw new NoSuchElementException();  
  96.         }  
  97.         return Double.valueOf(value);  
  98.     }  
  99.   
  100.     /** 
  101.      * 取出Double类型的Property,但以System的Property优先.如果都为Null则返回Default值,如果内容错误则抛出异常 
  102.      */  
  103.     public Double getDouble(String key, Integer defaultValue) {  
  104.         String value = getValue(key);  
  105.         return value != null ? Double.valueOf(value) : defaultValue.doubleValue();  
  106.     }  
  107.   
  108.     /** 
  109.      * 取出Boolean类型的Property,但以System的Property优先.如果都为Null抛出异常,如果内容不是true/false则返回false. 
  110.      */  
  111.     public Boolean getBoolean(String key) {  
  112.         String value = getValue(key);  
  113.         if (value == null) {  
  114.             throw new NoSuchElementException();  
  115.         }  
  116.         return Boolean.valueOf(value);  
  117.     }  
  118.   
  119.     /** 
  120.      * 取出Boolean类型的Property,但以System的Property优先.如果都为Null则返回Default值,如果内容不为true/false则返回false. 
  121.      */  
  122.     public Boolean getBoolean(String key, boolean defaultValue) {  
  123.         String value = getValue(key);  
  124.         return value != null ? Boolean.valueOf(value) : defaultValue;  
  125.     }  
  126.   
  127.     /** 
  128.      * 载入多个文件, 文件路径使用Spring Resource格式. 
  129.      */  
  130.     private Properties loadProperties(String... resourcesPaths) {  
  131.         Properties props = new Properties();  
  132.   
  133.         for (String location : resourcesPaths) {  
  134.   
  135.             InputStream is = null;  
  136.             try {  
  137.                 Resource resource = resourceLoader.getResource(location);  
  138.                 is = resource.getInputStream();  
  139.                 props.load(is);  
  140.             } catch (IOException ex) {  
  141.                 logger.error("Could not load properties from path:" + location , ex);  
  142.             } finally {  
  143.                 IOUtils.closeQuietly(is);  
  144.             }  
  145.         }  
  146.         return props;  
  147.     }  
  148. }  

Global.java 用来获取全局的一些常量,可以是从配置文件中读取的常量,也可以是定义成final static的常量,获取配置文件的话是调用上面那个类进行获取的。

  1. package com.store.base.secondmodel.base;  
  2.   
  3. import java.io.File;  
  4. import java.io.IOException;  
  5. import java.util.Map;  
  6.   
  7. import org.slf4j.Logger;  
  8. import org.slf4j.LoggerFactory;  
  9. import org.springframework.core.io.DefaultResourceLoader;  
  10.   
  11. import com.google.common.collect.Maps;  
  12. import com.store.base.secondmodel.base.util.PropertiesLoader;  
  13. import com.store.base.secondmodel.base.util.StringUtils;  
  14.   
  15. /** 
  16.  * 全局配置类 
  17.  * @author yiyong_wu 
  18.  * 
  19.  */  
  20. public class Global {  
  21.   
  22.     private static final Logger logger = LoggerFactory.getLogger(Global.class);  
  23.       
  24.     /** 
  25.      * 当前对象实例 
  26.      */  
  27.     private static Global global = new Global();  
  28.       
  29.     /** 
  30.      * 保存全局属性值 
  31.      */  
  32.     private static Map<String, String> map = Maps.newHashMap();  
  33.       
  34.     /** 
  35.      * 属性文件加载对象 
  36.      */  
  37.     private static PropertiesLoader loader = new PropertiesLoader("application.properties");  
  38.   
  39.     /** 
  40.      * 显示/隐藏 
  41.     public static final String SHOW = "1"; 
  42.     public static final String HIDE = "0"; 
  43.  
  44.     /** 
  45.      * 是/否 
  46.      */  
  47.     public static final String YES = "1";  
  48.     public static final String NO = "0";  
  49.       
  50.     /** 
  51.      * 状态 上/下 app专用 
  52.      */  
  53.     public static final String UPSHVELF = "1";  
  54.     public static final String DOWNSHVELF = "2";  
  55.       
  56.     public static final String SEPARATOR = "/";  
  57.       
  58.     /** 
  59.      * 对/错 
  60.      */  
  61.     public static final String TRUE = "true";  
  62.     public static final String FALSE = "false";  
  63.       
  64.     /** 
  65.      * 上传文件基础虚拟路径 
  66.      */  
  67.     public static final String USERFILES_BASE_URL = "/userfiles/";  
  68.       
  69.     /** 
  70.      * 针对富文本编辑器,结尾会产生的空div 
  71.      */  
  72.     public static final String ENDS = "<p><br></p>";  
  73.       
  74.       
  75.     /** 
  76.      * 默认空的私有构造函数 
  77.      */  
  78.     public Global() {  
  79.         //do nothing in this method,just empty  
  80.     }  
  81.       
  82.     /** 
  83.      * 获取当前对象实例 
  84.      */  
  85.     public static Global getInstance() {  
  86.         return global;  
  87.     }  
  88.       
  89.     /** 
  90.      * 获取配置 
  91.      */  
  92.     public static String getConfig(String key) {  
  93.         String value = map.get(key);  
  94.         if (value == null){  
  95.             value = loader.getProperty(key);  
  96.             map.put(key, value != null ? value : StringUtils.EMPTY);  
  97.         }  
  98.         return value;  
  99.     }  
  100.       
  101.     /** 
  102.      * 获取URL后缀 
  103.      */  
  104.     public static String getUrlSuffix() {  
  105.         return getConfig("urlSuffix");  
  106.     }  
  107.       
  108.     /** 
  109.      * 页面获取常量 
  110.      * @see ${fns:getConst('YES')} 
  111.      */  
  112.     public static Object getConst(String field) {  
  113.         try {  
  114.             return Global.class.getField(field).get(null);  
  115.         } catch (Exception e) {  
  116.             logger.error("获取常量出错", e);  
  117.         }  
  118.         return null;  
  119.     }  
  120.       
  121.     /** 
  122.      * 获取工程路径 
  123.      * @return 
  124.      */  
  125.     public static String getProjectPath(){  
  126.         // 如果配置了工程路径,则直接返回,否则自动获取。  
  127.         String projectPath = Global.getConfig("projectPath");  
  128.         if (StringUtils.isNotBlank(projectPath)){  
  129.             return projectPath;  
  130.         }  
  131.         try {  
  132.             File file = new DefaultResourceLoader().getResource("").getFile();  
  133.             if (file != null){  
  134.                 while(true){  
  135.                     File f = new File(file.getPath() + File.separator + "src" + File.separator + "main");  
  136.                     if (f == null || f.exists()){  
  137.                         break;  
  138.                     }  
  139.                     if (file.getParentFile() != null){  
  140.                         file = file.getParentFile();  
  141.                     }else{  
  142.                         break;  
  143.                     }  
  144.                 }  
  145.                 projectPath = file.toString();  
  146.             }  
  147.         } catch (IOException e) {  
  148.             logger.error("加载配置文件失败", e);  
  149.         }  
  150.         return projectPath;  
  151.     }  
  152.       
  153. }  

CookieUtil.java  从名称就知道是针对获取和存储cookie的一个工具类
  1. package com.store.base.secondmodel.base.util;  
  2.   
  3. import java.io.UnsupportedEncodingException;  
  4. import java.net.URLDecoder;  
  5. import java.net.URLEncoder;  
  6.   
  7. import javax.servlet.http.Cookie;  
  8. import javax.servlet.http.HttpServletRequest;  
  9. import javax.servlet.http.HttpServletResponse;  
  10.   
  11. import org.slf4j.Logger;  
  12. import org.slf4j.LoggerFactory;  
  13.   
  14. /** 
  15.  * Cookie工具类 
  16.  * @author yiyong_wu 
  17.  * 
  18.  */  
  19. public class CookieUtils {  
  20.   
  21.     private static final Logger logger = LoggerFactory.getLogger(CookieUtils.class);  
  22.       
  23.     /** 
  24.      * 私有构造函数 
  25.      */  
  26.     private CookieUtils() {  
  27.           
  28.     }  
  29.       
  30.     /** 
  31.      * 设置 Cookie(生成时间为1年) 
  32.      * @param name 名称 
  33.      * @param value 值 
  34.      */  
  35.     public static void setCookie(HttpServletResponse response, String name, String value) {  
  36.         setCookie(response, name, value, 60*60*24*365);  
  37.     }  
  38.       
  39.     /** 
  40.      * 设置 Cookie 
  41.      * @param name 名称 
  42.      * @param value 值 
  43.      * @param maxAge 生存时间(单位秒) 
  44.      * @param uri 路径 
  45.      */  
  46.     public static void setCookie(HttpServletResponse response, String name, String value, String path) {  
  47.         setCookie(response, name, value, path, 60*60*24*365);  
  48.     }  
  49.       
  50.     /** 
  51.      * 设置 Cookie 
  52.      * @param name 名称 
  53.      * @param value 值 
  54.      * @param maxAge 生存时间(单位秒) 
  55.      * @param uri 路径 
  56.      */  
  57.     public static void setCookie(HttpServletResponse response, String name, String value, int maxAge) {  
  58.         setCookie(response, name, value, "/", maxAge);  
  59.     }  
  60.       
  61.     /** 
  62.      * 设置 Cookie 
  63.      * @param name 名称 
  64.      * @param value 值 
  65.      * @param maxAge 生存时间(单位秒) 
  66.      * @param uri 路径 
  67.      */  
  68.     public static void setCookie(HttpServletResponse response, String name, String value, String path, int maxAge) {  
  69.         Cookie cookie = new Cookie(name, null);  
  70.         cookie.setPath(path);  
  71.         cookie.setMaxAge(maxAge);  
  72.         try {  
  73.             cookie.setValue(URLEncoder.encode(value, "utf-8"));  
  74.         } catch (UnsupportedEncodingException e) {  
  75.             logger.error("不支持的编码", e);  
  76.         }  
  77.         response.addCookie(cookie);  
  78.     }  
  79.       
  80.     /** 
  81.      * 获得指定Cookie的值 
  82.      * @param name 名称 
  83.      * @return 值 
  84.      */  
  85.     public static String getCookie(HttpServletRequest request, String name) {  
  86.         return getCookie(request, null, name, false);  
  87.     }  
  88.     /** 
  89.      * 获得指定Cookie的值,并删除。 
  90.      * @param name 名称 
  91.      * @return 值 
  92.      */  
  93.     public static String getCookie(HttpServletRequest request, HttpServletResponse response, String name) {  
  94.         return getCookie(request, response, name, true);  
  95.     }  
  96.     /** 
  97.      * 获得指定Cookie的值 
  98.      * @param request 请求对象 
  99.      * @param response 响应对象 
  100.      * @param name 名字 
  101.      * @param isRemove 是否移除 
  102.      * @return 值 
  103.      */  
  104.     public static String getCookie(HttpServletRequest request, HttpServletResponse response, String name, boolean isRemove) {  
  105.         String value = null;  
  106.         Cookie[] cookies = request.getCookies();  
  107.         if(cookies == null) {  
  108.             return value;  
  109.         }  
  110.           
  111.         for (Cookie cookie : cookies) {  
  112.             if (cookie.getName().equals(name)) {  
  113.                 try {  
  114.                     value = URLDecoder.decode(cookie.getValue(), "utf-8");  
  115.                 } catch (UnsupportedEncodingException e) {  
  116.                     logger.error("不支持的编码", e);  
  117.                 }  
  118.                 if (isRemove) {  
  119.                     cookie.setMaxAge(0);  
  120.                     response.addCookie(cookie);  
  121.                 }  
  122.             }  
  123.         }  
  124.           
  125.         return value;  
  126.     }  
  127. }  

SpringContextHolder.java 主要是用来在java代码中获取当前的ApplicationContext,需要在spring配置文件中配置这个bean并且懒加载设置成false;
  1. package com.store.base.secondmodel.base.util;  
  2.   
  3. import org.apache.commons.lang3.Validate;  
  4. import org.slf4j.Logger;  
  5. import org.slf4j.LoggerFactory;  
  6. import org.springframework.beans.factory.DisposableBean;  
  7. import org.springframework.context.ApplicationContext;  
  8. import org.springframework.context.ApplicationContextAware;  
  9. import org.springframework.context.annotation.Lazy;  
  10. import org.springframework.stereotype.Service;  
  11.   
  12. @Service  
  13. @Lazy(false)  
  14. public class SpringContextHolder implements ApplicationContextAware,  
  15.         DisposableBean {  
  16.   
  17.     private static Logger logger = LoggerFactory.getLogger(SpringContextHolder.class);  
  18.       
  19.     private static ApplicationContext applicationContext = null;  
  20.       
  21.     /** 
  22.      * 取得存储在静态变量中的ApplicationContext. 
  23.      */  
  24.     public static ApplicationContext getApplicationContext() {  
  25.         assertContextInjected();  
  26.         return applicationContext;  
  27.     }  
  28.   
  29.     /** 
  30.      * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. 
  31.      */  
  32.     @SuppressWarnings("unchecked")  
  33.     public static <T> T getBean(String name) {  
  34.         assertContextInjected();  
  35.         return (T) applicationContext.getBean(name);  
  36.     }  
  37.   
  38.     /** 
  39.      * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. 
  40.      */  
  41.     public static <T> T getBean(Class<T> requiredType) {  
  42.         assertContextInjected();  
  43.         return applicationContext.getBean(requiredType);  
  44.     }  
  45.       
  46.     @Override  
  47.     public void destroy() throws Exception {  
  48.         SpringContextHolder.clearHolder();  
  49.     }  
  50.   
  51.     /** 
  52.      * 实现ApplicationContextAware接口, 注入Context到静态变量中. 
  53.      */  
  54.     @Override  
  55.     public void setApplicationContext(ApplicationContext applicationContext) {  
  56.         logger.debug("注入ApplicationContext到SpringContextHolder:{}", applicationContext);  
  57.   
  58.         SpringContextHolder.applicationContext = applicationContext;  
  59.           
  60.         if (SpringContextHolder.applicationContext != null) {  
  61.             logger.info("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext);  
  62.         }  
  63.     }  
  64.   
  65.     /** 
  66.      * 清除SpringContextHolder中的ApplicationContext为Null. 
  67.      */  
  68.     public static void clearHolder() {  
  69.         if (logger.isDebugEnabled()){  
  70.             logger.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);  
  71.         }  
  72.         applicationContext = null;  
  73.     }  
  74.       
  75.     /** 
  76.      * 检查ApplicationContext不为空. 
  77.      */  
  78.     private static void assertContextInjected() {  
  79.         Validate.validState(applicationContext != null"applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");  
  80.     }  
  81.       
  82. }  
StringUtils.java字符串相关的一个工具类
  1. package com.store.base.secondmodel.base.util;  
  2.   
  3. import java.io.UnsupportedEncodingException;  
  4. import java.util.Locale;  
  5. import java.util.regex.Matcher;  
  6. import java.util.regex.Pattern;  
  7.   
  8. import javax.servlet.http.HttpServletRequest;  
  9.   
  10. import org.apache.commons.lang3.StringEscapeUtils;  
  11. import org.slf4j.Logger;  
  12. import org.slf4j.LoggerFactory;  
  13. import org.springframework.web.context.request.RequestContextHolder;  
  14. import org.springframework.web.context.request.ServletRequestAttributes;  
  15. import org.springframework.web.servlet.LocaleResolver;  
  16.   
  17. import com.store.base.util.Encodes;  
  18.   
  19.   
  20. /** 
  21.  * 字符串帮助类 
  22.  * @author yiyong_wu 
  23.  * 
  24.  */  
  25. public class StringUtils extends org.apache.commons.lang3.StringUtils {  
  26.   
  27.     private static final char SEPARATOR = '_';  
  28.     private static final String CHARSET_NAME = "UTF-8";  
  29.       
  30.     private static final Logger logger = LoggerFactory.getLogger(StringUtils.class);  
  31.       
  32.     /** 
  33.      * 转换为字节数组 
  34.      * @param str 
  35.      * @return 
  36.      */  
  37.     public static byte[] getBytes(String str){  
  38.         if (str != null){  
  39.             try {  
  40.                 return str.getBytes(CHARSET_NAME);  
  41.             } catch (UnsupportedEncodingException e) {  
  42.                 logger.error("", e);  
  43.                 return new byte[0];  
  44.             }  
  45.         }else{  
  46.             return new byte[0];  
  47.         }  
  48.     }  
  49.       
  50.     /** 
  51.      * 转换为字节数组 
  52.      * @param str 
  53.      * @return 
  54.      */  
  55.     public static String toString(byte[] bytes){  
  56.         try {  
  57.             return new String(bytes, CHARSET_NAME);  
  58.         } catch (UnsupportedEncodingException e) {  
  59.             logger.error("", e);  
  60.             return EMPTY;  
  61.         }  
  62.     }  
  63.       
  64.     /** 
  65.      * 是否包含字符串 
  66.      * @param str 验证字符串 
  67.      * @param strs 字符串组 
  68.      * @return 包含返回true 
  69.      */  
  70.     public static boolean inString(String str, String... strs){  
  71.         if (str != null){  
  72.             for (String s : strs){  
  73.                 if (str.equals(trim(s))){  
  74.                     return true;  
  75.                 }  
  76.             }  
  77.         }  
  78.         return false;  
  79.     }  
  80.       
  81.     /** 
  82.      * 替换掉HTML标签方法 
  83.      */  
  84.     public static String replaceHtml(String html) {  
  85.         if (isBlank(html)){  
  86.             return "";  
  87.         }  
  88.         String regEx = "<.+?>";  
  89.         Pattern p = Pattern.compile(regEx);  
  90.         Matcher m = p.matcher(html);  
  91.         return m.replaceAll("");  
  92.     }  
  93.       
  94.     /** 
  95.      * 替换为手机识别的HTML,去掉样式及属性,保留回车。 
  96.      * @param html 
  97.      * @return 
  98.      */  
  99.     public static String replaceMobileHtml(String html){  
  100.         if (html == null){  
  101.             return "";  
  102.         }  
  103.         return html.replaceAll("<([a-z]+?)\\s+?.*?>""<$1>");  
  104.     }  
  105.       
  106.     /** 
  107.      * 替换为手机识别的HTML,去掉样式及属性,保留回车。 
  108.      * @param txt 
  109.      * @return 
  110.      */  
  111.     public static String toHtml(String txt){  
  112.         if (txt == null){  
  113.             return "";  
  114.         }  
  115.         return replace(replace(Encodes.escapeHtml(txt), "\n""<br/>"), "\t""    ");  
  116.     }  
  117.   
  118.     /** 
  119.      * 缩略字符串(不区分中英文字符) 
  120.      * @param str 目标字符串 
  121.      * @param length 截取长度 
  122.      * @return 
  123.      */  
  124.     public static String abbr(String str, int length) {  
  125.         if (str == null) {  
  126.             return "";  
  127.         }  
  128.         try {  
  129.             StringBuilder sb = new StringBuilder();  
  130.             int currentLength = 0;  
  131.             for (char c : replaceHtml(StringEscapeUtils.unescapeHtml4(str)).toCharArray()) {  
  132.                 currentLength += String.valueOf(c).getBytes("GBK").length;  
  133.                 if (currentLength <= length - 3) {  
  134.                     sb.append(c);  
  135.                 } else {  
  136.                     sb.append("...");  
  137.                     break;  
  138.                 }  
  139.             }  
  140.             return sb.toString();  
  141.         } catch (UnsupportedEncodingException e) {  
  142.             logger.error("", e);  
  143.         }  
  144.         return "";  
  145.     }  
  146.       
  147.     /** 
  148.      * 转换为Double类型 
  149.      */  
  150.     public static Double toDouble(Object val){  
  151.         if (val == null){  
  152.             return 0D;  
  153.         }  
  154.         try {  
  155.             return Double.valueOf(trim(val.toString()));  
  156.         } catch (Exception e) {  
  157.             logger.error("", e);  
  158.             return 0D;  
  159.         }  
  160.     }  
  161.   
  162.     /** 
  163.      * 转换为Float类型 
  164.      */  
  165.     public static Float toFloat(Object val){  
  166.         return toDouble(val).floatValue();  
  167.     }  
  168.   
  169.     /** 
  170.      * 转换为Long类型 
  171.      */  
  172.     public static Long toLong(Object val){  
  173.         return toDouble(val).longValue();  
  174.     }  
  175.   
  176.     /** 
  177.      * 转换为Integer类型 
  178.      */  
  179.     public static Integer toInteger(Object val){  
  180.         return toLong(val).intValue();  
  181.     }  
  182.       
  183.     /** 
  184.      * 获得i18n字符串 
  185.      */  
  186.     public static String getMessage(String code, Object[] args) {  
  187.         LocaleResolver localLocaleResolver = SpringContextHolder.getBean(LocaleResolver.class);  
  188.         HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();    
  189.         Locale localLocale = localLocaleResolver.resolveLocale(request);  
  190.         return SpringContextHolder.getApplicationContext().getMessage(code, args, localLocale);  
  191.     }  
  192.       
  193.     /** 
  194.      * 获得用户远程地址 
  195.      */  
  196.     public static String getRemoteAddr(HttpServletRequest request){  
  197.         String remoteAddr = request.getHeader("X-Real-IP");  
  198.         if (isNotBlank(remoteAddr)) {  
  199.             remoteAddr = request.getHeader("X-Forwarded-For");  
  200.         }  
  201.         if (isNotBlank(remoteAddr)) {  
  202.             remoteAddr = request.getHeader("Proxy-Client-IP");  
  203.         }  
  204.         if (isNotBlank(remoteAddr)) {  
  205.             remoteAddr = request.getHeader("WL-Proxy-Client-IP");  
  206.         }  
  207.         return remoteAddr != null ? remoteAddr : request.getRemoteAddr();  
  208.     }  
  209.   
  210.     /** 
  211.      * 驼峰命名法工具 
  212.      * @return 
  213.      *      toCamelCase("hello_world") == "helloWorld"  
  214.      *      toCapitalizeCamelCase("hello_world") == "HelloWorld" 
  215.      *      toUnderScoreCase("helloWorld") = "hello_world" 
  216.      */  
  217.     public static String toCamelCase(String s) {  
  218.         String s1 =s;  
  219.         if (s1 == null) {  
  220.             return null;  
  221.         }  
  222.   
  223.         s1 = s.toLowerCase();  
  224.   
  225.         StringBuilder sb = new StringBuilder(s1.length());  
  226.         boolean upperCase = false;  
  227.         for (int i = 0; i < s1.length(); i++) {  
  228.             char c = s1.charAt(i);  
  229.   
  230.             if (c == SEPARATOR) {  
  231.                 upperCase = true;  
  232.             } else if (upperCase) {  
  233.                 sb.append(Character.toUpperCase(c));  
  234.                 upperCase = false;  
  235.             } else {  
  236.                 sb.append(c);  
  237.             }  
  238.         }  
  239.   
  240.         return sb.toString();  
  241.     }  
  242.   
  243.     /** 
  244.      * 驼峰命名法工具 
  245.      * @return 
  246.      *      toCamelCase("hello_world") == "helloWorld"  
  247.      *      toCapitalizeCamelCase("hello_world") == "HelloWorld" 
  248.      *      toUnderScoreCase("helloWorld") = "hello_world" 
  249.      */  
  250.     public static String toCapitalizeCamelCase(String s) {  
  251.         String s1 = s;  
  252.         if (s1 == null) {  
  253.             return null;  
  254.         }  
  255.         s1 = toCamelCase(s1);  
  256.         return s1.substring(01).toUpperCase() + s1.substring(1);  
  257.     }  
  258.       
  259.     /** 
  260.      * 驼峰命名法工具 
  261.      * @return 
  262.      *      toCamelCase("hello_world") == "helloWorld"  
  263.      *      toCapitalizeCamelCase("hello_world") == "HelloWorld" 
  264.      *      toUnderScoreCase("helloWorld") = "hello_world" 
  265.      */  
  266.     public static String toUnderScoreCase(String s) {  
  267.         if (s == null) {  
  268.             return null;  
  269.         }  
  270.   
  271.         StringBuilder sb = new StringBuilder();  
  272.         boolean upperCase = false;  
  273.         for (int i = 0; i < s.length(); i++) {  
  274.             char c = s.charAt(i);  
  275.   
  276.             boolean nextUpperCase = true;  
  277.   
  278.             if (i < (s.length() - 1)) {  
  279.                 nextUpperCase = Character.isUpperCase(s.charAt(i + 1));  
  280.             }  
  281.   
  282.             if ((i > 0) && Character.isUpperCase(c)) {  
  283.                 if (!upperCase || !nextUpperCase) {  
  284.                     sb.append(SEPARATOR);  
  285.                 }  
  286.                 upperCase = true;  
  287.             } else {  
  288.                 upperCase = false;  
  289.             }  
  290.   
  291.             sb.append(Character.toLowerCase(c));  
  292.         }  
  293.   
  294.         return sb.toString();  
  295.     }  
  296.    
  297.     /** 
  298.      * 转换为JS获取对象值,生成三目运算返回结果 
  299.      * @param objectString 对象串 
  300.      *   例如:row.user.id 
  301.      *   返回:!row?'':!row.user?'':!row.user.id?'':row.user.id 
  302.      */  
  303.     public static String jsGetVal(String objectString){  
  304.         StringBuilder result = new StringBuilder();  
  305.         StringBuilder val = new StringBuilder();  
  306.         String[] vals = split(objectString, ".");  
  307.         for (int i=0; i<vals.length; i++){  
  308.             val.append("." + vals[i]);  
  309.             result.append("!"+(val.substring(1))+"?'':");  
  310.         }  
  311.         result.append(val.substring(1));  
  312.         return result.toString();  
  313.     }  
  314.       
  315. }  
有了上面这些基础的东西,只需要在写一个控制层接口,就可以看到每次返回一个page对象,然后里面封装好了查询对象的列表,并且是按分页得出列表。
  1. package com.store.controller;  
  2.   
  3. import javax.servlet.http.HttpServletRequest;  
  4. import javax.servlet.http.HttpServletResponse;  
  5.   
  6. import org.springframework.beans.factory.annotation.Autowired;  
  7. import org.springframework.web.bind.annotation.RequestMapping;  
  8. import org.springframework.web.bind.annotation.ResponseBody;  
  9. import org.springframework.web.bind.annotation.RestController;  
  10.   
  11. import com.store.base.secondmodel.base.Page;  
  12. import com.store.base.secondmodel.pratice.model.Product;  
  13. import com.store.base.secondmodel.pratice.service.ProductService;  
  14.   
  15. /**  
  16.  *TODO  
  17.  *2016年10月11日  
  18.  *yiyong_wu  
  19.  */  
  20. @RestController  
  21. @RequestMapping("/product")  
  22. public class ProductController {  
  23.       
  24.     @Autowired  
  25.     private ProductService productService;  
  26.       
  27.     @ResponseBody  
  28.     @RequestMapping(value="/getPageProduct")  
  29.     public Page<Product> getPageProduct(HttpServletRequest request,HttpServletResponse response){  
  30.         Page<Product> page =  productService.findPage(new Page<Product>(request,response), new Product());  
  31.         return page;  
  32.     }  
  33.   
  34. }  

最后在看一下页面怎么使用这个page对象,这样我们就完整地介绍了这个一个分页功能,代码很多,但很完整。
  1. <%@ page contentType="text/html;charset=UTF-8"%>  
  2. <%@ include file="/WEB-INF/views/include/taglib.jsp"%>  
  3. <html>  
  4. <head>  
  5.     <title></title>  
  6.     <meta name="decorator" content="default" />  
  7.         function page(n, s) {  
  8.             if (n)  
  9.                 $("#pageNo").val(n);  
  10.             if (s)  
  11.                 $("#pageSize").val(s);  
  12.             $("#searchForm").attr("action", "${ctx}/app/bank/list");  
  13.             $("#searchForm").submit();  
  14.             return false;  
  15.         }  
  16.     </script>  
  17. </head>  
  18. <body>  
  19.   
  20.     <form:form id="searchForm" modelAttribute="XXXX" action="${ctx}/XXX" method="post" class="breadcrumb form-search ">  
  21.         <input id="pageNo" name="pageNo" type="hidden" value="${page.pageNo}" />  
  22.         <input id="pageSize" name="pageSize" type="hidden" value="${page.pageSize}" />  
  23.         <ul class="ul-form">  
  24.             <li>  
  25.                 <label>是否上架:</label>  
  26.                     <form:select id="status" path="status" class="input-medium">  
  27.                         <form:option value="" label=""/>  
  28.                         <form:options items="${fns:getDictList('yes_no_app')}" itemLabel="label" itemValue="value" htmlEscape="false"/>  
  29.                     </form:select>  
  30.             </li>  
  31.   
  32.             <li class="btns"><input id="btnSubmit" class="btn btn-primary" type="submit" value="查询"/>  
  33.             <li class="clearfix"></li>  
  34.         </ul>  
  35.     </form:form>  
  36.     <sys:message content="${message}" />  
  37.     <sys:message content="${message}" />  
  38.     <table id="contentTable"  
  39.         class="table table-striped table-bordered table-condensed">  
  40.         <thead>  
  41.             <tr>  
  42.                 <th>XXXX</th>  
  43.                 <th>XXXX</th>  
  44.                 <th>XXXX</th>  
  45.                 <th>XXXX</th>  
  46.                 <th>XXXX</th>  
  47.                 <th>XXXX</th>  
  48.                 <th>XXXX</th>  
  49.                 <th>XXXX</th>  
  50.             </tr>  
  51.         </thead>  
  52.         <tbody>  
  53.             <c:forEach items="${page.list}" var="XXXX">  
  54.                 <tr>  
  55.                     <td>${XXXX.name}</td>  
  56.                     <td><a href="${ctx}/app/bank/form?id=${XXXX.id}">${XXXX.}</a></td>  
  57.                     <td>${XXXX.}</td>  
  58.                     <td>${XXXX.}</td>  
  59.                     <td>${XXXX.}</td>  
  60.                     <td>${fns:getDictLabel(XXXX.isHot, 'yes_no_app', '无')}</td>  
  61.                     <td>${XXXX.}</td>  
  62.                     <td><c:if test="${XXXX.status==1 }">上架</c:if>  
  63.                         <c:if test="${XXXX.status==2 }">下架</c:if>  
  64.                     </td>                           
  65.                 </tr>  
  66.             </c:forEach>  
  67.         </tbody>  
  68.     </table>  
  69.     <div class="pagination">${page} <li style="padding-top: 6px;padding-left: 12px;float: left;">共${page.count}条</li></div>  
  70. </body>  
  71. </html>  

到这里就基本上把整个分页功能描述得比较清楚了,希望可以帮助到你们快速解决分页这个问题,当然要在前端显示分页漂亮的话要针对li做一些css样式啥的,最后祝福你可以快速掌握这个分页功能!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值