JavaWeb 四层架构模型实现品牌增删改查
Posted 岑_先生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaWeb 四层架构模型实现品牌增删改查相关的知识,希望对你有一定的参考价值。
JavaWeb 四层架构模型实现品牌增删改查
1.四层架构的模型图
2.步骤:
1.创建数据库(创建表)
2.导入需要的jar包(导入WebContent目录下的WEB-INF下的bin目录里面)
自行去网上找(一般老师会给)
3.创建包:com.cg.entity(装实体类)
com.cg.dao(装dao类)
com.cg.service(装service类)
com.cg.servlet(装servlet类)
com.cg.util(装工具类)
4.创建JDBC工具类(使用Druid数据库连接池)
5.配置数据库及连接池参数(放于src下)
6.实体类层:用来封装属性及其get set方法 toString方法,有参构造方法,无参构造方法等。
7.Dao层:对数据库的增删改查方法的封装(操作数据库)也是属于业务逻辑
8.Servlet(Controller):流程控制
9.Service:处理业务逻辑
10.jsp(View)页面(位于WebContent目录下)
3.代码:
1.创建数据库及表(tb_brand)
图片:
2.jar包的导入(导入WebContent目录下的WEB-INF下的bin目录里面)
3.包的创建(创建在src下)
我们用的druid所以我们同时也需把druid文件放入src下
代码片
.
1.工具类(位于com.cg.util包下)
创建JDBCUtil类
package com.cg.util;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.*;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
/**
* JDBC工具类(使用Druid数据库连接池)
*
*/
public class JDBCUtil
// 数据库连接池
private static DataSource ds;
// 静态代码块,用户初始化类,只会执行一次
static
try
// 加载druid.properties属性文件
Properties properties = new Properties();
properties.load(JDBCUtil.class.getClassLoader().getResourceAsStream("druid.properties"));
// 获取数据库连接池对象
ds = DruidDataSourceFactory.createDataSource(properties);
catch (Exception e)
e.printStackTrace();
// 获取工具类实例
public static JDBCUtil getInstance()
return new JDBCUtil();
// 数据库连接
private Connection connection;
// statement对象,执行SQL
private PreparedStatement preparedStatement;
// 查询结果集
private ResultSet resultSet;
/**
* 从数据库连接池中获取连接,因为是内部使用,所以设为private不暴露给外界使用
*
* @return 从数据库连接池中获取连接
*/
private Connection getConnection()
connection = null;
try
connection = ds.getConnection();
catch (SQLException e)
e.printStackTrace();
return connection;
/**
* 释放数据库资源
*/
public void close()
// 释放数据库资源
try
if (resultSet != null)
resultSet.close();
if (preparedStatement != null)
preparedStatement.close();
if (connection != null)
connection.close();
catch (SQLException e)
e.printStackTrace();
/**
* 执行增删改sql语句
*
* @param sql
* SQL语句,其中使用?占位符表示参数
* @param params
* SQL中需要的参数值,需要按照SQL中?的顺序,将参数值放入List中
* @return 是否执行成功
*/
public boolean update(String sql, List<Object> params)
// 获取连接
getConnection();
int rows = 0;
if (connection != null)
try
// 获取PreparedStatement对象
preparedStatement = connection.prepareStatement(sql);
// 循环设置参数
for (int i = 0; i < params.size(); i++)
preparedStatement.setObject(i + 1, params.get(i));
// 这一步非常必要,帮助我们查看执行的sql到底是什么样!用于解决问题
guessPreparedSql(sql, params);
// 执行SQL语句
rows = preparedStatement.executeUpdate();
catch (SQLException e)
e.printStackTrace();
return rows > 0;
/**
* 执行select语句
*
* @param sql
* SQL语句,其中使用?占位符表示参数
* @param params
* SQL中需要的参数值,需要按照SQL中?的顺序,将参数值放入List中
* @return 查询结果集
*/
public ResultSet query(String sql, List<Object> params)
// 获取连接
getConnection();
if (connection != null)
try
// 获取PreparedStatement对象
preparedStatement = connection.prepareStatement(sql);
// 循环设置参数
for (int i = 0; i < params.size(); i++)
preparedStatement.setObject(i + 1, params.get(i));
// 这一步非常必要,帮助我们查看执行的sql到底是什么样!
guessPreparedSql(sql, params);
// 执行sql,并获取结果集
resultSet = preparedStatement.executeQuery();
catch (SQLException e)
e.printStackTrace();
// 需要返回ResultSet需要提供给外部使用
return resultSet;
/**
* 执行无参select语句
*
* @param sql
* SQL语句
* @return 结果集
*/
public ResultSet query(String sql)
return query(sql, Collections.emptyList());
/**
* 获得PreparedStatement向数据库提交的SQL语句
*
* @param sql
* @param params
* @return
*/
private String guessPreparedSql(String sql, List<Object> params)
int paramNum = 0;
// 如果参数集不为空,取得其长度
if (null != params)
paramNum = params.size();
// 如果没有参数,说明不是动态SQL语句,直接返回原sql
if (1 > paramNum)
System.out.println(LocalDateTime.now() + "\\tprepared sql: " + sql);
return sql;
// 如果有参数,则是动态SQL语句,需要构造并返回新sql
StringBuffer returnSQL = new StringBuffer();
String[] subSQL = sql.split("\\\\?");
// 开始循环替换问号为参数值
for (int i = 0; i < paramNum; i++)
Object value = params.get(i);
if (value == null)
System.err.printf("第 %d 个参数的值为 null %n", i + 1);
returnSQL.append(subSQL[i]).append(value);
continue;
if (value instanceof Number)
// 数值不需要引号
returnSQL.append(subSQL[i]).append(value);
else
// 非数值需要引号,遇到特殊字符将其转义输出
String str = value.toString().replaceAll("(['\\\\\\\\])", "\\\\\\\\$1");
returnSQL.append(subSQL[i]).append(" '").append(str).append("' ");
// 如果问号不是原sql的最后一个字符,则将改问号后的部分添加到returnSQL中
if (subSQL.length > params.size())
returnSQL.append(subSQL[subSQL.length - 1]);
String formatSql = returnSQL.toString();
System.out.println(LocalDateTime.now() + "\\tprepared sql: " + formatSql);
return formatSql;
5.配置数据库及连接池参数。
druid.properties:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb
username=root //这里是你自己Navicat的用户名
password=123456 //这里是你自己Navicat的秘密
#初始化连接数量
initialSize=5
#最大连接数
maxActive=10
#最大等待时间
maxWait=3000
代码片
.
6.实体类层(位于com.cg.entity包下)
用来封装属性及其get set方法 toString方法,有参构造方法,无参构造方法等。
创建一个Brand类
package com.cg.entity;
import com.sun.xml.internal.bind.v2.model.core.ID;
/**
* 品牌实体类
* @author user
*
*/
public class Brand
private int id;
private String brandName;
private String companyName;
private int ordered;
private String description;
private int status;
public Brand()
super();
public Brand(int id, String brandName, String companyName, int ordered, String description, int status)
this.id = id;
this.brandName = brandName;
this.companyName = companyName;
this.ordered = ordered;
this.description = description;
this.status = status;
public int getId()
return id;
public String getBrandName()
return brandName;
public String getCompanyName()
return companyName;
public int getOrdered()
return ordered;
public String getDescription()
return description;
public int getStatus()
return status;
public void setId(int id)
this.id = id;
public void setBrandName(String brandName)
this.brandName = brandName;
public void setCompanyName(String companyName)
this.companyName = companyName;
public void setOrdered(int ordered)
this.ordered = ordered;
public void setDescription(String description)
this.description = description;
public void setStatus(int status)
this.status = status;
7.创建Dao类(位于com.cg.dao包下)
(1)创建BrandDao(里面有增加、查找、修改、删除)
package com.cg.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.cg.entity.Brand;
import com.cg.util.JDBCUtil;
import com.mysql.jdbc.Util;
/**
* 品牌数据访问类
* @author user
*
*/
public class BrandDao
public List<Brand> selectList()
try
//获取实例
JDBCUtil util = JDBCUtil.getInstance();
String sql = "select * from tb_brand";
ResultSet rs = util.query(sql);
//数据封装到一个集合
List<Brand> brands = new ArrayList<Brand>();
//遍历集合
while(rs.next())
//数据
int id = rs.getInt("id");
String brandName = rs.getString("brand_name");
String companyName = rs.getString("company_name");
int ordered = rs.getInt("ordered");
String des = rs.getString("description");
int status = rs.getInt("status");
Brand brand = new Brand();
//数据放入到brand中
brand.setBrandName(brandName);
brand.setCompanyName(companyName);
brand.setOrdered(ordered);
brand.setId(id);
brand.setDescription(des);
brand.setStatus(status);
//品牌放到集合里面
brands.add(brand);
util.close();
return brands;//把品牌返回出去
catch (Exception e)
e.printStackTrace();
return null;
/**
* 新增品牌
* @param brand 品牌信息
* @return 是否成功
*/
public boolean insertBrand(Brand brand)
JDBCUtil util = JDBCUtil.getInstance();//获取实例
String sql = "insert into tb_brand(brand_name,company_name,ordered,description,status)"
+"values(?,?,?,?,?)";
//实例化一个集合
List<Object> params = new ArrayList<>();
//放参数
params.add(brand.getBrandName());
params.add(brand.getCompanyName());
params.add(brand.getOrdered());
params.add(brand.getDescription());
params.add(brand.getStatus());
boolean flag = util.update(sql, params);
util.close();
return flag;
/**
* 根据id查询品牌
* @param id 主键id
* @return 品牌信息
*/
public Brand getBrandById(int id)
JDBCUtil util = JDBCUtil.getInstance();
try
String sql= "select * from tb_brand where id=?";
//为参数赋值
List<Object> params = new ArrayList<>();
params.add(id);
//执行
ResultSet rs = util.query(sql,params);
if(rs.next())
//数据
String brandName = rs.getString("brand_name");
String companyName = rs.getString("company_name");
int ordered = rs.getInt("ordered");
String des = rs.getString("description");
int status = rs.getInt("status");
Brand brand = new Brand();
//数据放入到brand中
brand.setBrandName(brandName);
brand.setCompanyName(companyName);
brand.setOrdered(ordered);
brand.setId(id);
brand.setDescription(des);
brand.setStatus(status);
return brand;
else
return null;
catch 前言
本文是基于单体架构实现的角色的增删改查的功能。前台使用Bootstrap+Ajax+Jsp , 后端使用Spring+SpringMvc+MyBatis进行开发,相信使用过这些技术的小伙伴应该很好的理解以下的内容,也希望看了这篇文章小伙伴们有所收获。
准备工作
后端技术
技术
说明
官网
Spring
Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。
https://spring.io/
SpringMvc
MVC框架
https://spring.io/projects/spring-boot
MyBatis
持久层框架
https://mybatis.org/mybatis-3/zh/index.html
Druid
数据库连接池
https://github.com/alibaba/druid
log4j
日志框架
https://logging.apache.org/log4j/2.x/
前端技术
Bootstrap
简洁、直观、强悍的前端开发框架
https://www.bootcss.com/
Ajax
前后端交互
https://www.w3school.com.cn/ajax/index.asp
Jsp
模板引擎
https://www.runoob.com/jsp/jsp-intro.html
layer.js
消息提示
http://www.h-ui.net/lib/layer.js.shtml
Modal插件
模态框(Modal)是覆盖在父窗体上的子窗体
https://www.runoob.com/bootstrap/bootstrap-modal-plugin.html
jquery.pagination.js
分页插件
http://www.jq22.com/yanshi5697/
角色维护-分页实现
分页前端功能实现
创建外部JavaScript源文件,role.js
在页面 role-page.jsp引入role.js文件
<script type="text/javascript" src="script/my-role.js"></script>
初始化全局函数
分页实现初始化全局函数,每页的条数,页码,模糊查询的关键词
//初始化全局变量
function initGlobalVariable() {
window.pageSize = 5; //每页的条数
window.pageNum = 1; //页码
window.keyword = ""; //关键词
}
声明分页函数
//给服务器发送请求获取分页数据(pageInfo),并在页面上显示分页效果(主体、页码导航条)
function showPage() {
// 给服务器发送请求获取分页数据:PageInfo
var pageInfo = getPageInfo();
// 在页面上的表格中tbody标签内显示分页的主体数据
generateTableBody(pageInfo);
// 在页面上的表格中tfoot标签内显示分页的页码导航条
initPagination(pageInfo);
}
获取分页数据
function getPageInfo() {
// 以同步请求方式调用$.ajax()函数并获取返回值(返回值包含全部响应数据)
var ajaxResult = $.ajax({
"url": "role/search/by/keyword.action",
"type": "post",
"data": {
"pageNum": (window.pageNum == undefined) ? 1 : window.pageNum,
"pageSize": (window.pageSize == undefined) ? 5 : window.pageSize,
"keyword": (window.keyword == undefined) ? "" : window.keyword
},
"dataType": "json",
"async": false // 为了保证getPageInfo()函数能够在Ajax请求拿到响应后获取PageInfo,需要设置为同步操作
});
// 从全部响应数据中获取JSON格式的响应体数据
var resultEntity = ajaxResult.responseJSON;
// 从响应体数据中获取result,判断当前请求是否成功
var result = resultEntity.result;
// 如果成功获取PageInfo
if (result == "SUCCESS") {
return resultEntity.data;
}
if (result == "FAILED") {
layer.msg(resultEntity.message);
}
return null;
}
使用PageInfo数据在tbody标签内显示分页数据
function generateTableBody(pageInfo) {
// 执行所有操作前先清空
$("#roleTableBody").empty(); //这个对应页面的 <tbody id="roleTableBody"> </tbody>
// 获取数据集合
var list = pageInfo.list;
// 判断list是否有效
if (list == null || list.length == 0) {
$("#roleTableBody").append("<tr><td colspan=‘4‘ style=‘text-align:center;‘>没有查询到数据!</td></tr>");
return;
}
for (var i = 0; i < list.length; i++) {
var role = list[i];
var checkBtn = "<button type=‘button‘ class=‘btn btn-success btn-xs‘><i class=‘ glyphicon glyphicon-check‘></i></button>";
var pencilBtn = "<button type=‘button‘ id=‘roleTableBody‘ roleid=‘" + role.id + "‘ class=‘btn btn-primary btn-xs editBtn‘><i class=‘ glyphicon glyphicon-pencil‘></i></button>";
var removeBtn = "<button type=‘button‘ roleid=‘" + role.id + "‘ class=‘btn btn-danger btn-xs removeBtn‘><i class=‘ glyphicon glyphicon-remove‘></i></button>";
var numberTd = "<td>" + (i + 1) + "</td>";
var checkBoxTd = "<td><input class=‘itemBox‘ roleid=‘" + role.id + "‘ type=‘checkbox‘></td>";
var roleNameTd = "<td>" + role.name + "</td>";
var btnTd = "<td>" + checkBtn + " " + pencilBtn + " " + removeBtn + "</td>";
var tr = "<tr>" + numberTd + checkBoxTd + roleNameTd + btnTd + "</tr>";
// 将前面拼好的HTML代码追加到#roleTableBody中
$("#roleTableBody").append(tr);
}
}
声明函数封装导航条初始化操作
function initPagination(pageInfo) {
// 声明变量存储分页导航条显示时的属性设置
var paginationProperties = {
num_edge_entries: 3, //边缘页数
num_display_entries: 5, //主体页数
callback: pageselectCallback, //回调函数
items_per_page: window.pageSize, //每页显示数据数量,就是pageSize
current_page: (window.pageNum - 1),//当前页页码
prev_text: "上一页", //上一页文本
next_text: "下一页" //下一页文本
};
// 显示分页导航条 <div id="Pagination" class="pagination"> <!-- 这里显示分页 --> </div>
$("#Pagination").pagination(pageInfo.total, paginationProperties);
}
在每一次点击“上一页”、“下一页”、“页码”时执行这个函数跳转页面
function pageselectCallback(pageIndex, jq) {
// 将全局变量中的pageNum修改为最新值
// pageIndex从0开始,pageNum从1开始
window.pageNum = pageIndex + 1;
// 调用分页函数重新执行分页
showPage();
return false;
}
页面初始化,就是我们点击角色维护页面需要加载的内容
$(function(){
// 调用分页参数初始化方法
initGlobalVariable();
// 执行分页
showPage();
});
关键词查询功能
在点击“查询”按钮后,获取文本框中填写的keyword值,赋值给全局变量keyword,调用showPage()函数即可。
//关键字查询实现
$("#searchBtn").click(function () {
//获取关键字查询的值
var keywordInput = $.trim($("#keywordInput").val());
/*if (keywordInput==null || keywordInput==""){
layer.msg("请输入关键词");
return;
}*/
window.keyword = keywordInput;
//执行查询操作
showPage();
});
分页后端实现
点击角色维护加载页面数据两种思路:
第一种是我们请求后台把查询到的数据放到Model,前台遍历把数据展示出来。
第二种是我们请求后台把查询到的数据当PageInfo,然后动态的拼接把数据展示到页面上。(我们采用第二种)
Controller方法的实现
@ResponseBody
@RequestMapping("/role/search/by/keyword")
public ResultEntity<PageInfo<Role>> search(
@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "5") Integer pageSize,
@RequestParam(value = "keyword", defaultValue = "") String keyword) {
// 1.查询得到PageInfo对象
PageInfo<Role> pageInfo = roleService.queryForKeywordWithPage(pageNum, pageSize, keyword);
// 2.封装结果对象返回
return ResultEntity.successWithData(pageInfo);
}
Service方法的实现
public PageInfo<Role> queryForKeywordWithPage(Integer pageNum, Integer pageSize, String keyword) {
// 1.开启分页功能
PageHelper.startPage(pageNum, pageSize);
// 2.执行查询
List<Role> list = roleMapper.selectForKeywordSearch(keyword);
// 3.封装为PageInfo对象
return new PageInfo<Role>(list);
}
Mapper方法的实现
List<Role> selectForKeywordSearch(String keyword);
Mapper.xml
<select id="selectForKeywordSearch" resultMap="BaseResultMap">
SELECT
id,
`name`
FROM
t_role
WHERE
`name` LIKE CONCAT(‘%‘, #{keyword}, ‘%‘)
</select>
角色维护-全选功能
功能在页面的位置
具体实现
标记
role-page.jsp
<thead>
<tr>
<th width="30">#</th>
<th width="30"><input id="summaryBox" type="checkbox"></th>
<th>名称</th>
<th width="100">操作</th>
</tr>
</thead>
my-role.js
for (var i = 0; i < list.length; i++) {
//省略
var checkBoxTd = "<td><input class=‘itemBox‘ roleid=‘" + role.id + "‘ type=‘checkbox‘></td>";
//省略
}
给summaryBox绑定单击响应函数
//全选/全不选功能实现
$("#summaryBox").click(function () {
//获取当前的选中状态
var currentStatus = this.checked;
$(".itemBox").prop("checked", currentStatus);
});
角色维护-批量删除
准备模态框
先准备模态框的HTML标签,include-modal-role-confirm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<div id="confirmModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title">角色维护删除</h4>
</div>
<div class="modal-body">
<p>您确定要删除下面的显示的内容吗?</p>
<table class="table table-bordered">
<thead>
<tr>
<th width="30">#</th>
<th>名称</th>
</tr>
</thead>
<tbody id="confirmModalTableBody"></tbody>
</table>
</div>
<div class="modal-footer">
<button id="confirmModalBtn" type="button" class="btn btn-primary">OK</button>
</div>
</div>
</div>
</div>
在role-page.jsp中包含include-modal-role-confirm.jsp文件, <%@ include file="/WEB-INF/include-modal-role-confirm.jsp" %>
getRoleListByRoleIdArray()函数
//id查询角色信息
function getRoleListByRoleIdArray(roleIdArray) {
//roleIdArray转换成JSON字符串
var roleIds = JSON.stringify(roleIdArray);
var ajaxResult = $.ajax({
"url": "role/get/list/by/id/list.action",
"type": "post",
"data": roleIds,
"contentType": "application/json;charset=UTF-8",
"dataType": "json",
"async": false
});
// 3.获取JSON对象类型的响应体
var resultEntity = ajaxResult.responseJSON;
var result = resultEntity.result;
if (result == "SUCCESS") {
// 5.如果成功,则返回roleList
return resultEntity.data;
}
if (result == "FAILED") {
layer.msg(resultEntity.message);
return null;
}
return null;
}
对应的后端代码:
@ResponseBody
@RequestMapping("role/get/list/by/id/list")
public ResultEntity<List<Role>> getRoleListByIdList(@RequestBody List<Integer> roleIds) {
List<Role> roleList = roleService.findRoleListByIdList(roleIds);
return ResultEntity.successWithData(roleList);
}
public List<Role> findRoleListByIdList(List<Integer> roleIds) {
return roleMapper.findRoleListByIdList(roleIds);
}
showRemoveConfirmModal()函数
// 打开删除确认模态框
function showRemoveConfirmModal() {
// 1.将模态框显示出来
$("#confirmModal").modal("show");
//获取角色数据
var roleList = getRoleListByRoleIdArray(window.roleIdArray);
//清空表格数据
$("#confirmModalTableBody").empty();
//填充confirmModalTableBody的数据
for (var i = 0; i < roleList.length; i++) {
// 5.获取角色相关数据
var role = roleList[i];
var id = role.id;
var name = role.name;
var trHTML = "<tr><td>" + (i+1) + "</td><td>" + name + "</td></tr>";
// 6.执行填充
$("#confirmModalTableBody").append(trHTML);
}
}
点击批量删除按钮绑定单击响应函数
标记批量删除按钮
<button type="button" class="btn btn-danger" id="batchRemoveBtn"
style="float: right; margin-left: 10px;">
<i class=" glyphicon glyphicon-remove"></i> 删除
</button>
检查itemBox是否被选中
// 给批量删除按钮绑定单击响应函数
$("#batchRemoveBtn").click(function () {
//获取被选中的itemBox数组长度
var length = $(".itemBox:checked").length;
if (length == 0) {
layer.msg("请选择要删除的记录!!");
return;
}
// 未完待续...
});
在弹出的模态框中显示confirm信息
// 给批量删除按钮绑定单击响应函数
$("#batchRemoveBtn").click(function () {
//获取被选中的itemBox数组长度
var length = $(".itemBox:checked").length;
if (length == 0) {
layer.msg("请选择要删除的记录!!");
return;
}
window.roleIdArray = new Array();
//遍历复选框
$(".itemBox:checked").each(function () {
//通过checkbox的roleid属性获取roleId值
var roleId = $(this).attr("roleid");
//存入数组
window.roleIdArray.push(roleId);
});
// 调用函数打开模态框
showRemoveConfirmModal();
});
点击模态框的OK按钮执行删除
标记OK按
<button **id="confirmModalBtn"** type="button" class="btn btn-primary">OK</button>
绑定单击响应函数
$("#confirmModalBtn").click(function () {
//数组转成Json
var roleIds = JSON.stringify(window.roleIdArray);
var ajaxResult = $.ajax({
"url": "role/batch/remove.action",
"type": "post",
"data": roleIds,
"contentType": "application/json;charset=UTF-8",
"dataType": "json",
"async": false,
"success": function (response) {
var result = response.result;
if (result == "SUCCESS") {
layer.msg("操作成功!");
// 如果删除成功,则重新调用分页方法
showPage();
}
if (result == "FAILED") {
layer.msg(response.message);
}
// 不管成功还是失败,都需要关掉模态框
$("#confirmModal").modal("hide");
},
"error": function (response) {
if (result == "FAILED") {
layer.msg(response.message);
}
}
});
});
后端代码
@ResponseBody
@RequestMapping(value = "role/batch/remove")
public ResultEntity<String> batchAdminList(@RequestBody List<Integer> roleIds) {
try {
roleService.batchRoleList(roleIds);
return ResultEntity.successWithoutData();
} catch (Exception e) {
return ResultEntity.failed(null, e.getMessage());
}
}
public void batchRoleList(List<Integer> roleIds) {
roleMapper.batchRoleList(roleIds);
}
<delete id="batchRoleList" parameterType="java.util.List">
delete from t_role where id in
<foreach collection="list" item="item" open="(" separator="," close=")" >
#{item}
</foreach>
</delete>
角色维护-新增
大体步骤
给“新增”按钮绑定单击响应函数
打开模态框
给“保存”按钮绑定单击响应函数
收集文本框内容
发送请求
请求处理完成关闭模态框、重新分页、清理表单
给新增按钮绑定单击响应函数
标记新增按钮
<button type="button" class="btn btn-primary" id="addBtn"
style="float: right;">
<i class="glyphicon glyphicon-plus"></i> 新增
</button>
绑定单击响应函数
$("#addBtn").click(function(){ alert("aaa..."); });
准备模态框
先准备模态框的HTML代码,include-modal-role-add.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<div id="addModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<form role="form">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title">角色添加</h4>
</div>
<div class="modal-body">
<input type="text" id="roleNameInput" class="form-control" placeholder="请输入角色名称" />
</div>
<div class="modal-footer">
<button type="button" id="addModalBtn" class="btn btn-default"><i class="glyphicon glyphicon-plus"></i> 保存</button>
<button type="reset" class="btn btn-primary"><i class="glyphicon glyphicon-refresh"></i> 重置</button>
</div>
</form>
</div>
</div>
</div>
将include-modal-role-add.jsp包含到role-page.jsp , <%@ include file="/WEB-INF/include-modal-role-add.jsp" %>
打开模态框
$("#addBtn").click(function(){ $("#addModal").modal("show"); });
给“保存”按钮绑定单击响应函数
标记“保存”按钮
<button id="addModalBtn" type="button" class="btn btn-success"> <i class="glyphicon glyphicon-plus"></i>保存 </button>
绑定单击响应函数
$("#addModalBtn").click(function () {
// 1.收集文本框内容
var roleName = $.trim($("#roleNameInput").val());
if (roleName == null || roleName == "") {
layer.msg("请输入有效角色名称!");
return;
}
// 2.发送请求
$.ajax({
"url": "role/save/role.action",
"type": "post",
"data": {
"roleName": roleName
},
"dataType": "json",
"success": function (response) {
var result = response.result;
if (result == "SUCCESS") {
layer.msg("操作成功!");
// 3.操作成功重新分页
// 前往最后一页
window.pageNum = 999999;
showPage();
}
if (result == "FAILED") {
layer.msg(response.message);
}
// 4.不管成功还是失败,关闭模态框
$("#addModal").modal("hide");
// 5.清理本次在文本框填写的数据
$("#roleNameInput").val("");
},
"error": function (response) {
layer.msg(response.message);
}
});
});
后端部分代码
@ResponseBody
@RequestMapping("role/save/role")
public ResultEntity<String> saveRole(@RequestParam("roleName") String roleName) {
try {
roleService.saveRole(roleName);
return ResultEntity.successWithoutData();
} catch (Exception e) {
return ResultEntity.failed(null, e.getMessage());
}
}
公共返回代码
public class ResultEntity<T> {
public static final String SUCCESS = "SUCCESS";
public static final String FAILED = "FAILED";
public static final String NO_MESSAGE = "NO_MESSAGE";
public static final String NO_DATA = "NO_DATA";
// 方便返回成功结果(不携带查询结果情况)
public static ResultEntity<String> successWithoutData() {
return new ResultEntity<String>(SUCCESS, NO_MESSAGE, NO_DATA);
}
// 方便返回成功结果(携带查询结果情况)
public static <E> ResultEntity<E> successWithData(E data) {
return new ResultEntity<E>(SUCCESS, NO_MESSAGE, data);
}
// 方便返回失败结果
public static <E> ResultEntity<E> failed(E data, String message) {
return new ResultEntity<E>(FAILED, message, data);
}
private String result;
private String message;
private T data;
public ResultEntity() {
}
public ResultEntity(String result, String message, T data) {
super();
this.result = result;
this.message = message;
this.data = data;
}
@Override
public String toString() {
return "ResultEntity [result=" + result + ", message=" + message + ", data=" + data + "]";
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
角色维护-更新
大体步骤
给编辑按钮绑定单击响应函数
打开模态框
- ? 准备模态框
- ? 把roleId保存到全局变量
- ? 获取到当前按钮所在行的roleName
- ? 使用roleName回显模态框中的表单
- ? 给“更新”按钮绑定单击响应函数
- ? 收集文本框内容
- ? 发送请求
- ? 请求处理完成关闭模态框、重新分页
给编辑按钮绑定单击响应函数
标记编辑按钮
my-role.js文件
function generateTableBody(pageInfo) {
//省略
var pencilBtn = "<button type=‘button‘ roleid=‘" + role.id + "‘ class=‘btn btn-primary btn-xs editBtn‘><i class=‘ glyphicon glyphicon-pencil‘></i></button>";
//省略
}
}
准备模态框
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<div id="editModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<form role="form">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title">尚筹网系统弹窗</h4>
</div>
<div class="modal-body">
<input type="text" id="roleNameInputEdit" class="form-control"
placeholder="请输入角色名称" />
</div>
<div class="modal-footer">
<button id="editModalBtn" type="button" class="btn btn-warning">
<i class="glyphicon glyphicon-edit"></i> 更新
</button>
<button type="reset" class="btn btn-primary">
<i class="glyphicon glyphicon-refresh"></i> 重置
</button> </div>
</form>
</div>
</div>
</div>
将include-modal-role-add.jsp包含到role-page.jsp , <%@ include file="/WEB-INF/include-modal-role-edit.jsp" %>
绑定单击响应函数
$("#roleTableBody").on("click", ".editBtn", function () {
// 1.获取当前按钮的roleId
window.roleId = $(this).attr("roleId");
// 2.获取当前按钮所在行的roleName
var roleName = $(this).parents("tr").children("td:eq(2)").text();
// 3.修改模态框中文本框的value值,目的是在显示roleName
$("#roleNameInputEdit").val(roleName);
// 4.打开模态框
$("#editModal").modal("show");
});
给“更新”按钮绑定单击响应函数
$("#editModalBtn").click(function () {
// 1.获取文本框值
var roleName = $.trim($("#roleNameInputEdit").val());
if (roleName == null || roleName == "") {
layer.msg("请输入有效角色名称!");
return;
}
// 2.发送请求
$.ajax({
"url": "role/update.action",
"type": "post",
"data": {
"id": window.roleId,
"name": roleName
},
"dataType": "json",
"success": function (response) {
var result = response.result;
if (result == "SUCCESS") {
layer.msg("操作成功!");
// 3.操作成功重新分页
showPage();
}
if (result == "FAILED") {
layer.msg(response.message);
}
// 4.不管成功还是失败,关闭模态框
$("#editModal").modal("hide");
}
});
});
后端部分代码
@ResponseBody
@RequestMapping("role/update")
public ResultEntity<String> updateRole(@RequestParam("id") Integer id,
@RequestParam("name") String name) {
Role role = new Role();
role.setId(id);
role.setName(name);
try {
roleService.updateRole(role);
return ResultEntity.successWithoutData();
} catch (Exception e) {
return ResultEntity.failed(null, e.getMessage());
}
}
异常映射兼容异步请求
问题表现
Ajax请求在服务器端处理过程中抛出异常,经过异常处理器:
@ControllerAdvice
public class CrowdFundingExceptionResolever {
@ExceptionHandler(value=Exception.class)
public ModelAndView catchException(Exception exception) {
ModelAndView mav = new ModelAndView();
mav.addObject("exception", exception);
mav.setViewName("system-error");
return mav;
}
}
目前这个异常处理机制,只能返回页面,而不能针对Ajax请求返回JSON格式的响应数据。所以Ajax请求处理过程中,如果抛出异常,返回异常信息页面,Ajax程序无法正常解析,导致页面不能正常显示和工作,也不能给出友好的错误提示。
问题解决思路
异步请求特点
分辨异步请求的工具方法
/**
* 用于判断一个请求是否是异步请求
* @param request
* @return
*/
public static boolean checkAsyncRequest(HttpServletRequest request) {
// 1.获取相应请求消息头
String accept = request.getHeader("Accept");
String xRequested = request.getHeader("X-Requested-With");
// 2.判断请求消息头数据中是否包含目标特征
if(
(stringEffective(accept) && accept.contains("application/json"))
||
(stringEffective(xRequested) && xRequested.contains("XMLHttpRequest")) ) {
return true;
}
return false;
}
/**
* 判断字符串是否有效
* @param source 待验证字符串
* @return true表示有效,false表示无效
*/
public static boolean stringEffective(String source) {
return source != null && source.length() > 0;
}
升级后的异常处理器
首先引入:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
@ControllerAdvice
public class CrowdFundingExceptionResolever {
@ExceptionHandler(value = Exception.class)
public ModelAndView catchException(
Exception exception,
HttpServletRequest request,
HttpServletResponse response) throws IOException {
// 1.对当前请求进行检查
boolean checkAsyncRequestResult = CrowdFundingUtils.checkAsyncRequest(request);
// 2.如果是异步请求
if(checkAsyncRequestResult) {
// 根据异常类型在常量中的映射,使用比较友好的文字显示错误提示消息
String exceptionClassName = exception.getClass().getName();
String message = CrowdFundingConstant.EXCEPTION_MESSAGE_MAP.get(exceptionClassName);
if(message == null) {
message = "系统未知错误";
}
// 3.创建ResultEntity对象
ResultEntity<String> resultEntity = ResultEntity.failed(ResultEntity.NO_DATA, message);
// 4.将resultEntity转换为JSON格式
Gson gson = new Gson();
String json = gson.toJson(resultEntity);
// 5.将json作为响应数据返回给浏览器
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(json);
return null;
}
ModelAndView mav = new ModelAndView();
mav.addObject("exception", exception);
mav.setViewName("system-error");
return mav;
}
}
常量类
public class CrowdFundingConstant {
public static final Map<String, String> EXCEPTION_MESSAGE_MAP = new HashMap<String, String>();
static {
EXCEPTION_MESSAGE_MAP.put("java.lang.ArithmeticException", "系统在进行数学运算时发生错误");
EXCEPTION_MESSAGE_MAP.put("java.lang.RuntimeException", "系统在运行时发生错误");
EXCEPTION_MESSAGE_MAP.put("com.atguigu.crowd.funding.exception.LoginException", "登录过程中运行错误");
}
}
我是阿福,公众号「阿福聊编程」作者,对后端技术保持学习爱好者,我会经常更新JAVA技术文章,在进阶的路上,共勉!