任务清单
- 分页显示Admin数据
- 不带关键词分页
- 带关键词分页
- 新增Admin
- 更新Admin
- 单挑删除Admin
分页
目标
以分页的形式把管理员信息显示到页面上.
特殊需求:兼顾关键词查询,让后端代码不管有没有查询条件都能够以分页形式显示数据.
思路
- 点击用户维护
- 跳转到admin-page.jsp,在页面初始化的时候把分页数据也加载出来
- 在page页面查询关键词的时候,提交给后台,然后在页面初始化的时候把分页数据加载出来
- 点击上一页下一页,把页面pageNum(和keyword)提交给后台,然后在页面初始化的时候把分页数据加载出来
技术点
- 让SQL语句针对keyword时有时无的情况进行适配
- 使用SQL中做字符串连接的函数:CONCAT(“%”,#{keyword},”%”)
- keyword有值:“like %tom%“
- Key 无值:”like %%“
- pageHelper使用
- 引入依赖
- 在SqlSessionFactoryBean中配置PageHelper
- 在Java代码中使用
- PageHelper.startPage(PageNum,PageSize)
- PageInfo<Admin> pageInfo = new PageInfo(adminList);
- 显示页码
- 使用JQuery插件:Pagination
后端代码
查询Admin数据的SQL语句
<select id= "selectAdminByKeyword" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from t_admin where login_acct like CONCAT("%",#{keyword},"%") or user_name like CONCAT("%",#{keyword},"%") or email like CONCAT("%",#{keyword},"%") </select>
准备PageHelper环境
1、导入坐标
<!--mybatis 分页插件--> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.11</version> </dependency>
2、配置SqlSessionFactoryBean
<!-- 配置 MyBatis 的插件 --> <property name="plugins"> <array> <!-- 配置 PageHelper --> <bean class="com.github.pagehelper.PageInterceptor"> <property name="properties"> <value> <!-- 分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。 你可以配置helperDialect属性来指定分页插件使用哪种方言。配置时,可以使用下面的缩写值: oracle,mysql,mariadb,sqlite,hsqldb,postgresql,db2,sqlserver,informix,h2,sqlserver2012,derby 特别注意:使用 SqlServer2012 数据库时,需要手动指定为 sqlserver2012,否则会使用 SqlServer2005 的方式进行分页。 --> helperDialect=mysql <!-- 默认false禁用 启用合理化时,如果pageNum<1会查询第一页, 如果pageNum>pages会查询最后一页 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 --> reasonable=true <!-- 支持通过 Mapper 接口参数来传递分页参数,默认值false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。 使用方法可以参考测试代码中的 com.github.pagehelper.test.basic 包下的 ArgumentsMapTest 和 ArgumentsObjTest。 --> supportMethodsArguments=true <!-- 为了支持startPage(Object params)方法,增加了该参数来配置参数映射, 用于从对象中根据属性名取值, 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值, 默认值为pageNum=pageNum;pageSize=pageSize;count=countSql; reasonable=reasonable;pageSizeZero=pageSizeZero。 --> params=count=countSql <!-- 默认值为 false。设置为 true 时,允许在运行时根据多数据源自动识别对应方言的分页 (不支持自动选择sqlserver2012,只能使用sqlserver)。 --> autoRuntimeDialect=true </value> </property> </bean> </array> </property>
3、AdminService方法
@Override public PageInfo<Admin> selectAdminByKeyWord(String keyWord, Integer pageNum, Integer pageSize) { // 1. 开启pageHelper的静态方法,开启分页功能 PageHelper.startPage(pageNum,pageSize); // 2. 执行查询 List<Admin> admins = adminMapper.selectAdminByKeyWord(keyWord); // 3. 封装到pageInfo对象中 PageInfo<Admin> pageInfo = PageInfo.of(admins); return pageInfo; }
4、AdminController
@RequestMapping("/admin/get/page.html") public String getPageInfo( //注意:页面上有可能不提供关键词,要进行匹配 //@RequestParam注解中设置defaultValue属性为空字符串表示不提供关键词 @RequestParam(value = "keyword", defaultValue = "") String keyword, //默认从第一页开始 @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum, @RequestParam(value = "pageSize", defaultValue = "2") Integer pageSize, ModelMap modelMap) { // 1. 获取pageInfo PageInfo<Admin> adminPageInfo = adminService.selectAdminByKeyWord(keyword, pageNum, pageSize); // 2. 将pageInfo存入Model modelMap.addAttribute(ConstantUtil.ATTR_NAME_PAGEINFO, adminPageInfo); return "/admin-page"; }
5、前端代码
根据返回的pageInfo数据,拼接成表进行显示
<tbody> <c:if test="${empty requestScope.pageInfo.list}"> <tr> <td colspan="6" align="center"> 抱歉!没有查询到你要的数据</td> </tr> </c:if> <c:if test="${!empty requestScope.pageInfo.list}"> <c:forEach items="${requestScope.pageInfo.list}" var="admin" varStatus="myStatus"> <tr> <td>${myStatus.count}</td> <td><input type="checkbox"></td> <td>${admin.loginAcct}</td> <td>${admin.userName}</td> <td>${admin.email}</td> <td> <%--<button type="button" class="btn btn-success btn-xs"><i class=" glyphicon glyphicon-check"></i></button>--%> <a href="assign/to/assign/role/page.html?adminId=${admin.id}&pageNum=${requestScope.pageInfo.pageNum}&keyword=${param.keyword}" class="btn btn-success btn-xs"><i class=" glyphicon glyphicon-check"></i></a> <%--<button type="button" class="btn btn-primary btn-xs"><i class=" glyphicon glyphicon-pencil"></i></button>--%> <%--<button type="button" class="btn btn-danger btn-xs"><i class=" glyphicon glyphicon-remove"></i></button>--%> <a href="admin/to/edit/page.html?adminId=${admin.id}&pageNum=${requestScope.pageInfo.pageNum}&keyword=${param.keyword}" class="btn btn-primary btn-xs"><i class=" glyphicon glyphicon-pencil"></i></a> <a href="admin/remove/${admin.id}/${requestScope.pageInfo.pageNum}/${param.keyword}.html" class="btn btn-danger btn-xs"><i class=" glyphicon glyphicon-remove"></i></a> </td> </tr> </c:forEach> </c:if> </tbody>
6、加入Pagination插件环境
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html> <html lang="zh_CN"> <%@include file="/WEB-INF/pages/admincommon/include-head.jsp" %> <link rel="stylesheet" href="static/css/pagination.css"/> <script type="text/javascript" src="static/jquery/jquery.pagination.js"></script> <script type="text/javascript">
7、编写pagination代码
<script type="text/javascript">
$(function () { initPagination(); }); //生成分页导航条函数 function initPagination() { //获取总记录数 var totalRecord = ${requestScope.pageInfo.total}; var properties = { num_edge_entries: 3, //边缘页数 num_display_entries: 5, //主体页数 callback: pageselectCallback, items_per_page:${requestScope.pageInfo.pageSize}, //每页显示1项 current_page: ${requestScope.pageInfo.pageNum - 1},//Pagination内部使用pageIndex来管理页面 prev_text: "上一页", next_text: "下一页" } // 生成页码导航条 $("#Pagination").pagination(totalRecord, properties); } //pageIndex 0- function pageselectCallback(pageIndex, jQuery) { //根据pageIndex计算pageNum var pageNum = pageIndex + 1; //跳转页面 window.location.href = "admin/get/page.html?pageNum=" + pageNum + "&keyword=${param.keyword}"; //由于每个按钮都是超链接,所以这里取消超链接的默认行为
//由于会回调函数,且我们这里时跳转页面,因此会不断重复
//解决方法是return false取消默认回调的行为
return false } </script>
关键词查询
页面调整待提交的表单
<form action="admin/get/page.html" method="post" class="form-inline" role="form" style="float:left;"> <div class="form-group has-feedback"> <div class="input-group"> <div class="input-group-addon">查询条件</div> <input name="keyword" class="form-control has-success" type="text" placeholder="请输入查询条件"> </div> </div> <button type="submit" class="btn btn-warning"><i class="glyphicon glyphicon-search"></i> 查询 </button> </form>
翻页时保持keyword值
//pageIndex 0- function pageselectCallback(pageIndex, jQuery) { //根据pageIndex计算pageNum var pageNum = pageIndex + 1; //跳转页面 //EL表达式中的param也是一个隐含对象,可以用来获取请求参数 window.location.href = "admin/get/page.html?pageNum=" + pageNum + "&keyword=${param.keyword}"; //由于每个按钮都是超链接,所以这里取消超链接的默认行为 return false; }
注意:EL表达式中的param是一个隐含的对象,可以用来获取请求参数.
新增管理员信息
目标
创建新的管理员信息,通过表单
思路
代码
设置唯一约束
Alter table ‘表名’ add unique index(‘字段名’)
业务层逻辑
@Override public void saveAdmin(Admin admin) { //生成系统当前时间 Date date = new Date(); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String createTime = format.format(date); admin.setCreateTime(createTime); //针对登陆密码进行加密 String source = admin.getUserPswd(); String encoded = MD5Util.md5(source); admin.setUserPswd(encoded); //执行保存,如果账号被占用会抛出异常 try{ adminMapper.insert(admin); }catch(Exception e){ e.printStackTrace(); //检测当前捕获的异常对象,如果是DuplicateKeyException类型说明账号重复导致 if (e instanceof DuplicateKeyException){ //抛出自定义的LoginAcctAlreadyExist throw new LoginAccountAlreadlyInUse(ConstantUtil.MESSAGE_SYSTEM_ERROR_LOGIN_NOT_UNIQUE); } //如果不是则继续往上抛 throw e; } }
异常映射处理器类
当抛出对应异常时,会被异常拦截器捕捉
@ExceptionHandler(LoginAccountAlreadlyInUse.class) public ModelAndView resolverLoginAccountAlreadlyInUseException(LoginAccountAlreadlyInUse e, HttpServletRequest request, HttpServletResponse response) throws IOException { String viewName = "admin-add"; ModelAndView modelAndView = common(viewName, e, request, response); return modelAndView; } /** * 提取可复用部分 * @return */ public ModelAndView common(String viewName, Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException { boolean requestType = CrowdUtil.isAjaxRequest(request); if (requestType) { ResultEntity<Object> failed = ResultEntity.failed(e.getMessage()); String json = new Gson().toJson(failed); response.getWriter().write(json); return null; } else { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("exception", e); modelAndView.setViewName(viewName); return modelAndView; } }
自定义异常
控制层
@RequestMapping("/save/admin.html") public String saveAdmin(Admin admin) { //执行保存 adminService.saveAdmin(admin); // 为了让用户第一眼就看到新增加的用户,直接跳转到最后一页 return "redirect:/admin/page.html?pageNum=" + Integer.MAX_VALUE; }
保存之后重定向
"redirect:/admin/page.html?pageNum=" + Integer.MAX_VALUE;
表单页面
配置跳转 <mvc:view-controller path="/admin/to/add/page.html" view-name="admin-add"/>
更新删除管理员信息
目标
通过提交页面上的表单修改某个Admin的数据
思路
- 在admin-page.jsp页面点击修改,带着id到后台,查询出对应的admin
- 把admin保存在request域
- 重定向到edit页面并填充
- 当点击修改时到后台执行更新
- 重定向到page页面
- 为了保持所在页和关键词还需要pageNum和keyword
- 重定向到main-page.jsp点击删除(携带id)
- 提交到后台处理完,重定向到page页面
- 什么时候使用重定向,不需要再返回操作,不需要request的数据
代码
<a href="admin/to/edit/page.html?adminId=${admin.id}&pageNum=${requestScope.pageInfo.pageNum}&keyword=${param.keyword}" class="btn btn-primary btn-xs"><i class=" glyphicon glyphicon-pencil"></i></a> <a href="admin/remove/${admin.id}/${requestScope.pageInfo.pageNum}/${param.keyword}.html" class="btn btn-danger btn-xs"><i class=" glyphicon glyphicon-remove"></i></a>
controller
@RequestMapping("/admin/to/edit/page.html") public String toAdminEdit(@RequestParam("adminId") Integer adminId, ModelMap map) { // 1.根据 id(主键)查询待更新的 Admin 对象 Admin admin = adminService.selectAdminById(adminId); // 2.将 Admin 对象存入模型 map.addAttribute("admin", admin); return "admin-edit"; } // @RequestMapping("/admin/update.html") public String editAdmin(Admin admin, @RequestParam("pageNum") Integer pageNum, @RequestParam("keyword") String keyword, ModelMap map) { adminService.updateAdmin(admin); return "redirect:/admin/get/page.html?pageNum=" + pageNum + "&keyword=" + keyword; }
@RequestMapping("/admin/remove/{id}/{pageNum}/{keyword}") public String deleteAdmin(@PathVariable("id") Integer id, @PathVariable("pageNum") Integer pageNum, @PathVariable(value = "keyword") String keyword) { adminService.deleteAdminByPrimaryKey(id); return "redirect:/admin/get/page.html?pageNum=" + pageNum + "&keyword=" + keyword; }
service
@Override public void deleteAdminByPrimaryKey(Integer id) { adminMapper.deleteByPrimaryKey(id); } @Override public Admin selectAdminById(Integer adminId) { Admin admin = adminMapper.selectByPrimaryKey(adminId); return admin; } @Override public void updateAdmin(Admin admin) { try { adminMapper.updateByPrimaryKeySelective(admin); } catch (Exception e) { if (e instanceof DuplicateKeyException) { throw new LoginAccountAlreadlyInUseForUpdate("不允许重复的账户"); } } }