角色和菜单功能
一、角色功能
接下来我们可以完成角色管理的增删改查操作
1. Bean对象
创建sys_role
对应的实体对象SysRole
@Data
public class SysRole {
private Integer id;
private String name;
private String notes;
private Date createTime;
}
2. Dao层
现在我们就可以在Dao层创建涉及相关的数据库操作的方法。
public interface IRoleDao {
/**
* 查询所有的用户信息
* @return
*/
public List<SysRole> list(SysRole entity);
/**
* 分页查询的方法
* @param pageUtils 分页数据
* @return
*/
public List<SysRole> listPage(PageUtils pageUtils);
public int save(SysRole entity);
public SysRole findById(int id);
public int updateById(SysRole entity);
int deleteById(int id);
int count(PageUtils pageUtils);
}
然后是具体的实现
public class RoleDaoImpl implements IRoleDao {
@Override
public List<SysRole> list(SysRole entity) {
QueryRunner queryRunner = MyDbUtils.getQueryRunner();
String sql = "select * from sys_role ";
try {
List<SysRole> list = queryRunner.query(sql, new ResultSetHandler<List<SysRole>>() {
@Override
public List<SysRole> handle(ResultSet resultSet) throws SQLException {
// 存储返回结果的容器
List<SysRole> list = new ArrayList<>();
while(resultSet.next()){
// 每次循环一次 user 存储一条数据
SysRole entity = new SysRole();
entity.setId(resultSet.getInt("id"));
entity.setName(resultSet.getString("name"));
entity.setNotes(resultSet.getString("notes"));
entity.setCreateTime(resultSet.getDate("create_time"));
list.add(entity); // 把查询的记录封装到了集合容器中
}
return list; // 返回查询的结果
}
});
return list;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
@Override
public List<SysRole> listPage(PageUtils pageUtils) {
QueryRunner queryRunner = MyDbUtils.getQueryRunner();
String sql = "select * from sys_role limit ?,?";
if(StringUtils.isNotEmpty(pageUtils.getKey())){
// 需要带条件查询
sql = "select * from sys_role where name like '%"+pageUtils.getKey()+"%' or notes like '%"+pageUtils.getKey()+"%' limit ?,?";
}
// 计算 分页开始的位置
int startNo = pageUtils.getStart();
try {
// BeanListHandler 会自动的帮助我们完成字段和属性的映射。前提是属性和字段完全一直
// 此处不会通过驼峰命名法 装换
// List<SysUser> list = queryRunner.query(sql, new BeanListHandler<SysUser>(SysUser.class));
List<SysRole> list = queryRunner.query(sql, new ResultSetHandler<List<SysRole>>() {
@Override
public List<SysRole> handle(ResultSet resultSet) throws SQLException {
// 存储返回结果的容器
List<SysRole> list = new ArrayList<>();
while(resultSet.next()){
// 每次循环一次 user 存储一条数据
SysRole entity = new SysRole();
entity.setId(resultSet.getInt("id"));
entity.setName(resultSet.getString("name"));
entity.setNotes(resultSet.getString("notes"));
entity.setCreateTime(resultSet.getDate("create_time"));
list.add(entity); // 把查询的记录封装到了集合容器中
}
return list; // 返回查询的结果
}
},startNo,pageUtils.getPageSize());
return list;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
@Override
public int save(SysRole entity) {
QueryRunner queryRunner = MyDbUtils.getQueryRunner();
String sql = "insert into sys_role(name,notes) values(?,?)";
try {
return queryRunner.update(sql,entity.getName(),entity.getNotes());
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return 0;
}
@Override
public SysRole findById(int id) {
QueryRunner queryRunner = MyDbUtils.getQueryRunner();
String sql = "select * from sys_role where id = ? ";
try {
return queryRunner.query(sql, new ResultSetHandler<SysRole>() {
@Override
public SysRole handle(ResultSet resultSet) throws SQLException {
// 存储返回结果的容器
if(resultSet.next()){
// 每次循环一次 user 存储一条数据
SysRole entity = new SysRole();
entity.setId(resultSet.getInt("id"));
entity.setName(resultSet.getString("name"));
entity.setNotes(resultSet.getString("notes"));
entity.setCreateTime(resultSet.getDate("create_time"));
return entity;
}
return null; // 返回查询的结果
}
},id);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
@Override
public int updateById(SysRole entity) {
QueryRunner queryRunner = MyDbUtils.getQueryRunner();
String sql = "update sys_role set name = ? ,notes=? where id = ?";
try {
return queryRunner.update(sql,entity.getName(),entity.getNotes(),entity.getId());
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return 0;
}
@Override
public int deleteById(int id) {
QueryRunner queryRunner = MyDbUtils.getQueryRunner();
String sql = "delete from sys_role where id = ?";
try {
return queryRunner.update(sql,id);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return 0;
}
@Override
public int count(PageUtils pageUtils) {
QueryRunner queryRunner = MyDbUtils.getQueryRunner();
String sql = "select count(1) from sys_role ";
if(StringUtils.isNotEmpty(pageUtils.getKey())){
sql = "select count(1) from sys_role where username like '%"+pageUtils.getKey()+"%' or nickname like '%"+pageUtils.getKey()+"%' ";
}
try {
return queryRunner.query(sql, new ResultSetHandler<Integer>() {
@Override
public Integer handle(ResultSet resultSet) throws SQLException {
resultSet.next();
return resultSet.getInt(1);
}
});
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return 0;
}
}
3. Service层
添加角色管理的Service
相关的代码
public interface IRoleService {
public List<SysRole> list(SysRole entity);
public int save(SysRole entity);
public SysRole findById(int id);
public int updateById(SysRole entity);
int deleteById(int id);
void listPage(PageUtils pageUtils);
int count(PageUtils pageUtils);
}
具体接口对应的实现类
public class RoleServiceImpl implements IRoleService {
private IRoleDao dao = new RoleDaoImpl();
@Override
public List<SysRole> list(SysRole entity) {
return dao.list(entity);
}
@Override
public int save(SysRole entity) {
return dao.save(entity);
}
@Override
public SysRole findById(int id) {
return dao.findById(id);
}
@Override
public int updateById(SysRole entity) {
return dao.updateById(entity);
}
@Override
public int deleteById(int id) {
return dao.deleteById(id);
}
@Override
public void listPage(PageUtils pageUtils) {
// 查询分页的数据
List<SysRole> list = dao.listPage(pageUtils);
// 查询 满足查询条件的记录数
int count = dao.count(pageUtils);
// 封装分页的数据
pageUtils.setList(list);
pageUtils.setTotalCount(count);
}
@Override
public int count(PageUtils pageUtils) {
return dao.count(pageUtils);
}
}
4. Servlet层
RoleServlet
需要继承前面定义的BaseServlet
,然后重写定义的抽象方法。同时要重写list
方法。具体如下:
@WebServlet(name = "roleServlet",urlPatterns = {"/sys/roleServlet"})
public class RoleServlet extends BaseServlet{
private IRoleService service = new RoleServiceImpl();
@Override
public void list(HttpServletRequest req, HttpServletResponse resp) throws Exception {
super.list(req, resp); // 封装了分页的相关的操作
// TODO 写我们自己的查询处理
service.listPage(pageUtils);
req.setAttribute(Constant.LIST_PAGE_UTILS,pageUtils);
// 页面跳转
req.getRequestDispatcher("/sys/role/list.jsp").forward(req,resp);
}
@Override
public void saveOrUpdatePage(HttpServletRequest req, HttpServletResponse resp) throws Exception {
}
@Override
public void saveOrUpdate(HttpServletRequest req, HttpServletResponse resp) throws Exception {
}
@Override
public void remove(HttpServletRequest req, HttpServletResponse resp) throws Exception {
}
@Override
public void findById(HttpServletRequest req, HttpServletResponse resp) throws Exception {
}
}
5. 角色查询
展示角色数据,那么我们需要做的操作:
- 修改
main.jsp
中的跳转地址 - 修改
/sys/role/list.jsp
中的访问地址和table数据
6. 添加和更新
添加和删除功能是类似的。我们一块来实现,在Servlet中完善进入添加更新页面的方法和保存和更新的方法的逻辑
@Override
public void saveOrUpdatePage(HttpServletRequest req, HttpServletResponse resp) throws Exception {
// 如果是更新。需要查询出当前的信息
String id = req.getParameter("id");
if(StringUtils.isNotEmpty(id)){
// 说明是更新
SysRole entity = service.findById(Integer.parseInt(id));
req.setAttribute("entity",entity);
}
req.getRequestDispatcher("/sys/role/addOrUpdate.jsp").forward(req,resp);
}
@Override
public void saveOrUpdate(HttpServletRequest req, HttpServletResponse resp) throws Exception {
// 获取提交的数据
String id = req.getParameter("id");
String name = req.getParameter("name");
String notes = req.getParameter("notes");
SysRole entity = new SysRole();
entity.setNotes(notes);
entity.setName(name);
if(StringUtils.isNotEmpty(id)){
// 更新
entity.setId(Integer.parseInt(id));
service.updateById(entity);
}else {
// 保存数据
service.save(entity);
}
// 做重定向查询
resp.sendRedirect("/sys/roleServlet?action=list");
}
然后就是对应的页面处理
<form class="form-horizontal m-t" id="signupForm" action="/sys/roleServlet?action=saveOrUpdate" method="post">
<input type="hidden" name="id" value="${entity.id}">
<div class="form-group">
<label class="col-sm-3 control-label">名称:</label>
<div class="col-sm-8">
<input id="name" name="name" class="form-control" value="${entity.name}"
type="text" aria-required="true" aria-invalid="true" class="error">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">描述:</label>
<div class="col-sm-8">
<input id="notes" name="notes" class="form-control" value="${entity.notes}"
type="textarea" aria-required="true" aria-invalid="false" class="valid">
</div>
</div>
<div class="form-group">
<div class="col-sm-8 col-sm-offset-3">
<button class="btn btn-default" οnclick="resetPage()" type="button">取消</button>
<button class="btn btn-primary" type="submit">提交</button>
</div>
</div>
</form>
这样就OK了
7.删除角色
删除角色我们需要做一个判断。也就是已经分配给用户的角色是不能被删除的。所以删除操作的时候我们需要添加这样一个判断
@Override
public void remove(HttpServletRequest req, HttpServletResponse resp) throws Exception {
// 删除角色信息
String id = req.getParameter("id");
// 删除角色信息 我们需要做校验 如果该角色已经分配给了用户。那么这个角色就不能被删除
boolean flag = service.checkRoleDispatch(Integer.parseInt(id));
PrintWriter writer = resp.getWriter();
if(flag){
// 表示没有被分配,可以删除
service.deleteById(Integer.parseInt(id));
writer.write("ok");
}else{
// 表示不能被删除
writer.write("error");
}
writer.flush();
writer.close();
}
service中的处理
@Override
public boolean checkRoleDispatch(int roleId) {
SysUser entity = new SysUser();
entity.setRoleId(roleId);
return userService.list(entity).size() == 0 ?true:false;
}
这个是在UserService
的基础上做的扩展,实现了方法的复用。
在前端页面的处理上。加了条件判断。
function removeData(id) {
swal({
title: "您确定要删除这条信息吗",
text: "删除后将无法恢复,请谨慎操作!",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "删除",
closeOnConfirm: false
}, function () {
$.get("/sys/roleServlet?action=remove&id=" + id, function (msg) {
if(msg === 'ok'){
// 表示删除成功
// 再发起一个查询的操作
window.location.href = "/sys/roleServlet?action=list"
}else{
// 表示不能被删除
swal("删除失败!", "该角色已经被分配, 不能删除!!!。", "warning");
}
})
});
}
二、菜单功能
1.查询功能
系统左侧菜单栏我们需要做成动态的。那么就需要维护相关的菜单数据。所以设计了sys_menu
这张表。具体的实现。如下,先定义SysMenu
这个实体
@Data
public class SysMenu {
private Integer id;
private String name;
private String url;
private Integer parentId;
private int seq;
private Date createTime;
}
然后维护DAO
,Service
和Servlet
的逻辑。
@WebServlet(name = "menuServlet",urlPatterns = {"/sys/menuServlet"})
public class MenuServlet extends BaseServlet{
private IMenuService service = new MenuServiceImpl();
/**
* 菜单功能不做分页
* @param req
* @param resp
* @throws Exception
*/
@Override
public void list(HttpServletRequest req, HttpServletResponse resp) throws Exception {
List<SysMenu> list = service.list(null);
req.setAttribute("list",list);
req.getRequestDispatcher("/sys/menu/list.jsp").forward(req,resp);
}
@Override
public void saveOrUpdatePage(HttpServletRequest req, HttpServletResponse resp) throws Exception {
}
@Override
public void saveOrUpdate(HttpServletRequest req, HttpServletResponse resp) throws Exception {
}
@Override
public void remove(HttpServletRequest req, HttpServletResponse resp) throws Exception {
}
@Override
public void findById(HttpServletRequest req, HttpServletResponse resp) throws Exception {
}
}
然后页面的展示处理。注意main.jsp
的菜单地址和/sys/menu/list.jsp
页面的调整,我们在此处没有做分页的处理操作。
2.添加和更新
添加和更新处理很类似。我们一并的实现。添加和更新我们已经在用户管理
和角色管理
中已经实现了。所以在此处的难度就降低了很多。步骤一样
- 进入登录页面:需要准备相关的数据(根据Id查询信息和查询所有的父菜单信息)
- 提交表单数据:后端服务获取数据后做添加和更新的操作
在此处需要注意的是:父菜单分配功能,需要使用到下拉菜单
对应的代码
<div class="form-group">
<label class="col-sm-3 control-label">父菜单:</label>
<div class="col-sm-8">
<select class="form-control m-b" name="parentId">
<option value="-1">---本身就是父菜单---</option>
<c:forEach items="${parents}" var="parent">
<option value="${parent.id}" ${parent.id == entity.parentId?'selected':''}>${parent.name}</option>
</c:forEach>
</select>
</div>
</div>
表单的提交功能。后端处理数据
@Override
public void saveOrUpdate(HttpServletRequest req, HttpServletResponse resp) throws Exception {
// 获取表单提交的数据
String id = req.getParameter("id");
String name = req.getParameter("name");
String url = req.getParameter("url");
String seq = req.getParameter("seq");
String parentId = req.getParameter("parentId");
SysMenu menu = new SysMenu();
menu.setName(name);
menu.setUrl(url);
if(StringUtils.isNotEmpty(seq)){
menu.setSeq(Integer.parseInt(seq));
}
if(StringUtils.isNotEmpty(parentId)){
menu.setParentId(Integer.parseInt(parentId));
}
if(StringUtils.isNotEmpty(id)){
// 更新
menu.setId(Integer.parseInt(id));
service.updateById(menu);
}else{
// 添加
service.save(menu);
}
resp.sendRedirect("/sys/menuServlet?action=list");
}
功能搞定
3.菜单数据展示
菜单数据有父子菜单的关系。所以在展示数据的时候需要体现这种关系。我们可以通过双重循环的方式来实现。效果如下:
同时我们可以通过序号来控制菜单的显示的顺序。关键是在查询的时候通过seq
升序查询
4.删除菜单
删除菜单本身很简单。但是我们要考虑父子菜单的关系和菜单被分配给角色的情况。那么有些情况是不能被删除的。所以我们在Servlet中需要做相关的判断校验
@Override
public void remove(HttpServletRequest req, HttpServletResponse resp) throws Exception {
int id = Integer.parseInt(req.getParameter("id"));
// 删除菜单的判断
// 1.菜单如果分配给了角色就不能被删除
boolean flag = service.isDispatcher(id);
String msg = "ok";
if(flag){
// 表示已经被分配了
msg = "error";
}else{
SysMenu entity = service.findById(id);
// 2.需要删除的菜单是父菜单
if(entity.getParentId() == -1){
// 有子菜单的父菜单不能被删除 -- 判断是否有子菜单
flag = service.haveSubMenu(id);
if(flag){
// 有子菜单
msg = "error";
}else{
// 父菜单没有子菜单 可以删除
service.deleteById(id);
}
}else{
// 3.子菜单 可以被删除
service.deleteById(id);
}
}
PrintWriter writer = resp.getWriter();
writer.write(msg);
writer.flush();
}
三、动态绑定
实现用户和角色的绑定以及角色和菜单的绑定。实现整个系统动态功能分配管理的效果。
1.角色和菜单
角色和菜单是多对多的关联关系。所以我们通过sys_role_menu
来维护他们之间的关联关系。我们在更新角色信息的时候来维护菜单信息。
需要注意的地方,在jsp页面中展示数据注意样式
在展示数据的时候我们在进入更新页面前需要对菜单数据做处理
- 查询所以的菜单信息
- 对当前角色具有的菜单需要标识
保存更新数据的逻辑。针对菜单我们的步骤是:
- 先删除该角色的所有菜单
- 新增分配的菜单信息
2.用户和角色
用户和角色是一对一的关联关系,那么这块我们就可以在添加和更新用户的时候直接分配角色信息,这块我们操作的内容:
- 进入更新/添加界面前需要查询所有的角色信息
- 在更新/添加界面中我们需要添加一个下拉菜单来处理分配功能
- 表单数据提交到后台Servlet中我们需要处理角色相关的数据。同时调整前面写的JDBC的方法
然后对应的表单代码
<div class="form-group">
<label class="col-sm-3 control-label">分配角色:</label>
<div class="col-sm-8">
<select class="form-control m-b" name="roleId">
<c:forEach items="${requestScope.roles}" var="role">
<option value="${role.id}" ${role.id == entity.roleId?'selected':''}>${role.name}</option>
</c:forEach>
</select>
</div>
</div>
后端的处理代码