【尚筹网】五、管理员维护
- 任务清单
- 分页管理管理员信息
- 目标
- 思路
- 代码
- 引入 PageHelper
- AdminMapper 中编写 SQL 语句
- AdminMapper 接口生成方法
- AdminService
- AdminHandler
- 页面显示主体
- 在页面上使用 `Pagination` 实现导航条
- 关键词查询
- 页面上调整表单
- 在翻页时保持关键词查询条件
- 单条删除
- 目标
- 思路
- 代码
- 调整删除的按钮
- AdminHandler.remove()
- AdminService.remove()
- 新增
- 目标
- 思路
- 在 `t_admin` 表中给账号添加唯一约束
- 调整修改按钮
- 配置 `view-controller`
- 准备表单页面
- handler 方法
- Service 方法
- 处理唯一约束
- 创建自定义异常类
- 在异常处理器类添加方法
- 修改 Service 方法
- 更新
- 目标
- 思路
- 回显表单
- 调整铅笔按钮
- Handler 方法
- 准备页面
- Service 方法
- 执行更新
- Handler 方法
- Service 方法
- 创建自定义异常
- 在异常处理器类添加方法
- 修改 Service 方法
任务清单
- 分页显示 Admin 数据
- 不带关键词分页
- 带关键词分页
- 新增 Admin
- 更新 Admin
- 单条删除 Admin
分页管理管理员信息
目标
将数据库中的 Admin 数据在页面上以分页形式显示。在后端将“带关键词”和“不带关键词”的分页合并为同一套代码
思路
代码
引入 PageHelper
-
确认是否加入了依赖
<!-- MyBatis 分页插件 --> <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId> </dependency>
-
在
SqlSessionFactoryBean
配置 MyBatis 插件
<!-- 配置 SqlSessionFactoryBean 整合 MyBatis -->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean" ><!-- 指定 MyBatis 全局配置文件位置 --><property name="configLocation" value="classpath:mybatis/mybatis-config.xml" /><!-- 指定 Mapper.xml 配置文件位置 --><property name="mapperLocations" value="classpath:mybatis/mapper/*Mapper.xml" /><!-- 配置数据源 --><property name="dataSource" ref="dataSource" /><!-- 配置插件 --><property name="plugins" ><array><!-- 配置 PageHelper --><bean class="com.github.pagehelper.PageHelper"><property name="properties"><props><!-- 配置数据库方言,告诉 PageHelper 当前使用的数据库 --><prop key="dialect">mysql</prop><!-- 配置页码的合理化修正,在1~总页数之间修正页码 --><prop key="reasonable">true</prop></props></property></bean></array></property>
</bean>
AdminMapper 中编写 SQL 语句
<select id="selectAdminByKeyword" resultMap="BaseResultMap">select id, login_acct, user_pswd, user_name, email, create_timefrom t_adminwhere login_acct like concat('%',#{keyword},'%')or user_name like concat('%',#{keyword},'%')or email like concat('%',#{keyword},'%')
</select>
AdminMapper 接口生成方法
List<Admin> selectAdminByKeyword(String keyword);
AdminService
@Override
public PageInfo<Admin> getPageInfo(String keyword, Integer pageNum, Integer pageSize) {// 1.调用 PageHelper 的静态方法开启分页功能// 这里充分体现了 PageHelper 的“非侵入式”设计:原本要做的查询不必有任何修改PageHelper.startPage(pageNum, pageSize);// 2.执行查询List<Admin> list = adminMapper.selectAdminByKeyword(keyword);// 3.封装为 PageInfo 对象返回return new PageInfo<>(list);
}
AdminHandler
@RequestMapping("/admin/get/page.html")
public String getPageInfo(// 使用 @RequestParam 注解的 defaultValue 属性,指定默认值,在请求中没有携带对应参数时使用默认值// keyword 默认值使用空字符串,和 SQL 语句配合实现两种情况的适配@RequestParam(value = "keyword", defaultValue = "") String keyword,// pageNum 默认值使用 1@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,// pageSize 默认值使用 5@RequestParam(value = "pageSize", defaultValue = "5") Integer pageSize,ModelMap modelMap
){// 调用 Service 方法获取 PageInfo 对象PageInfo<Admin> pageInfo = adminService.getPageInfo(keyword, pageNum, pageSize);// 将 PageInfo 对象存入模型modelMap.addAttribute(CrowdConstant.ATTR_NAME_PAGE_INFO, pageInfo);return "admin-page";
}
页面显示主体
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html><%@include file="/WEB-INF/include-head.jsp" %><body><%@include file="/WEB-INF/include-nav.jsp" %><div class="container-fluid"><div class="row"><%@include file="/WEB-INF/include-sidebar.jsp" %><div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"><div class="panel panel-default"><div class="panel-heading"><h3 class="panel-title"><i class="glyphicon glyphicon-th"></i> 数据列表</h3></div><div class="panel-body"><form 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 class="form-control has-success" type="text"placeholder="请输入查询条件"></div></div><button type="button" class="btn btn-warning"><i class="glyphicon glyphicon-search"></i>查询</button></form><button type="button" class="btn btn-danger" style="float:right;margin-left:10px;"><iclass=" glyphicon glyphicon-remove"></i> 删除</button><button type="button" class="btn btn-primary" style="float:right;"onclick="window.location.href='add.html'"><i class="glyphicon glyphicon-plus"></i>新增</button><br><hr style="clear:both;"><div class="table-responsive"><table class="table table-bordered"><thead><tr><th width="30">#</th><th width="30"><input type="checkbox"></th><th>账号</th><th>名称</th><th>邮箱地址</th><th width="100">操作</th></tr></thead><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><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></td></tr></c:forEach></c:if></tbody><tfoot><tr><td colspan="6" align="center"><ul class="pagination"><li class="disabled"><a href="#">上一页</a></li><li class="active"><a href="#">1 <spanclass="sr-only">(current)</span></a></li><li><a href="#">2</a></li><li><a href="#">3</a></li><li><a href="#">4</a></li><li><a href="#">5</a></li><li><a href="#">下一页</a></li></ul></td></tr></tfoot></table></div></div></div></div></div></div></body>
</html>
在页面上使用 Pagination
实现导航条
在有需要的页面进行引入
<%@include file="/WEB-INF/include-head.jsp" %>
<link rel="stylesheet" href="css/pagination.css" />
<script type="text/javascript" src="jquery/jquery.pagination.js"></script>
-
HTML代码准备
使用
Pagination
要求的 div 标签替换原有的页码部分<tfoot><tr><td colspan="6" align="center"><ul class="pagination"><li class="disabled"><a href="#">上一页</a></li><li class="active"><a href="#">1 <spanclass="sr-only">(current)</span></a></li><li><a href="#">2</a></li><li><a href="#">3</a></li><li><a href="#">4</a></li><li><a href="#">5</a></li><li><a href="#">下一页</a></li></ul></td></tr> </tfoot>
<tfoot><tr><td colspan="6" align="center"><div id="Pagination" class="pagination"></div></td></tr> </tfoot>
-
JQuery 代码
<script type="text/javascript">$(function () {// 对页面导航条进行初始化initPagination();});// 初始化分页导航条function initPagination(){// 获取总记录数var totalRecord = ${requestScope.pageInfo.total};// 声明一个 JSON 对象存储分页导航条需要用到的数据var properties = {// 边缘页数num_edge_entries: 3,// 主体页数num_display_entries: 5,// 指定用户点击翻页的按钮时跳转页面的回调函数callback: pageSelectCallback,// 每页要显示的记录数items_per_page: ${requestScope.pageInfo.pageSize},// 当前页码, Pagination 内部使用 pageIndex 来管理页码,pageIndex 从0开始,pageNum 从1开始,所以 pageNum - 1current_page: ${requestScope.pageInfo.pageNum - 1},prev_text: "上一页",next_text: "下一页"}// 生成页码导航条$('#Pagination').pagination(totalRecord, properties);}// 回调函数的含义:声明出来以后不是自己调用,而是交给系统或框架调用// 用户点击“上一页、下一页、1、2、3."这样的页码时调用这个函数实现页面跳转// pageIndex 是 Pagination 传给我们的那个从0开始的页码function pageSelectCallback(pageIndex, jQuery){// 根据 pageIndex 计算得到 pageNumvar pageNum = pageIndex + 1;// 跳转到指定页window.location.href = "admin/get/page.html?pageNum=" + pageNum;// 由于每一个页码都是超链接,所以在这个函数最后取消超链接的默认行为return false;} </script>
-
修改
Pagination
源码// 所有初始化完成,绘制链接 drawLinks(); // 回调函数 // opts.callback(current_page, this);
关键词查询
页面上调整表单
<form class="form-inline" role="form" style="float:left;" action="admin/get/page.html" method="post"><div class="form-group has-feedback"><div class="input-group"><div class="input-group-addon">查询条件</div><label><input name="keyword" class="form-control has-success" type="text"placeholder="请输入查询条件"></label></div></div><button type="submit" class="btn btn-warning"><i class="glyphicon glyphicon-search"></i>查询</button>
</form>
在翻页时保持关键词查询条件
window.location.href = "admin/get/page.html?pageNum=" + pageNum + "&keyword=${param.keyword}";
单条删除
目标
在页面上点击单条删除按钮 ,实现 Admin 对应记录的删除
思路
代码
调整删除的按钮
<%-- <button type="button" class="btn btn-danger btn-xs">
<i class=" glyphicon glyphicon-remove"></i>
</button> --%>
<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>
AdminHandler.remove()
@RequestMapping("admin/remove/{adminId}/{pageNum}/{keyword}.html")
public String remove(@PathVariable("adminId") Integer adminId,@PathVariable("pageNum") Integer pageNum,@PathVariable("keyword") String keyword){// 调用 Service 方法执行删除adminService.remove(adminId);// 页面跳转:回到分页页面// 尝试方案1:直接转发到 admin-page.jsp 会无法显示分页数据// return "admin-page";// 尝试方案2:转发到 /admin/get/page.html 页面,一旦刷新页面会重复执行删除浪费性能// return "forward:/admin/get/page.html";// 尝试方案3:重定向到 /admin/get/page.html 页面// 同时为了保持原本所在的页面和查询关键词再附加 pageNum 和 keyword 两个请求参数return "redirect:/admin/get/page.html?pageNum=" + pageNum + "&keyword=" + keyword;
}
AdminService.remove()
@Override
public void remove(Integer adminId) {adminMapper.deleteByPrimaryKey(adminId);
}
新增
目标
将表单提交的 Admin 对象保存到数据库中
要求:
- loginAcct 不能重复
- 密码加密
思路
在 t_admin
表中给账号添加唯一约束
alter table `project_crowd`.`t_admin` add unique index (`login_acct`);
调整修改按钮
<%-- <button type="button" class="btn btn-primary" style="float:right;"onclick="window.location.href='add.html'"><i class="glyphicon glyphicon-plus"></i>新增
</button> --%>
<a href="admin/to/add/page.html" class="btn btn-primary" style="float:right;"onclick="window.location.href='add.html'"><i class="glyphicon glyphicon-plus"></i>新增
</a>
配置 view-controller
<mvc:view-controller path="/admin/to/add/page.html" view-name="admin-add" />
准备表单页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html><%@include file="/WEB-INF/include-head.jsp" %><body><%@include file="/WEB-INF/include-nav.jsp" %><div class="container-fluid"><div class="row"><%@include file="/WEB-INF/include-sidebar.jsp" %><div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"><ol class="breadcrumb"><li><a href="/admin/to/main/page.html">首页</a></li><li><a href="/admin/get/page.html">数据列表</a></li><li class="active">新增</li></ol><div class="panel panel-default"><div class="panel-heading">表单数据<div style="float:right;cursor:pointer;" data-toggle="modal" data-target="#myModal"><i class="glyphicon glyphicon-question-sign"></i></div></div><div class="panel-body"><form action="admin/save.html" method="post" role="form"><p>${requestScope.exception.message}</p><div class="form-group"><label for="inputLoginAcct">登录账号</label><input type="text" class="form-control" id="inputLoginAcct" name="loginAcct"placeholder="请输入登录账号"></div><div class="form-group"><label for="inputPassword">登录密码</label><input type="text" class="form-control" id="inputPassword" name="userPswd"placeholder="请输入登录密码"></div><div class="form-group"><label for="inputUserName">用户昵称</label><input type="text" class="form-control" id="inputUserName" name="userName"placeholder="请输入用户昵称"></div><div class="form-group"><label for="inputEmail">邮箱地址</label><input type="email" class="form-control" id="inputEmail" name="email"placeholder="请输入邮箱地址"><p class="help-block label label-warning">请输入合法的邮箱地址, 格式为:xxxx@xxxx.com</p></div><button type="submit" class="btn btn-success"><i class="glyphicon glyphicon-plus"></i>新增</button><button type="reset" class="btn btn-danger"><i class="glyphicon glyphicon-refresh"></i>重置</button></form></div></div></div></div></div></body>
</html>
handler 方法
@RequestMapping("admin/save.html")
public String save(Admin admin){adminService.saveAdmin(admin);return "redirect:/admin/get/page.html?pageNum=" + Integer.MAX_VALUE;
}
Service 方法
public void saveAdmin(Admin admin) {// 1.密码加密String userPswd = admin.getUserPswd();userPswd = CrowdUtil.md5(userPswd);admin.setUserPswd(userPswd);// 2.生成创建时间Date date = new Date();SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String createTime = format.format(date);admin.setCreateTime(createTime);// 3.执行插入adminMapper.insert(admin);
}
处理唯一约束
创建自定义异常类
package com.atguigu.crowd.exception;/*** Copyright (C) 2024 - 2024 Jasonakeke, Inc. All Rights Reserved** @Desc : 保存或者更新 Admin 时,如果检测到登录账号重复则抛出这个异常* @Time : 2024/12/1 22:05* @Author : Code_By_Jasonakeke* @Email : 2284037977@qq.com* @Class : LoginAcctAlreadyInUseException* @IDE : IntelliJ IDEA*/
public class LoginAcctAlreadyInUseException extends RuntimeException {private static final long serialVersionUID = 1L;public LoginAcctAlreadyInUseException(String message) {super(message);}public LoginAcctAlreadyInUseException(String message, Throwable cause) {super(message, cause);}public LoginAcctAlreadyInUseException(Throwable cause) {super(cause);}public LoginAcctAlreadyInUseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {super(message, cause, enableSuppression, writableStackTrace);}public LoginAcctAlreadyInUseException() {}
}
在异常处理器类添加方法
@ExceptionHandler(LoginAcctAlreadyInUseException.class)
public ModelAndView resolveLoginAcctAlreadyInUseException(LoginAcctAlreadyInUseException exception, HttpServletRequest request, HttpServletResponse response) throws IOException {String viewName = "admin-add";return commonResolve(viewName, exception, request, response);
}
修改 Service 方法
public void saveAdmin(Admin admin) {// 1.密码加密String userPswd = admin.getUserPswd();userPswd = CrowdUtil.md5(userPswd);admin.setUserPswd(userPswd);// 2.生成创建时间Date date = new Date();SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String createTime = format.format(date);admin.setCreateTime(createTime);// 3.执行插入try {adminMapper.insert(admin);} catch (Exception e) {e.printStackTrace();logger.info("异常全类名:{}", e.getClass().getName());if (e instanceof DuplicateKeyException) {throw new LoginAcctAlreadyInUseException(CrowdConstant.MESSAGE_LOGIN_ACCT_ALREADY_IN_USE);}}
}
更新
目标
修改现有 Admin 的数据,不修改密码和创建时间
思路
回显表单
调整铅笔按钮
<%-- <button type="button" class="btn btn-primary btn-xs">
<i class=" glyphicon glyphicon-pencil"></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>
Handler 方法
@RequestMapping("admin/to/edit/page.html")
public String toEditPage(@RequestParam("adminId") Integer adminId,ModelMap modelMap
){// 1.根据 adminId 查询 Admin 对象Admin admin = adminService.getAdminById(adminId);// 2.将 Admin 对象存入模型modelMap.addAttribute("admin", admin);return "admin-edit";
}
准备页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html><%@include file="/WEB-INF/include-head.jsp" %><body><%@include file="/WEB-INF/include-nav.jsp" %><div class="container-fluid"><div class="row"><%@include file="/WEB-INF/include-sidebar.jsp" %><div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"><ol class="breadcrumb"><li><a href="/admin/to/main/page.html">首页</a></li><li><a href="/admin/get/page.html">数据列表</a></li><li class="active">更新</li></ol><div class="panel panel-default"><div class="panel-heading">表单数据<div style="float:right;cursor:pointer;" data-toggle="modal" data-target="#myModal"><i class="glyphicon glyphicon-question-sign"></i></div></div><div class="panel-body"><form action="admin/update.html" method="post" role="form"><input type="hidden" name="id" value="${requestScope.admin.id}"><input type="hidden" name="pageNum" value="${param.pageNum}"><input type="hidden" name="keyword" value="${param.keyword}"><p>${requestScope.exception.message}</p><div class="form-group"><label for="inputLoginAcct">登录账号</label><input type="text" class="form-control" id="inputLoginAcct" name="loginAcct" value="${requestScope.admin.loginAcct}"placeholder="请输入登录账号"></div><div class="form-group"><label for="inputUserName">用户昵称</label><input type="text" class="form-control" id="inputUserName" name="userName" value="${requestScope.admin.userName}"placeholder="请输入用户昵称"></div><div class="form-group"><label for="inputEmail">邮箱地址</label><input type="email" class="form-control" id="inputEmail" name="email" value="${requestScope.admin.email}"placeholder="请输入邮箱地址"><p class="help-block label label-warning">请输入合法的邮箱地址, 格式为:xxxx@xxxx.com</p></div><button type="submit" class="btn btn-success"><i class="glyphicon glyphicon-edit"></i>更新</button><button type="reset" class="btn btn-danger"><i class="glyphicon glyphicon-refresh"></i>重置</button></form></div></div></div></div></div></body>
</html>
Service 方法
@Override
public Admin getAdminById(Integer adminId) {return adminMapper.selectByPrimaryKey(adminId);
}
执行更新
Handler 方法
]
@RequestMapping("admin/update.html")
public String update(Admin admin,@RequestParam("pageNum") Integer pageNum,@RequestParam("keyword") String keyword){adminService.update(admin);return "redirect:/admin/get/page.html?pageNum=" + pageNum + "&keyword=" + keyword;
}
Service 方法
@Override
public void update(Admin admin) {try {// Selective:表示有选择的更新,对于 null 值的字段不更新adminMapper.updateByPrimaryKeySelective(admin);} catch (Exception e) {e.printStackTrace();logger.info("异常全类名:{}", e.getClass().getName());if (e instanceof DuplicateKeyException) {throw new LoginAcctAlreadyInUseException(CrowdConstant.MESSAGE_LOGIN_ACCT_ALREADY_IN_USE);}}
}
创建自定义异常
package com.atguigu.crowd.exception;/*** Copyright (C) 2024 - 2024 Jasonakeke, Inc. All Rights Reserved** @Desc :* @Time : 2024/12/1 23:12* @Author : Code_By_Jasonakeke* @Email : 2284037977@qq.com* @Class : LoginAcctAlreadyInUseForUpdateException* @IDE : IntelliJ IDEA*/
public class LoginAcctAlreadyInUseForUpdateException extends RuntimeException {private static final long serialVersionUID = 1L;public LoginAcctAlreadyInUseForUpdateException() {}public LoginAcctAlreadyInUseForUpdateException(String message) {super(message);}public LoginAcctAlreadyInUseForUpdateException(String message, Throwable cause) {super(message, cause);}public LoginAcctAlreadyInUseForUpdateException(Throwable cause) {super(cause);}public LoginAcctAlreadyInUseForUpdateException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {super(message, cause, enableSuppression, writableStackTrace);}
}
在异常处理器类添加方法
@ExceptionHandler(LoginAcctAlreadyInUseForUpdateException.class)
public ModelAndView resolveLoginAcctAlreadyInUseForUpdateException(LoginAcctAlreadyInUseForUpdateException exception, HttpServletRequest request, HttpServletResponse response) throws IOException {String viewName = "system-error";return commonResolve(viewName, exception, request, response);
}
修改 Service 方法
@Override
public void update(Admin admin) {try {// Selective:表示有选择的更新,对于 null 值的字段不更新adminMapper.updateByPrimaryKeySelective(admin);} catch (Exception e) {e.printStackTrace();logger.info("异常全类名:{}", e.getClass().getName());if (e instanceof DuplicateKeyException) {throw new LoginAcctAlreadyInUseForUpdateException(CrowdConstant.MESSAGE_LOGIN_ACCT_ALREADY_IN_USE);}}
}