摘要:本文实现了一个后台由Spring+Mybatis+SpringMVC组成,分页采用PageHelper,前台展示使用bootstrap-paginator来显示效果的分页实例。整个项目由maven构成。这里主要讲了分页的实例,框架怎么搭建就不再说明,主要是在这里的基础上来增加分页功能的。注意,此文是在这个基础 Spring+Mybatis+SpringMVC+Maven+MySql搭建实例 之上来做分页的,建议文中看不懂的配置可以看看这里。
整个工程下载(旧版本,日志打印使用log4j,数据库配置放在properties文件)
新版本下载:https://github.com/appleappleapple/ssm_project (日志打印使用logback,数据库配置放在POM.XML)博主推荐使用新版本,而且这里详细说明了整个工程应用的框架,数据源配置,SQL语句等等!
重要的事情说三遍:请下新版本请下新版本请下新版本~
最后的结果如下:
环境:jdk1.6
Tomcat 7.0
Eclipse luna/windows 7
一、后台PageHelper使用
PageHelper:https://github.com/pagehelper/Mybatis-PageHelper
1、引入jar包
<!-- 添加分布插件的包pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.0.0</version>
</dependency>
2.mybatis-config.xml中添加插件
这样子就引入进来了,接下来就是来开始分页功能的实现3、mapper文件中添加如下一个方法:
<select id="selectUserByUserName" parameterType="java.lang.String" resultMap="BaseResultMap">
SELECT *
FROM t_user
WHERE 1 = 1
<if test="userName != null and userName !=''">
AND USER_NAME = #{userName,jdbcType=VARCHAR}
</if>
ORDER BY USER_ID
</select>
注意,这里的返回其实是一个list
<!--设置domain类和数据库中表的字段一一对应,注意数据库字段和domain类中的字段名称不致,此处一定要! -->
<resultMap id="BaseResultMap" type="com.lin.domain.User">
<id column="USER_ID" property="userId" jdbcType="INTEGER" />
<result column="USER_NAME" property="userName" jdbcType="CHAR" />
<result column="USER_PASSWORD" property="userPassword" jdbcType="CHAR" />
<result column="USER_EMAIL" property="userEmail" jdbcType="CHAR" />
</resultMap>
4、然后就是dao类
/**
*
* @author linbingwen
* @since 2015年10月22日
* @param userName
* @return
*/
List<User> selectUserByUserName(@Param("userName") String userName);
这里一定的记得加@Param(“userName”)
接下来就可以在service层中添加分页查询的的接口了
5、接口类
/**
*
* @author linbingwen
* @since 2015年10月23日
* @param userName 查询条件,可为空
* @param pageNo 查询条件,可为空,默认取1
* @param pageSize 查询条件,可为空,默认取10
* @return
*/
PagedResult<User> queryByPage(String userName,Integer pageNo,Integer pageSize);
6、实现类
public PagedResult<User> queryByPage(String userName,Integer pageNo,Integer pageSize ) {
pageNo = pageNo == null?1:pageNo;
pageSize = pageSize == null?10:pageSize;
PageHelper.startPage(pageNo,pageSize); //startPage是告诉拦截器说我要开始分页了。分页参数是这两个。
return BeanUtil.toPagedResult(userDao.selectUserByUserName(userName));
}
这里就可以直接在返回里头使用了PageHelper,这里userDao.selectUserByUserName(userName)的返回是一个list
其中,PagedResult是我自己封装的一个分页结果类
package com.lin.util;
import java.util.List;
import com.lin.dto.BaseEntity;
/**
-
功能概要:
-
@author linbingwen
-
@since 2015年10月23日
*/
public class PagedResult extends BaseEntity {/serialVersionUID/
private static final long serialVersionUID = 1L;private List dataList;//数据
private long pageNo;//当前页
private long pageSize;//条数
private long total;//总条数
private long pages;//总页面数目
public List getDataList() {
return dataList;
}public void setDataList(List dataList) {
this.dataList = dataList;
}public long getPageNo() {
return pageNo;
}public void setPageNo(long pageNo) {
this.pageNo = pageNo;
}public long getPageSize() {
return pageSize;
}public void setPageSize(long pageSize) {
this.pageSize = pageSize;
}public long getTotal() {
return total;
}public void setTotal(long total) {
this.total = total;
}public long getPages() {
return pages;
}public void setPages(long pages) {
this.pages = pages;
}
}
这是它的基类
package com.lin.dto;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
-
类说明:bean基类
-
-
详细描述:
-
@author costin_law
-
@since 2014-5-5
*/
public abstract class BaseEntity implements Serializable{
private static final long serialVersionUID = 1L;private static Map<Class<?>,PropertyInfo[]> class2Props = new HashMap
package com.lin.util;
import java.util.List;
import com.github.pagehelper.Page;
import com.lin.util.PagedResult;
/**
- 功能概要:
- @author linbingwen
- @since 2015年10月22日
*/
public class BeanUtil {
public static <T> PagedResult<T> toPagedResult(List<T> datas) {
PagedResult<T> result = new PagedResult<T>();
if (datas instanceof Page) {
Page page = (Page) datas;
result.setPageNo(page.getPageNum());
result.setPageSize(page.getPageSize());
result.setDataList(page.getResult());
result.setTotal(page.getTotal());
result.setPages(page.getPages());
}
else {
result.setPageNo(1);
result.setPageSize(datas.size());
result.setDataList(datas);
result.setTotal(datas.size());
}
return result;
}
}
7、这样就好了,可以跑单元测试了
/**
* 分页测试
* @author linbingwen
* @since 2015年10月22日
*/
@Test
public void queryByPage(){
PagedResult<User> pagedResult = userService.queryByPage(null,1,10);//null表示查全部
logger.debug("查找结果" + pagedResult);
}
输出结果:
看不清的话看下面
查找结果{total:46,dataList:Page{pageNum=1, pageSize=10, startRow=0, endRow=10, total=46, pages=5, reasonable=false,
pageSizeZero=true},pageNo:1,pageSize:10,pages:5}
其中的dataList中存放的就是数据
打个断点看下就知道了:
二、前台展示分页结果
前台展示主要使用了bootstrap-paginator,这里的原理其实就是将上面查出来的结果,转换成json数据传给前台,然后前台再根据条数和分页数目、总目生成表格,同时每次点击对应的按钮都发送一个ajax请求到后台查询应对的数据,前台每次发送到后台都会包含分页数目、查询条件
1、Controller层的基类
这个基类主要实现了将数据转成json
引用到的jar包如下:
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.3</version>
<classifier>jdk15</classifier>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>1.6.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.4.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.34</version>
</dependency>
基类如下:
package com.lin.controller;
import com.lin.common.HttpConstants;
import com.lin.json.JsonDateValueProcessor;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JsonConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
/**
-
Controller基类
*/
public class BaseController {protected Logger logger = LoggerFactory.getLogger(this.getClass());
protected final static String DATE_FORMATE = “yyyy-MM-dd”;
/**
- 返回服务端处理结果
- @param obj 服务端输出对象
- @return 输出处理结果给前段JSON格式数据
- @author YANGHONGXIA
- @since 2015-01-06
*/
public String responseResult(Object obj){
JSONObject jsonObj = null;
if(obj != null){
logger.info(“后端返回对象:{}”, obj);
JsonConfig jsonConfig = new JsonConfig();
jsonConfig.registerJsonValueProcessor(Date.class, new JsonDateValueProcessor());
jsonObj = JSONObject.fromObject(obj, jsonConfig);
logger.info(“后端返回数据:” + jsonObj);
if(HttpConstants.SERVICE_RESPONSE_SUCCESS_CODE.equals(jsonObj.getString(HttpConstants.SERVICE_RESPONSE_RESULT_FLAG))){
jsonObj.element(HttpConstants.RESPONSE_RESULT_FLAG_ISERROR, false);
jsonObj.element(HttpConstants.SERVICE_RESPONSE_RESULT_MSG, “”);
}else{
jsonObj.element(HttpConstants.RESPONSE_RESULT_FLAG_ISERROR, true);
String errMsg = jsonObj.getString(HttpConstants.SERVICE_RESPONSE_RESULT_MSG);
jsonObj.element(HttpConstants.SERVICE_RESPONSE_RESULT_MSG, errMsg==null?HttpConstants.SERVICE_RESPONSE_NULL:errMsg);
}
}
logger.info(“输出结果:{}”, jsonObj.toString());
return jsonObj.toString();
}
/**
- 返回成功
- @param obj 输出对象
- @return 输出成功的JSON格式数据
*/
public String responseSuccess(Object obj){
JSONObject jsonObj = null;
if(obj != null){
logger.info(“后端返回对象:{}”, obj);
JsonConfig jsonConfig = new JsonConfig();
jsonConfig.registerJsonValueProcessor(Date.class, new JsonDateValueProcessor());
jsonObj = JSONObject.fromObject(obj, jsonConfig);
logger.info(“后端返回数据:” + jsonObj);
jsonObj.element(HttpConstants.RESPONSE_RESULT_FLAG_ISERROR, false);
jsonObj.element(HttpConstants.SERVICE_RESPONSE_RESULT_MSG, “”);
}
logger.info(“输出结果:{}”, jsonObj.toString());
return jsonObj.toString();
}
/**
- 返回成功
- @param obj 输出对象
- @return 输出成功的JSON格式数据
*/
public String responseArraySuccess(Object obj){
JSONArray jsonObj = null;
if(obj != null){
logger.info(“后端返回对象:{}”, obj);
JsonConfig jsonConfig = new JsonConfig();
jsonConfig.registerJsonValueProcessor(Date.class, new JsonDateValueProcessor());
jsonObj = JSONArray.fromObject(obj, jsonConfig);
logger.info(“后端返回数据:” + jsonObj);
}
logger.info(“输出结果:{}”, jsonObj.toString());
return jsonObj.toString();
}
/**
- 返回成功
- @param obj 输出对象
- @return 输出成功的JSON格式数据
*/
public String responseSuccess(Object obj, String msg){
JSONObject jsonObj = null;
if(obj != null){
logger.info(“后端返回对象:{}”, obj);
JsonConfig jsonConfig = new JsonConfig();
jsonConfig.registerJsonValueProcessor(Date.class, new JsonDateValueProcessor());
jsonObj = JSONObject.fromObject(obj, jsonConfig);
logger.info(“后端返回数据:” + jsonObj);
jsonObj.element(HttpConstants.RESPONSE_RESULT_FLAG_ISERROR, false);
jsonObj.element(HttpConstants.SERVICE_RESPONSE_RESULT_MSG, msg);
}
logger.info(“输出结果:{}”, jsonObj.toString());
return jsonObj.toString();
}
/**
- 返回失败
- @param errorMsg 错误信息
- @return 输出失败的JSON格式数据
*/
public String responseFail(String errorMsg){
JSONObject jsonObj = new JSONObject();
jsonObj.put(HttpConstants.RESPONSE_RESULT_FLAG_ISERROR, true);
jsonObj.put(HttpConstants.SERVICE_RESPONSE_RESULT_MSG, errorMsg);
logger.info(“输出结果:{}”, jsonObj.toString());
return jsonObj.toString();
}
}
上面用到的一些变量如下:
package com.lin.common;
public class HttpConstants {
public static final String SYSTEM_ERROR_MSG = "系统错误";
public static final String REQUEST_PARAMS_NULL = "请求参数为空";
public static final String SERVICE_RESPONSE_NULL = "服务端返回结果为空";
// 服务端返回成功的标志
public static final String SERVICE_RESPONSE_SUCCESS_CODE = "AMS00000";
// 服务端返回结果的标志
public static final String SERVICE_RESPONSE_RESULT_FLAG = "returnCode";
// 服务端返回结果失败的标志
public static final String SERVICE_RESPONSE_RESULT_MSG = "errorMsg";
// 返回给前段页面成功或失败的标志
public static final String RESPONSE_RESULT_FLAG_ISERROR = "isError";
// 执行删除操作
public static final String OPERATION_TYPE_DELETE = "D";
public static final String ENUM_PATH = "com.mucfc.msm.enumeration.";
}
引用一个包的内容如下:
package com.lin.json;
import net.sf.json.JsonConfig;
import net.sf.json.processors.JsonValueProcessor;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class JsonDateValueProcessor implements JsonValueProcessor {
/**
* datePattern
*/
private String datePattern = "yyyy-MM-dd HH:mm:ss";
/**
* JsonDateValueProcessor
*/
public JsonDateValueProcessor() {
super();
}
/**
* @param format
*/
public JsonDateValueProcessor(String format) {
super();
this.datePattern = format;
}
/**
* @param value
* @param jsonConfig
* @return Object
*/
public Object processArrayValue(Object value, JsonConfig jsonConfig) {
return process(value);
}
/**
* @param key
* @param value
* @param jsonConfig
* @return Object
*/
public Object processObjectValue(String key, Object value, JsonConfig jsonConfig) {
return process(value);
}
/**
* process
*
* @param value
* @return
*/
private Object process(Object value) {
try {
if (value instanceof Date) {
SimpleDateFormat sdf = new SimpleDateFormat(datePattern, Locale.UK);
return sdf.format((Date) value);
}
return value == null ? "" : value.toString();
} catch (Exception e) {
return "";
}
}
/**
* @return the datePattern
*/
public String getDatePattern() {
return datePattern;
}
/**
* @param pDatePattern the datePattern to set
*/
public void setDatePattern(String pDatePattern) {
datePattern = pDatePattern;
}
}
这里主要实现了能将list/map/set/数组等转换成josn,并传到前台‘
2、光这里写不行,还得配置springMVC中以json来传递数据,并配置自己的字符过滤器,要不然中文传到前台可能乱码,这里的配置比较复杂,大部分时间都花在这里,
这里我直接放spingMVC的配置:spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?><!-- 扫描controller(controller层注入) -->
<context:component-scan base-package=“com.lin.controller” use-default-filters=“false”>
<context:include-filter type=“annotation” expression=“org.springframework.stereotype.Controller”/>
<context:include-filter type=“annotation” expression=“org.springframework.web.bind.annotation.ControllerAdvice”/>
</context:component-scan>
<!-- 会自动注册了validator ConversionService -->
<mvc:annotation-driven validator="validator" conversion-service="conversionService" content-negotiation-manager="contentNegotiationManager">
<mvc:message-converters register-defaults="true">
<!-- StringHttpMessageConverter编码为UTF-8,防止乱码 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
<property name = "supportedMediaTypes">
<list>
<bean class="org.springframework.http.MediaType">
<constructor-arg index="0" value="text"/>
<constructor-arg index="1" value="plain"/>
<constructor-arg index="2" value="UTF-8"/>
</bean>
<bean class="org.springframework.http.MediaType">
<constructor-arg index="0" value="*"/>
<constructor-arg index="1" value="*"/>
<constructor-arg index="2" value="UTF-8"/>
</bean>
</list>
</property>
</bean>
<!-- 避免IE执行AJAX时,返回JSON出现下载文件 -->
<bean id="fastJsonHttpMessageConverter" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
</list>
</property>
<!--<property name="serializerFeature">-->
<!--这个地方加上这个功能吧,能自己配置一些东西,比如时间的格式化,null输出""等等-->
<!--</property>-->
</bean>
</mvc:message-converters>
<mvc:argument-resolvers>
<bean class="org.springframework.data.web.PageableHandlerMethodArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
<!-- 内容协商管理器 -->
<!--1、首先检查路径扩展名(如my.pdf);2、其次检查Parameter(如my?format=pdf);3、检查Accept Header-->
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<!-- 扩展名至mimeType的映射,即 /user.json => application/json -->
<property name="favorPathExtension" value="true"/>
<!-- 用于开启 /userinfo/123?format=json 的支持 -->
<property name="favorParameter" value="true"/>
<property name="parameterName" value="format"/>
<!-- 是否忽略Accept Header -->
<property name="ignoreAcceptHeader" value="false"/>
<property name="mediaTypes"> <!--扩展名到MIME的映射;favorPathExtension, favorParameter是true时起作用 -->
<value>
json=application/json
xml=application/xml
html=text/html
</value>
</property>
<!-- 默认的content type -->
<property name="defaultContentType" value="text/html"/>
</bean>
<!-- 当在web.xml 中 DispatcherServlet使用 <url-pattern>/</url-pattern> 映射时,能映射静态资源 -->
<mvc:default-servlet-handler />
<!-- 静态资源映射 -->
<mvc:resources mapping="/static/**" location="/WEB-INF/static/"/>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/view/" p:suffix=".jsp"/>
<!-- 这里设置静态的资源 -->
3、Spirng中也和配置:
<?xml version="1.0" encoding="UTF-8"?> <!-- 以下 validator ConversionService 在使用 mvc:annotation-driven 会 自动注册-->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
</bean>
<!-- 引入jdbc配置文件 -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:properties/*.properties</value>
<!--要是有多个配置文件,只需在这里继续添加即可 -->
</list>
</property>
</bean>
<!-- 扫描注解Bean -->
<context:component-scan base-package="com.lin.service">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 激活annotation功能 -->
<context:annotation-config />
<!-- 激活annotation功能 -->
<context:spring-configured />
<!-- 注解事务配置 -->
<!-- 类型转换及数据格式化 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"/>
<!-- 配置数据源 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!-- 不使用properties来配置 -->
<!-- <property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/learning" />
<property name="username" value="root" />
<property name="password" value="christmas258@" /> -->
<!-- 使用properties来配置 -->
<property name="driverClassName">
<value>${jdbc_driverClassName}</value>
</property>
<property name="url">
<value>${jdbc_url}</value>
</property>
<property name="username">
<value>${jdbc_username}</value>
</property>
<property name="password">
<value>${jdbc_password}</value>
</property>
</bean>
<!-- 自动扫描了所有的XxxxMapper.xml对应的mapper接口文件,这样就不用一个一个手动配置Mpper的映射了,只要Mapper接口类和Mapper映射文件对应起来就可以了。 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage"
value="com.lin.dao" />
</bean>
<!-- 配置Mybatis的文件 ,mapperLocations配置**Mapper.xml文件位置,configLocation配置mybatis-config文件位置-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath*:com/lin/mapper/**/*.xml"/>
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml" />
<!-- <property name="typeAliasesPackage" value="com.tiantian.ckeditor.model"
/> -->
</bean>
其中validator这个bean需要引用如下:
javax.validation validation-api 1.1.0.Final <dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.0.1.Final</version>
</dependency>
4、conroller层编写
package com.lin.controller;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import com.github.pagehelper.Page;
import com.lin.domain.User;
import com.lin.service.UserService;
import com.lin.util.PagedResult;
/**
-
功能概要:UserController
-
@author linbingwen
-
@since 2015年9月28日
*/
@Controller
public class UserController extends BaseController {private Logger logger = LoggerFactory.getLogger(getClass());
@Resource
private UserService userService;@RequestMapping("/")
public ModelAndView getIndex(){
ModelAndView mav = new ModelAndView(“index”);
User user = userService.selectUserById(1);
mav.addObject(“user”, user);
return mav;
}/**
- 显示首页
- @author linbingwen
- @since 2015年10月23日
- @return
*/
@RequestMapping("/bootstrapTest1")
public String bootStrapTest1(){
return “bootstrap/bootstrapTest1”;
}
/**
- 分页查询用户信息
- @author linbingwen
- @since 2015年10月23日
- @param page
- @return
*/
@RequestMapping(value="/list.do", method= RequestMethod.POST)
@ResponseBody
public String list(Integer pageNumber,Integer pageSize ,String userName) {
logger.info(“分页查询用户信息列表请求入参:pageNumber{},pageSize{}”, pageNumber,pageSize);
try {
PagedResult pageResult = userService.queryByPage(userName, pageNumber,pageSize);
return responseSuccess(pageResult);
} catch (Exception e) {
return responseFail(e.getMessage());
}
}
}
5、最后一步就是前台的页面了,这里可以先写页面再来写controller也可以的
<%@ page language=“java” contentType=“text/html; charset=UTF-8”
pageEncoding=“UTF-8”%>
序号 | 用户名 | 密码 | 用户邮箱 |
---|
6、最终运行结果
最后以web工程运行就可以了:
结果如下:
打印出来的一些日志:
后台返回给前台的就是json