Day-10综合案例2

web综合案例二

1. 项目开发流程

#. 开发流程
1. 提出需求
		产品经理/项目经理
		需求分析工程师
2. 开发
		前端工程师
		后端工程师
        美工
        数据库管理员(DBA) 
        
3. 测试
		测试工程师
		
4. 上线
		运维工程师

2. 开发从何入手

#问
1. 前端干什么
		发起请求,接收响应,显示数据
2. 后端干什么
		接收请求,处理业务,响应数据
		
#答
1. 前端: 
	1). 发起请求
		a. 请求地址
		b. 请求参数
2. 后端
	1). 接收请求
		a. 指定请求地址
		b. 获取请求参数
	2). 业务处理
		数据库
    3). 响应数据
    	指定响应数据格式
3. 前端
	1). 接收响应
		解析响应数据
	2). 数据显示到页面上(最终目的)

3.需求

# 三个模块
    1. 用户管理
    2. 角色管理
    3. 权限管理
# 需求
	0. 每个模块的功能一致(以下以'用户管理'模块为例)
	1. 查询用户功能(分页,搜索)
	2. 新增用户功能
	3. 删除用户功能
	4. 修改用户功能	
# 要求
	1. 根据提供的素材完成以上3个模块的功能 (用户管理,其他两个模块类似)
	2. 资料里有完整代码,大家先自己写,实在想不出来再看
# 提示
	1. elementUI不懂的可以查阅官方文档
		https://element.eleme.cn/#/zh-CN/component/installation
	2. 注意每个功能背后的多表联动	

4.技术架构

1598815852659

5.数据库设计

5.1数据表设计分析

用户_角色_权限经典五张表

# 表和表关系说明
1. 三张表
	a. 用户表 t_user
	b. 角色表 t_role
	c. 权限表 t_permission
2. 表与表之间的关系
	a. 用户表和角色表 : 多对多
		一个用户可以拥有多个角色 : 一个qq用户可以申请绿钻(qq音乐),黄钻(qq空间),粉钻(qq炫舞)
		一个角色可以拥有多个用户 : 一个黄钻角色, 可以有张三,李四...用户
	b. 角色表和权限表 : 多对多
    	一个角色可以拥有多个权限 : 一个黄钻角色可以拥有 隐身访问(A权限),查看被挡访客(B权限)
    	一个权限可以拥有多个角色 : A权限 拥有 黄钻角色, 超级钻
   # 用户和权限没有直接关系,通过角色产生关系
3. 另外两张表   
		多对多表关系建立: 用一张中间表,引用两张主表的主键作为外键(联合主键)
	a. 用户角色中间表
	b. 角色权限中间表
	
    	

1599442125145

5.2创建表的sql语句

-- 1.用户表t_user
CREATE TABLE `t_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT, -- 用户编号
  `username` varchar(32) DEFAULT NULL,	-- 用户名字
  `password` varchar(32) DEFAULT NULL,	-- 用户密码
  `remark` varchar(32) DEFAULT NULL,	-- 用户备注
  `email` varchar(32) DEFAULT NULL,		-- 用户邮箱
  `createTime` timestamp not NULL DEFAULT CURRENT_TIMESTAMP, -- 该用户创建时间
  `updateTime` timestamp not NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- 该用户修改时间
  PRIMARY KEY (`id`) -- 设置主键
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_user
-- ----------------------------

insert into t_user values(null,'admin','admin',null,'admin@163.com',null,null)
,(null,'zhansan','123',null,'zhansan@163.com',null,null);

-- 2.角色表t_role
CREATE TABLE `t_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT, -- 角色id
  `name` varchar(32) DEFAULT NULL,	-- 角色名字
  `keyword` varchar(64) DEFAULT NULL,	-- 角色关键字
  `description` varchar(128) DEFAULT NULL,	-- 角色描述
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_role
-- ----------------------------
INSERT INTO `t_role` VALUES ('1', '管理员', 'ROLE_ADMIN', '这是管理员')
,('2', '会员', 'ROLE_MEMBER', '这是会员')
,('3', '游客', 'ROLE_VISITOR', '这是游客');

-- 3.权限表
CREATE TABLE `t_permission` (
  `id` int(11) NOT NULL AUTO_INCREMENT, -- 权限编号
  `name` varchar(32) DEFAULT NULL,		-- 权限名字
  `keyword` varchar(64) DEFAULT NULL,	-- 权限关键字
  `description` varchar(128) DEFAULT NULL,	-- 权限描述
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_permission
-- ----------------------------
INSERT INTO `t_permission` VALUES ('1', '新增', 'ITEM_ADD', '这是新增权限')
,('2', '删除', 'ITEM_DELETE', '这是删除权限')
,('3', '编辑', 'ITEM_EDIT', '这是编辑权限')
,('4', '查询', 'ITEM_QUERY', '这是查询权限');

-- 4.用户角色中间表
CREATE TABLE `t_user_role` (
  `user_id` int(11) NOT NULL, 
  `role_id` int(11) NOT NULL,
  PRIMARY KEY (`user_id`,`role_id`),
  KEY `FK_Reference_8` (`role_id`),
  CONSTRAINT `FK_Reference_7` FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`),
  CONSTRAINT `FK_Reference_8` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_user_role
-- ----------------------------
INSERT INTO `t_user_role` VALUES ('1', '1'),('2', '2');


-- 5.角色权限中间表
CREATE TABLE `t_role_permission` (
  `role_id` int(11) NOT NULL,
  `permission_id` int(11) NOT NULL,
  PRIMARY KEY (`role_id`,`permission_id`),
  KEY `FK_Reference_12` (`permission_id`),
  CONSTRAINT `FK_Reference_11` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`),
  CONSTRAINT `FK_Reference_12` FOREIGN KEY (`permission_id`) REFERENCES `t_permission` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_role_permission
-- ----------------------------
INSERT INTO `t_role_permission` 
	VALUES ('1', '1'),('1', '2'),('1', '3'),('1', '4'),('2', '3'),('2', '4'),('3', '4');

5.3创建好表之间的关系

image-20201001155428288

6.导入maven项目

将下发的素材中的web-case2的maven项目导入idea中。

image-20201001160258713

image-20201001160453847

image-20201001160757002

3.修改当前maven工程的本地仓库位置:

image-20201001161002845

image-20201001161248183

4.启动tomcat访问用户角色权限管理系统主页

image-20201001161550236

image-20201001161608474

7.BaseServlet编写

​ 1.问题:
​ 一个请求对应一个servlet,所以当请求过多的时候,servlet太多了.
​ 解决思路:
​ 一个servlet,要能够处理多个请求

​ 2.一个servlet如何处理多个请求?
​ 发送请求时,多个请求的访问地址是同一个servlet,但是传递不同的methodName参数,用来区分请求的类 型。

​ 例如:

请求地址:
	http://localhost:8080/userServlet?methodName=findAllUsers
	http://localhost:8080/userServlet?methodName=deleteUserById
后台使用一个userServlet可以通过methodName获取value,来执行后台具体的方法:到底是执行findAllUsers还是deleteUserById   

代码演示如下:

浏览器地址栏输入的地址:

image-20201001214830832

image-20201001214810921

package com.itheima.case2.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/user")
public class UserServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取请求的参数
        String methodName = request.getParameter("methodName");
        //2.根据获取的参数来判断执行下面哪个方法
        if("findAllUsers".equals(methodName)){
            //调用查询所有用户方法
            findAllUsers(request,response);
        }else if("deleteUserById".equals(methodName)){
            //调用根据id删除用户的方法
            deleteUserById(request,response);
        }
    }

    /*
        页面的请求url:http://localhost:8080/user?methodName=findAllUsers
        说明:findAllUsers需要携带参数request和response的原因是我们需要在findAllUsers
        方法体中进行处理请求和响应代码,也就是说之前写在doGet或者doPost方法体中的代码
        现在需要书写在这个方法体中
     */
    public void findAllUsers(HttpServletRequest request,HttpServletResponse response){
        System.out.println("查询用户");
    }

    /*
        页面的请求url:http://localhost:8080/user?methodName=deleteUserById
     */
    public void deleteUserById(HttpServletRequest request,HttpServletResponse response){
        System.out.println("删除用户");
    }
}

3.一个servlet处理的请求过多,那么if判断太过繁琐.而且每个servlet都需要判断.并且if判断条件处将方法名写死了。

​ 如何优化?

​ 目的:根据methodName找到对应的方法,然后执行这个方法。

​ 反射机制

package com.itheima.case2.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

@WebServlet("/user")
public class UserServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取请求的参数
        String methodName = request.getParameter("methodName");
        //2.根据获取的参数来判断执行下面哪个方法
        /*if("findAllUsers".equals(methodName)){
            //调用查询所有用户方法
            findAllUsers(request,response);
        }else if("deleteUserById".equals(methodName)){
            //调用根据id删除用户的方法
            deleteUserById(request,response);
        }*/
        /*
            使用反射机制执行浏览器的请求方法,那么首先应该获取Class对象,获取Class对象有三种方式。我们这里使用this.getClass()方式
            ,this表示当前servlet类的对象,当前servlet类由tomcat创建的
         */
        //获取Class类的对象
        Class clazz = this.getClass();
        /*
            要执行的方法的方法名就是前台获取的methodName,并且每个servlet中的方法都需要参数request和response对象,
            所以我们使用clazz对象调用方法获取请求要执行的方法.
            public Method getMethod(String name, Class<?>... parameterTypes)
                    参数:
                        name 表示方法名
                        parameterTypes:表示参数类型,属于Class类型
         */
        try {
            Method m = clazz.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
            /*
                执行获取到的方法,方法需要两个对象作为参数:request response
                public Object invoke(Object obj, Object... args)
                    参数:
                        obj:调用方法的对象 this,this表示当前servlet类的对象,当前servlet类由tomcat创建的
                        args:执行的方法需要的实参  request,response
             */
            m.invoke(this, request, response);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /*
        页面的请求url:http://localhost:8080/user?methodName=findAllUsers
        说明:findAllUsers需要携带参数request和response的原因是我们需要在findAllUsers
        方法体中进行处理请求和响应代码,也就是说之前写在doGet或者doPost方法体中的代码
        现在需要书写在这个方法体中
     */
    public void findAllUsers(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("查询用户");
    }


    /*
        页面的请求url:http://localhost:8080/user?methodName=deleteUserById
     */
    public void deleteUserById(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("删除用户");
    }
}

4.上述使用反射技术执行指定请求的方法的方式不仅仅适用于用户请求,可以适合于任意模块请求,例如权限以及角色模块,所以我们可以将上述反射技术的代码抽取到一个单独的servlet中,这样以后所有的servlet只需要继承这个servlet,并定义具体请求的方法即可。

我们将书写反射技术的代码的servlet称为BaseServlet

package com.itheima.case2.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

@WebServlet("/BaseServlet")
public class BaseServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取请求的参数
        String methodName = request.getParameter("methodName");
        //2.根据获取的参数来判断执行下面哪个方法
        /*if("findAllUsers".equals(methodName)){
            //调用查询所有用户方法
            findAllUsers(request,response);
        }else if("deleteUserById".equals(methodName)){
            //调用根据id删除用户的方法
            deleteUserById(request,response);
        }*/
        /*
            使用反射机制执行浏览器的请求方法,那么首先应该获取Class对象,获取Class对象有三种方式。我们这里使用this.getClass()方式
            ,this表示当前servlet类的对象,当前servlet类由tomcat创建的
         */
        //获取Class类的对象
        Class clazz = this.getClass();
        /*
            要执行的方法的方法名就是前台获取的methodName,并且每个servlet中的方法都需要参数request和response对象,
            所以我们使用clazz对象调用方法获取请求要执行的方法.
            public Method getMethod(String name, Class<?>... parameterTypes)
                    参数:
                        name 表示方法名
                        parameterTypes:表示参数类型,属于Class类型
         */
        try {
            Method m = clazz.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
            /*
                执行获取到的方法,方法需要两个对象作为参数:request response
                public Object invoke(Object obj, Object... args)
                    参数:
                        obj:调用方法的对象 this,this表示当前servlet类的对象,当前servlet类由tomcat创建的
                        args:执行的方法需要的实参  request,response
             */
            m.invoke(this, request, response);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

5.所有的servlet都要去继承BaseServlet

package com.itheima.case2.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

@WebServlet("/user")
public class UserServlet extends BaseServlet {
    /*
        页面的请求url:http://localhost:8080/user?methodName=findAllUsers
        说明:findAllUsers需要携带参数request和response的原因是我们需要在findAllUsers
        方法体中进行处理请求和响应代码,也就是说之前写在doGet或者doPost方法体中的代码
        现在需要书写在这个方法体中
     */
    public void findAllUsers(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("查询用户");
    }


    /*
        页面的请求url:http://localhost:8080/user?methodName=deleteUserById
     */
    public void deleteUserById(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("删除用户");
    }
}

8.用户查询

8.1需求分析

image-20201001224706183

image-20201105202617390

说明;

​ 1.显示用户数据在页面上我们需要使用分页,所以我们在后台需要一个PageBean类

​ 2.通过上述分析我们了解需要查询t_user表的用户信息以及t_role角色表的角色信息。这两张表需要使用第三张表t_user_role产生关联进行多表查询。

根据之前学习的mybatis框架,多表查询需要在实体类中产生对对应的关系。

由于我们查询用户信息,然后在查询角色信息,所以我们应该在User实体类中定义List集合存储多个角色。

image-20201105202752016

8.2流程分析

image-20201105203115652

补充:

在实际开发中:

pojo: plain old java object java中老的对象,实体类。正常实体类都放到pojo包中。对应数据表

vo:view object. 存放前端需要的数据的实体类。

8.3前端代码

说明:查询用户信息的前端页面是userList.html。我们需要使用vue的ajax技术向后台发送请求获取数据然后响应给前端页面,就是先将数据准备好,视图还没有加载好,我们可以使用vue的生命周期函数created完成ajax的请求。

image-20201002154107189

说明:response.data表示后台返回的PageBean对象的json格式。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>用户管理系统</title>
    <link rel="stylesheet" href="element-ui/lib/theme-chalk/index.css">
    <script src="js/vue.js"></script>
    <script src="element-ui/lib/index.js"></script>
    <script src="js/axios-0.18.0.js"></script>

    <link rel="stylesheet" href="css/style.css">
</head>
<body>
    <div id="div">
            <div class="content-header common-search-border1">
                <el-button class="butAdd" @click="handleCreate"> 新增用户
                </el-button>
            </div>

        <div class="app-container">
            <div class="box common-search-border2">
                <div class="filter-container">
                    <!-- TODO: 搜索栏 -->
                    <!--
                        :inline="true	行内表单模式 (表单项横向排列)
                        :model="queryParams"  数据绑定
                        ref="queryParams" 用于找到本表单使用
                    -->
                    <el-form :inline="true" :model="queryParams" ref="queryParams"
                             class="formInfo">
                        <el-form-item label="用户名称" label-width="80px">
                            <el-input placeholder="请输入" v-model="queryParams.username"
                                      style="width: 200px;" class="filter-item"></el-input>
                        </el-form-item>
                        <el-form-item>
                            <el-button @click="handleRest">清除</el-button>
                            <el-button type="primary" class="butOK" @click="handleSearch">搜索</el-button>
                        </el-form-item>
                    </el-form>
                </div>
            </div>
            <div class="box common-table-border" style="margin:8px 0 0;">
                <!--
                    TODO: 用户列表
                -->
                <!--
                    data	显示的数据
                    fit	列的宽度是否自撑开
                -->
                <el-table
                        :data="userList"
                        fit
                        style="width: 100%;">

                    <el-table-column label="编号" width="50px" align="center" prop="id">
                    </el-table-column>
                    <el-table-column label="用户名" align="center" prop="username">
                    </el-table-column>
                    <el-table-column label="邮箱" align="center"  prop="email">
                    </el-table-column>
                    <el-table-column label="创建日期" align="center" prop="createTime">
                    </el-table-column>
                    <el-table-column label="修改日期" align="center" prop="updateTime">
                    </el-table-column>

                    <el-table-column
                            label="角色"
                            width="120"
                            align="center">
                        <!--
                           slot-scope = "scope"
                               用于指定当前的数据(本案例是userList中的当前行元素)
                       -->
                        <template slot-scope="scope">
                            <!--
                                悬浮提示信息展示框
                                    trigger = "hover" 鼠标移上去触发
                                    placement="left" 悬浮框显示位置在左边

                            -->
                            <el-popover v-for="role in scope.row.roles" trigger="hover" placement="left">
                                <!-- 前两个p标签是提示信息, div标签是展示信息 -->
                                <p style="font-size: 10px;color: blue">角色编号: {{ role.id }}</p>
                                <p style="font-size: 10px;color: blue">角色描述: {{ role.description }}</p>
                                <div slot="reference" class="name-wrapper">
                                    <el-tag size="medium">{{ role.name }}</el-tag>
                                </div>
                            </el-popover>
                        </template>
                    </el-table-column>
                    <el-table-column label="操作" width="180">
                        <template slot-scope="scope">
                            <!--
                                 TODO: 修改和删除
                             -->
                            <div class="operation" v-if="scope.row.username != 'admin'">
                                <el-button type="primary" size="mini" @click="handleUpdate(scope.row)">修改
                                </el-button>
                                <el-button size="mini" type="danger" @click="handleDeleted(scope.row)"> 删除
                                </el-button>
                            </div>
                        </template>
                    </el-table-column>

                </el-table>
                <!--
                     TODO: 分页组件
                       @size-change: 当改变每页条数时触发的函数
                       @current-change:当改变页码时触发的函数
                       current-page :默认的页码
                       :page-sizes:每页条数选择框中显示的值
                       :page-size : 默认的每页条数
                       layout: 分页组件的布局
                           total(总条数), sizes(每页条数), prev(上一页), pager(所有的页码), next(下一页), jumper(跳转页码)
                       :total: 总条数
                 -->
                <div class="pagination-container">
                    <el-pagination
                            class="pagiantion"
                            v-show="pagination.total>0"
                            @size-change="handleSizeChange"
                            @current-change="handleCurrentChange"
                            :current-page="pagination.pageNum"
                            :page-sizes="[3, 5, 10, 15]"
                            :page-size="pagination.pageSize"
                            layout="total, sizes, prev, pager, next, jumper"
                            :total="pagination.total">
                    </el-pagination>
                </div>
            </div>
            <!--TODO: 新增用户弹层
                   :visible.sync 是否显示 (dialogFormVisible=true 显示, =false隐藏)
            -->
            <el-dialog title="新增用户" align="left" :visible.sync="dialogFormVisible" width="40%" top="8vh">
                <!--
                    model	表单数据对象
                    rules	表单验证规则
                -->
                <el-form label-width="120px" :model="user" :rules="rules" ref="form">
                    <!--
                    prop	表单域 model 字段,在使用 validate、resetFields 方法的情况下,该属性是必填的
                    -->
                    <el-form-item label="用户名称:" prop="username">
                            <!-- v-model 双向数据绑定  autocomplete=off 取消自动补全功能-->
                        <el-input v-model="user.username" autocomplete="off"></el-input>
                    </el-form-item>
                    <el-form-item label="邮箱:" prop="email">
                        <el-input v-model="user.email" autocomplete="off"></el-input>
                    </el-form-item>
                    <el-form-item label="密码:" prop="password">
                        <el-input type="password" v-model="user.password" autocomplete="off"></el-input>
                    </el-form-item>
                    <!--
                        TODO: 动态获取角色
                    -->
                    <el-form-item label="角色: " prop="roleIds">
                        <!--
                         value / v-model	绑定值 (双向数据绑定)
                            multiple	是否多选
                        -->
                        <el-select v-model="user.roleIds" multiple placeholder="请选择(默认游客)">
                            <!--
                                value	选项的值	string/number/object
                                label	选项的标签,若不设置则默认与 value 相同 (显示出来的标签名)
                                key     文档中没有说明, 就是取值跟value相同,删除也不影响本组件使用
                            -->
                            <el-option
                                    v-for="role in roleList"
                                    :key="role.id"
                                    :label="role.name"
                                    :value="role.id">
                            </el-option>
                        </el-select>
                    </el-form-item>

                    <el-form-item label="备注:" prop="remark">
                        <el-input v-model="user.remark" autocomplete="off"></el-input>
                    </el-form-item>
                </el-form>
                <div slot="footer" class="dialog-footer">
                    <el-button @click="handleCreateCancel">取 消</el-button>
                    <el-button type="primary" @click="handleCreateConfirm">确 定</el-button>
                </div>
            </el-dialog>
            <!-- end -->
            <!-- 修改用户弹层 -->
            <el-dialog title="修改用户" align="left" :visible.sync="dialogUptFormVisible" width="40%" top="8vh">
                <el-form label-width="120px" :model="updateUser" :rules="rules" ref="uptForm">
                    <el-form-item label="用户名称:" prop="username">
                        <el-input v-model="updateUser.username" autocomplete="off"></el-input>
                    </el-form-item>
                    <el-form-item label="邮箱:" prop="email">
                        <el-input v-model="updateUser.email" autocomplete="off"></el-input>
                    </el-form-item>
                    <el-form-item label="密码:" prop="password">
                        <el-input type="password" v-model="updateUser.password" autocomplete="off"></el-input>
                    </el-form-item>

                    <el-form-item label="角色: " prop="roleIds">
                        <el-select v-model="updateUser.roleIds" multiple placeholder="请选择(默认游客)">
                            <el-option
                                    v-for="role in roleList"
                                    :key="role.id"
                                    :label="role.name"
                                    :value="role.id">
                            </el-option>
                        </el-select>
                    </el-form-item>
                </el-form>
                <div slot="footer" class="dialog-footer">
                    <el-button @click="handleUpdateCancel">取 消</el-button>
                    <el-button type="primary" @click="handleUpdateConfirm">确 定</el-button>
                </div>
            </el-dialog>
            <!-- end -->
        </div>
    </div>
</body>
<script>

    new Vue({
        el: "#div",
        data: {
            pagination: {
                total: 0,  //总条数,这里需要从后台数据库查询
                pageNum: 1, //当前页
                pageSize: 5 //每页显示条数
            },
            userList: [],  //用户列表数据,这里需要从后台数据库查询
            roleList: [], // 角色列表数据,这里需要从后台数据库查询
            queryParams: { // 搜索条件
                username: ''
            },
            dialogFormVisible: false, // 添加窗口显示状态
            dialogUptFormVisible: false,//修改窗口显示状态
            user: { // 用户数据
                username: '',
                email: '',
                password: '',
                remark: '',
                roleIds: ''
            },
            updateUser: { //用户的修改数据
                id: "",
                username: '',
                email: '',
                password: '',
                roleIds: []
            },
            rules: { //校验规则
                username: [
                    {required: true, message: '请填写', trigger: 'blur'}
                ],
                email: [
                    {required: true, message: '请填写', trigger: 'blur'},
                    {type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change']}
                ],
                password: [
                    {required: true, message: '请填写', trigger: 'blur'}
                ]
            }

        },
        created() {
            //TODO: 页面加载好之前的查询工作
            //ajax请求分页的用户信息
            /*
                1.  /user  表示请求的servlet
                2.  methodName=findUserByPage  表示执行后台的哪个方法
                3.  "curPage="+this.pagination.pageNum 表示当前页,传递到后台,第一次值是1
                4.
             */
            axios.get("/user?methodName=findUserByPage&curPage="+this.pagination.pageNum+"&pageSize="+this.pagination.pageSize).
                  then(response=>{
                        //rsponse表示响应对象 response.data表示接收的响应数据
                        // console.log(response.data);
                        //1.将用户信息赋值给userList
                        this.userList=response.data.list;
                        //2.将用户总数量赋值给pagination的属性total
                        this.pagination.total=response.data.count;
            });
        },
        methods: {
            //TODO: 获取数据
            getUserList() {

                // 以下是伪数据

                let response = {
                    data:{
                        "flag": true,
                        "message": "获取用户列表成功",
                        "result": {
                            "rows": [
                                {
                                    "createTime": "2020-05-20 00:00:00.0",
                                    "updateTime": "2020-05-20 00:00:00.0",
                                    "id": 1,
                                    "username": "zs",
                                    "password": "123",
                                    "remark" : "管理员",
                                    "email" : "zs@itcast.cn",
                                    "roleIds" : "[2]",
                                    "roles" : [
                                            {
                                                "id": "1",
                                                "name": "A管理员",
                                                "description" : "A模块的管理员"
                                            },
                                            {
                                                "id": "3",
                                                "name": "B管理员",
                                                "description" : "B模块的管理员"
                                            }
                                    ]
                                },
                                {
                                    "createTime": "2020-05-20 00:00:00.0",
                                    "updateTime": "2020-05-20 00:00:00.0",
                                    "id": 1,
                                    "username": "zs",
                                    "password": "123",
                                    "remark" : "管理员",
                                    "email" : "zs@itcast.cn",
                                    "roleIds" : ["2"],
                                    "roles" : [
                                        {
                                            "id": "1",
                                            "name": "A管理员",
                                            "description" : "A模块的管理员"
                                        },
                                        {
                                            "id": "3",
                                            "name": "B管理员",
                                            "description" : "B模块的管理员"
                                        }
                                    ]
                                }
                            ],
                            "total": 15
                        }
                    }
                };

                // 指定总记录数
                this.pagination.total = response.data.result.total;
                // 执行分页显示的数据
                this.userList = response.data.result.rows;


            },
            //TODO: 查询角色表
            getRoleList() {
                //以下是伪数据,数据要从后台获取
                let response = [
                        {
                            "id": "1",
                            "name": "管理员"
                        } ,
                        {
                            "id": "2",
                            "name": "会员"
                        },
                        {
                            "id": "3",
                            "name": "游客"
                        }
                    ]

                this.roleList = response

            },
            //将查询条件置空
            handleRest() {
                this.queryParams = {
                    username: ''
                }
            },
            //搜索
            handleSearch() {
                //将当前页码设置为1
                this.pagination.pageNum = 1;
                this.getUserList();
            },
            //每页数量改变的时候
            handleSizeChange(val) {
                this.pagination.pageSize = val;
                this.pagination.pageNum = 1;
                this.getUserList();
            },
            //当前页码改变的时候
            handleCurrentChange(val) {
                this.pagination.pageNum = val;
                this.getUserList();
            },
            // 新增用户
            handleCreate() {
                if (this.$refs['form']) {
                    this.$refs['form'].resetFields();
                }
                this.dialogFormVisible = true;

            },
            //新增用户确定 TODO:
            handleCreateConfirm() {
                var t = this;
                this.$refs['form'].validate((valid) => {
                    if (valid) {

                    }
                });
            },
            //新增用户取消
            handleCreateCancel() {
                this.dialogFormVisible = false;
            },
            // 修改用户
            handleUpdate(row) {
                var t = this;
                if (t.$refs['uptForm']) {
                    t.$refs['uptForm'].resetFields();
                }
                t.updateUser.id = row.id;
                t.updateUser.username = row.username;
                t.updateUser.password = row.password;
                t.updateUser.email = row.email;
                t.updateUser.roleIds = row.roleIds;

                t.dialogUptFormVisible = true;

            },
            //修改用户确定 TODO
            handleUpdateConfirm() {
                this.$refs['uptForm'].validate((valid) => {
                    if (valid) {

                    }
                });
            },
            //修改用户取消
            handleUpdateCancel() {
                this.dialogUptFormVisible = false;
            },
            // 删除用户 TODO
            handleDeleted(row) {

            }
        }
    })
</script>
</html>

8.4后端代码

1.PageBean

POJO: 普通实体类,用来封装数据库的数据的

VO : 用于封装页面需要的数据的实体

image-20201002154916525

package com.itheima.case2.vo;

import java.util.List;
/*
    分页bean,这里使用自定义泛型类,这样可以适合于其他类型不仅仅是User,例如Role(角色) Permission(权限)
 */
public class PageBean<T> {
    //1.定义成员变量保存页码上的分页数据
    private List<T> list;
    //2.定义变量保存当前页码
    private int curPage;
    //3.定义成员变量保存每页显示条数
    private int pageSize;
    //4.定义成员变量保存总记录数
    private int count;
    //定义成员方法计算起始索引
    public int getStartIndex(){
        int startIndex=(curPage-1)*pageSize;
        //返回起始索引
        return startIndex;
    }
    //get set


    public List<T> getList() {
        return list;
    }

    public void setList(List<T> list) {
        this.list = list;
    }

    public int getCurPage() {
        return curPage;
    }

    public void setCurPage(int curPage) {
        this.curPage = curPage;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}

说明:和之前写的PageBean基本一样,只是element不需要总页码,所以可以将总页码去掉。

2.web层

UserServlet:

package com.itheima.case2.web;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima.case2.service.UserService;
import com.itheima.case2.vo.PageBean;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

@WebServlet("/user")
public class UserServlet extends BaseServlet {
    /*
        页面的请求url:http://localhost:8080/user?methodName=findAllUsers
        说明:findAllUsers需要携带参数request和response的原因是我们需要在findAllUsers
        方法体中进行处理请求和响应代码,也就是说之前写在doGet或者doPost方法体中的代码
        现在需要书写在这个方法体中
     */
    /*public void findAllUsers(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("查询用户");
    }*/


    /*
        页面的请求url:http://localhost:8080/user?methodName=deleteUserById
     */
   /* public void deleteUserById(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("删除用户");
    }*/
   /*
        分页查询所有的用户和角色信息
    */
    public void findUserByPage(HttpServletRequest request, HttpServletResponse response) {
        //1.获取参数
        int curPage = Integer.parseInt(request.getParameter("curPage"));
        int pageSize = Integer.parseInt(request.getParameter("pageSize"));
        //2.创建service对象
        UserService userService = new UserService();
        //3.获取分页的PageBean对象
        PageBean pageBean = userService.findUserByPage(curPage, pageSize);


        try {
            //4.将pageBean转换为json转换
            String json = new ObjectMapper().writeValueAsString(pageBean);
            //5.将转换后的json数据响应给前端
            response.getWriter().write(json);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

BaseServlet:

package com.itheima.case2.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

@WebServlet("/BaseServlet")
public class BaseServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取请求的参数
        String methodName = request.getParameter("methodName");
        //2.根据获取的参数来判断执行下面哪个方法
        /*if("findAllUsers".equals(methodName)){
            //调用查询所有用户方法
            findAllUsers(request,response);
        }else if("deleteUserById".equals(methodName)){
            //调用根据id删除用户的方法
            deleteUserById(request,response);
        }*/
        /*
            使用反射机制执行浏览器的请求方法,那么首先应该获取Class对象,获取Class对象有三种方式。我们这里使用this.getClass()方式
            ,this表示当前servlet类的对象,当前servlet类由tomcat创建的
         */
        //获取Class类的对象
        Class clazz = this.getClass();
        /*
            要执行的方法的方法名就是前台获取的methodName,并且每个servlet中的方法都需要参数request和response对象,
            所以我们使用clazz对象调用方法获取请求要执行的方法.
            public Method getMethod(String name, Class<?>... parameterTypes)
                    参数:
                        name 表示方法名
                        parameterTypes:表示参数类型,属于Class类型
         */
        try {
            Method m = clazz.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
            /*
                执行获取到的方法,方法需要两个对象作为参数:request response
                public Object invoke(Object obj, Object... args)
                    参数:
                        obj:调用方法的对象 this,this表示当前servlet类的对象,当前servlet类由tomcat创建的
                        args:执行的方法需要的实参  request,response
             */
            m.invoke(this, request, response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.service层

package com.itheima.case2.service;

import com.itheima.case2.dao.UserMapper;
import com.itheima.case2.pojo.User;
import com.itheima.case2.utils.SqlSessionUtil;
import com.itheima.case2.vo.PageBean;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

public class UserService {
    //分页查询所有用户
    public PageBean findUserByPage(int curPage, int pageSize) {
        //1.创建分页类PageBean对象
        PageBean<User> pb = new PageBean<>();
        //2.将当前页和每页显示条数放到pb中
        pb.setCurPage(curPage);
        pb.setPageSize(pageSize);
        //3.获取会话对象
        SqlSession session = SqlSessionUtil.getSession();
        //4.获取用户接口代理对象
        UserMapper mapper = session.getMapper(UserMapper.class);
        //5.查询用户信息
        List<User> userList = mapper.findUserByPage(pb.getStartIndex(), pageSize);
        //6.将查询的用户信息放到pb中
        pb.setList(userList);
        //7.查询用户的总数量
        int count = mapper.findAllUserCount();
        //8.将用户的总数量放到pb中
        pb.setCount(count);
        //9.释放资源
        session.close();
        //10.返回pb对象
        return pb;
    }
}

4.dao层

页面显示效果:

image-20201002155724536

说明:页面最后显示的内容是用户和对应角色信息,所以需要多表产生关联查询。用户信息位于t_user表中,角色信息位于t_role表中,如果要对这两张表产生关联需要使用中间表t_user_role。

分页查询所有的用户信息,并且还有用户对应的角色,所以这里是t_user,t_role以及中间表t_user_role三张表的多表查询。

注意:页面只需要t_user表和t_role表信息,并不需要中间表t_user_role的信息,中间表只是起到一个过渡的作用,所以这里sql语句写法和mybatis实现有点和之前学习的mybatis多对多不太一样。

image-20201002160437057

sql语句分析:

分析:
1.先在t_user表中进行分页查询,假设起始索引是0,每页显示5条数据
2.将上述查询的结果作为临时表和t_role以及t_user_role表进行连接查询

/*
	分析:
	1.先在t_user表中进行分页查询,假设起始索引是0,每页显示5条数据
	2.将上述查询的结果作为临时表和t_role以及t_user_role表进行连接查询
*/        
select u.id uid,u.username,u.password,u.remark,u.email,u.createTime,u.updateTime,
 r.id rid,r.name,r.keyword,r.description
from (select * from t_user limit 0,5) u 
inner join t_user_role ur
inner join t_role r
on u.id = ur.user_id and ur.role_id = r.id;

注意:这里多表我们就是映射文件方式查询,不使用注解,注解也可以。多表和动态sql一般都是使用映射文件xml方式开发

这里需要注意,maven工程的映射文件放到哪里,由于核心配置文件采用包扫描方式加载接口:

image-20201002161250732

包扫描方式要求接口和映射文件名必须一致,并且在同一路径下。而maven工程配置文件只能存放到resources目录下,不能放到其他位置。那么为了保证映射文件和接口在同一路径下,我们可以在resources目录上右键创建和接口同一路径。

image-20201002162154804

image-20201002162248481

说明:由于是创建目录不是包,所以这里使用/进行分隔。

image-20201002162349859

UserMapper.xml映射文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
映射文件
namespace 指定接口的类全名
-->
<mapper namespace="com.itheima.case2.dao.UserMapper">
    <!--
    查询语句
    id: 接口中方法的名字
    resultType:返回的实体类的类型,类全名
    List<User> findUserByPage(@Param("startIndex") int startIndex, @Param("pageSize")int pageSize);
    -->
    <resultMap id="userAndRole" type="User" autoMapping="true">
        <!--t_user表的主键-->
        <id column="uid" property="id"/>
        <!--一对多,这里中间表只是起到一个桥梁的作用,并不需要查询该表数据-->
        <collection property="roles" javaType="list" ofType="Role" autoMapping="true">
        <!--t_role表的主键-->
            <id column="rid" property="id"/>
        </collection>
    </resultMap>
    <select id="findUserByPage" resultMap="userAndRole">
        select u.id uid,u.username,u.password,u.remark,u.email,u.createTime,u.updateTime,
               r.id rid,r.name,r.keyword,r.description
        from (select * from t_user limit #{startIndex},#{pageSize}) u
        inner join t_user_role ur
        inner join t_role r
        on u.id = ur.user_id and ur.role_id = r.id;
    </select>
</mapper>

UserMapper接口:

package com.itheima.case2.dao;

import com.itheima.case2.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface UserMapper {
    /*
        分页查询所有的用户信息,并且还有用户对应的角色,所以这里是t_user,t_role以及中间表t_user_role三张表的多表查询。
        这里多表我们就是映射文件方式查询,不使用注解,注解也可以。多表和动态sql一般都是使用映射文件xml方式开发
     */
    List<User> findUserByPage(@Param("startIndex") int startIndex, @Param("pageSize")int pageSize);
    //查询用户的总数量
    @Select("select count(*) from t_user")
    int findAllUserCount();
}

8.5页面效果

image-20201002162632508

9.面向接口的开发(spring的ioc)

9.1 面向接口开发的好处

之前我们在servlet中创建业务层UserService的对象,都是使用类,即UserService是一个类。而在实际开发中我们往往都是使用接口,然后创建接口实现类的对象。

假设按照我们之前书写的代码那样,使用类而不是接口,那么我们如果当前类有需要修改或者扩展的代码,那么每次都需要修改类的源代码,并且还得重新编译和发布到服务器上。

例如原来的类是UserService类,然后对代码进行扩展放到UserService2类中每次都要修改代码,这样很不方便。

所以我们使用面向接口编程就可以解决上述问题。

1.创建接口,并定义实现类,将之前类的方法放到实现类UserServiceImpl中,并且将该类放到impl包下

image-20201003153652879

UserService接口:

package com.itheima.case2.service;

import com.itheima.case2.vo.PageBean;

public interface UserService {
    //分页查询所有用户
    public PageBean findUserByPage(int curPage, int pageSize);
}

实现类UserServiceImpl:

package com.itheima.case2.service.impl;

import com.itheima.case2.dao.UserMapper;
import com.itheima.case2.pojo.User;
import com.itheima.case2.service.UserService;
import com.itheima.case2.utils.SqlSessionUtil;
import com.itheima.case2.vo.PageBean;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

public class UserServiceImpl implements UserService{
    //分页查询所有用户
    public PageBean findUserByPage(int curPage, int pageSize) {
        //1.创建分页类PageBean对象
        PageBean<User> pb = new PageBean<>();
        //2.将当前页和每页显示条数放到pb中
        pb.setCurPage(curPage);
        pb.setPageSize(pageSize);
        //3.获取会话对象
        SqlSession session = SqlSessionUtil.getSession();
        //4.获取用户接口代理对象
        UserMapper mapper = session.getMapper(UserMapper.class);
        //5.查询用户信息
        List<User> userList = mapper.findUserByPage(pb.getStartIndex(), pageSize);
        //6.将查询的用户信息放到pb中
        pb.setList(userList);
        //7.查询用户的总数量
        int count = mapper.findAllUserCount();
        //8.将用户的总数量放到pb中
        pb.setCount(count);
        //9.释放资源
        session.close();
        //10.返回pb对象
        return pb;
    }
}

2.Servlet:

image-20201003202509841

9.2使用反射创建读取配置文件中的实现类创建对象

按照上述做法我们虽然引入接口,但是如果想扩展UserServiceImpl实现类中的内容,即切换到UserServiceImpl2实现类我们还需修改servlet源码,所以我们这里将实现类都存在配置文件中,然后读取配置文件,并使用反射技术创建实现类对象,如果每次想修改实现类,只需要修改配置文件即可。

beans.properties配置文件如下:

image-20201003203145290

userService=com.itheima.case2.service.impl.UserServiceImpl

UserServlet代码如下:

 /*
        分页查询所有的用户和角色信息
    */
    public void findUserByPage(HttpServletRequest request, HttpServletResponse response) {


        try {
            //1.获取参数
            int curPage = Integer.parseInt(request.getParameter("curPage"));
            int pageSize = Integer.parseInt(request.getParameter("pageSize"));
            //2.创建service对象
//        UserService userService = new UserServiceImpl();
            //加载配置文件,参数只需要给配置文件名即可
            ResourceBundle rb = ResourceBundle.getBundle("beans");
            //根据配置文件的key获取value即实现类全名 com.itheima.case2.service.impl.UserServiceImpl
            String classPath = rb.getString("userService");
            //获取实现类的Class对象
            Class clazz = Class.forName(classPath);
            //创建实现类对象
            UserService userService = (UserService) clazz.newInstance();
            //3.获取分页的PageBean对象
            PageBean pageBean = userService.findUserByPage(curPage, pageSize);
            //4.将pageBean转换为json转换
            String json = new ObjectMapper().writeValueAsString(pageBean);
            //5.将转换后的json数据响应给前端
            response.getWriter().write(json);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

9.3 工厂类

​ 工厂模式:
​ 就是专门用来创建对象的一个工具类.
​ 将对象的创建由new的方式替换成反射+配置的方式.
​ 优势:
​ 1.解耦
​ 2.方便bean(理解为对象)的管理.(单例工厂和多例工厂)
​ 单例:一个类创建的对象只有一个
​ 多例:一个类创建的对象有多个
​ 工厂类就是将上述使用反射+配置的方式.抽取到工厂类中

9.3.1 多例工厂

BeansFactory类:

package com.itheima.case2.utils;

import com.itheima.case2.service.UserService;

import java.util.ResourceBundle;

public class BeansFactory {
    //多例工厂
    public static Object getBeans(String beanName) {
        Object obj = null;
        try {
            //加载配置文件,参数只需要给配置文件名即可
            ResourceBundle rb = ResourceBundle.getBundle("beans");
            //根据配置文件的key获取value即实现类全名 com.itheima.case2.service.impl.UserServiceImpl
            String classPath = rb.getString(beanName);
            //获取实现类的Class对象
            Class clazz = Class.forName(classPath);
            //创建实现类对象
            obj = clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        //返回bean对象
        return obj;
    }
}

UserServlet:

package com.itheima.case2.web;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima.case2.service.UserService;
import com.itheima.case2.service.impl.UserServiceImpl;
import com.itheima.case2.utils.BeansFactory;
import com.itheima.case2.vo.PageBean;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ResourceBundle;

@WebServlet("/user")
public class UserServlet extends BaseServlet {
    /*
        页面的请求url:http://localhost:8080/user?methodName=findAllUsers
        说明:findAllUsers需要携带参数request和response的原因是我们需要在findAllUsers
        方法体中进行处理请求和响应代码,也就是说之前写在doGet或者doPost方法体中的代码
        现在需要书写在这个方法体中
     */
    /*public void findAllUsers(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("查询用户");
    }*/


    /*
        页面的请求url:http://localhost:8080/user?methodName=deleteUserById
     */
   /* public void deleteUserById(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("删除用户");
    }*/
   /*
        分页查询所有的用户和角色信息
    */
    public void findUserByPage(HttpServletRequest request, HttpServletResponse response) {


        try {
            //1.获取参数
            int curPage = Integer.parseInt(request.getParameter("curPage"));
            int pageSize = Integer.parseInt(request.getParameter("pageSize"));
            //2.创建service对象
//        UserService userService = new UserServiceImpl();
          /*  //加载配置文件,参数只需要给配置文件名即可
            ResourceBundle rb = ResourceBundle.getBundle("beans");
            //根据配置文件的key获取value即实现类全名 com.itheima.case2.service.impl.UserServiceImpl
            String classPath = rb.getString("userService");
            //获取实现类的Class对象
            Class clazz = Class.forName(classPath);
            //创建实现类对象
            UserService userService = (UserService) clazz.newInstance();*/
            //使用工厂类获取业务层对象
            UserService userService = (UserService) BeansFactory.getBeans("userService");
            //3.获取分页的PageBean对象
            PageBean pageBean = userService.findUserByPage(curPage, pageSize);
            //4.将pageBean转换为json转换
            String json = new ObjectMapper().writeValueAsString(pageBean);
            //5.将转换后的json数据响应给前端
            response.getWriter().write(json);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

说明:上述在UserServlet中使用BeanFactory调用静态方法getBeans多次获取类的对象,每次获取的对象都是不一样的。

代码如下:

image-20201003213602657

大家思考下,我们在角色权限模块中也有可能使用到UserService实现类对象,那么没有必要在重新创建实现类对象,直接获取之前创建好的对象即可,这样可以节省内存空间,即使UserService实现类对象是单例即可。

9.3.2 单例工厂

相对于多例工厂的好处,节省了资源.

  单例工厂
     核心:第一次创建的对象需要反复使用
     1.创建的对象需要保存起来   声明map
     2.第二次获取的话,直接获取第一次创建的对象即可.

在上述BeansFactory工厂类中添加一个创建单例对象的方法

package com.itheima.case2.utils;

import com.itheima.case2.service.UserService;

import java.util.HashMap;
import java.util.ResourceBundle;

public class BeansFactory {
    //多例工厂
    public static Object getBeans(String beanName) {
        Object obj = null;
        try {
            //加载配置文件,参数只需要给配置文件名即可
            ResourceBundle rb = ResourceBundle.getBundle("beans");
            //根据配置文件的key获取value即实现类全名 com.itheima.case2.service.impl.UserServiceImpl
            String classPath = rb.getString(beanName);
            //获取实现类的Class对象
            Class clazz = Class.forName(classPath);
            //创建实现类对象
            obj = clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        //返回bean对象
        return obj;
    }
    //定义map集合保存单例对象,保证只有一个map集合,所以定义到成员变量位置
    //Map集合的key是配置文件的等号内容userService,value存放的是等号右边内容com.itheima.case2.service.impl.UserServiceImpl
    private static HashMap<String,Object> beansMap = new HashMap<>();
    //定义方法提供单个对象,单例会有多线程安全问题,所以加同步
    public synchronized static Object getSingleBean(String beanName){
        //从map集合中根据key获取value,如果没有obj是null
        Object obj = beansMap.get(beanName);
        //判断map集合中是否含有数据
        if(obj==null){
            //第一次获取
            ResourceBundle resourceBundle = ResourceBundle.getBundle("beans");
            String classPath = resourceBundle.getString(beanName);

            //2.创建对象
            try {
                obj = Class.forName(classPath).newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
            //将读取的内容放到map集合中
            beansMap.put(beanName,obj);
        }
        //返回单例对象
        return obj;
    }
}

image-20201003220509120

9.4 spring的ioc

什么是ioc:控制反转,以前我们要获取对象,我们自己new.主动获取。现在有了工厂模式,我们需要获取对象,是工厂创建,我们被动接受工厂创建的对象.这就是控制反转.说白了ioc就是工厂模式.    

10.SqlSession优化(动态代理)(spring的aop)

我们之前在操作mybatis会话对象SqlSession的时候都是先获取SqlSession对象,使用完毕之后,然后在关闭。那么在每个service类中都要进行获取并且关闭,那么这样造成代码比较冗余。我们可以简化代码开发,把获取SqlSession会话对象和关闭SqlSession会话对象的代码放到工具类中,然后在service类中获取接口代理对象,并使用接口代理对象调用接口的方法既可以完成相应的功能。

问题1:我们如何完成上述功能,就是使用什么样的技术完成?

使用动态代理技术完成。

问题2:使用动态代理技术代理谁?

由于我们是在获取接口代理对象即Mapper对象之前需要先获取会话SqlSession对象,所以我们可以使用动态代理技术代理Mapper接口,生成Mapper接口代理对象。然后对接口中的方法进行增强,增强的内容是获取SqlSession会话对象和关闭SqlSession会话对象。

修改获取SqlSession会话对象的工具类SqlSessionUtil代码:

在工具类中定义静态方法获取mapper的代理对象,从而对mapper的方法做增强。

 //获取mapper的代理对象,从而对mapper的方法做增强
    //方法参数clazz表示方法getMapper(UserMapper.class)的参数UserMapper.class
//    public static Object getProxyMapper(Class clazz) {//Class clazz = UserMapper.class
    public static <T> T getProxyMapper(Class<T> clazz) {//Class clazz = UserMapper.class


        /**
         * 参数1:类加载器,一般被代理对象的类加载器
         * 参数2:接口,被代理对象实现的结构
         * 参数3:调用处理器,处理UserMapper接口的增强方法,findUserByPage findAllUserCount等
         */
        ClassLoader classLoader = clazz.getClassLoader();
        Class<?>[] interfaces = {clazz};//{UserMapper.class}
        T proxyMapper = (T)Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
            /*
                参数:
                    method :UserMapper接口的方法findUserByPage findAllUserCount等
                    args:UserMapper接口的方法findUserByPage findAllUserCount等的参数
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                //调用工具类SqlSessionUtil中的方法获取会话SqlSession对象
                SqlSession session = getSession();
                //获取原来的mapper对象
                Object mapper = session.getMapper(clazz);

                //1.执行原有功能:findUserByPage findAllUserCount等
                //res表示原来方法findUserByPage findAllUserCount的返回值
                Object res = method.invoke(mapper, args);


                //2.释放资源
                session.close();
                //返回给findUserByPage findAllUserCount的调用者
                return res;
            }
        });
        //返回UserMapper接口代理对象
        return proxyMapper;
    }

改写后的Service类:

package com.itheima.case2.service.impl;

import com.itheima.case2.dao.UserMapper;
import com.itheima.case2.pojo.User;
import com.itheima.case2.service.UserService;
import com.itheima.case2.utils.SqlSessionUtil;
import com.itheima.case2.vo.PageBean;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

public class UserServiceImpl implements UserService{
    //分页查询所有用户
    public PageBean findUserByPage(int curPage, int pageSize) {
        //1.创建分页类PageBean对象
        PageBean<User> pb = new PageBean<>();
        //2.将当前页和每页显示条数放到pb中
        pb.setCurPage(curPage);
        pb.setPageSize(pageSize);
        //3.获取会话对象
//        SqlSession session = SqlSessionUtil.getSession();
//        //4.获取用户接口代理对象
//        UserMapper mapper = session.getMapper(UserMapper.class);
        //使用工具类中的动态代理方法获取Mapper对象
//        UserMapper mapper = (UserMapper) SqlSessionUtil.getProxyMapper(UserMapper.class);
        UserMapper mapper = SqlSessionUtil.getProxyMapper(UserMapper.class);
        //5.查询用户信息
        List<User> userList = mapper.findUserByPage(pb.getStartIndex(), pageSize);
        //6.将查询的用户信息放到pb中
        pb.setList(userList);
        //7.查询用户的总数量
        int count = mapper.findAllUserCount();
        //8.将用户的总数量放到pb中
        pb.setCount(count);
        //9.释放资源
//        session.close();
        //10.返回pb对象
        return pb;
    }
}

图解说明:

1599533208409

11.更改用户

1.分析

1.修改页面的角色位置下拉列表显示多个角色

image-20201005074445845

说明:下拉列表的角色应该到角色表中查询并显示到页面中。

2.业务来说,先回显用户信息,然后做更改的时候,先删除中间表数据,再向中间表数据添加新的关系.

说明:

​ 1)对于回显数据时,对于用户名称、邮箱、密码都需要显示在页面中,而角色也应该显示到页面中,注意这里角色应该是来自于和用户信息一起查询的信息,和上述第一步单独从角色表中查询不一样,第一步是从角色表中查询出来角色信息显示到下拉列表中。

​ 2)当修改完用户信息和对应的角色信息之后,点击确定按钮,首先修改用户表信息,然后修改用户的角色信息,那么这样一来中间表就会发生改变,所以对于中间表的数据是先删除中间表数据,再向中间表数据添加新的关系.

2.前端修改页面的下拉列表显示角色信息

当页面加载完毕的时候,应该到角色表中查询角色信息显示到下拉列表中。所以应该在生命周期created函数中进行数据的查找。

1.在userList.html页面中查找角色信息

image-20201005112451236

image-20201005112708792

在上面使用vue对roleList进行遍历显示到下拉列表中。

image-20201005113102374

2.RoleServlet

package com.itheima.case2.web;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima.case2.pojo.Role;
import com.itheima.case2.service.RoleService;
import com.itheima.case2.utils.BeansFactory;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

@WebServlet("/role")
public class RoleServlet extends BaseServlet {
    //获取角色对象
    RoleService roleService = (RoleService) BeansFactory.getSingleBean("roleService");
   /*
        查询所有角色信息
    */
   public void findAllRoles(HttpServletRequest request,HttpServletResponse response){
       try {
           //使用roleService对象调用方法查询所有的角色
           List<Role> roleList = roleService.findAllRoles();
           //转换为json格式数据
           String jsonStr = new ObjectMapper().writeValueAsString(roleList);
           //响应给浏览器
           response.getWriter().print(jsonStr);
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
}

配置文件:

userService=com.itheima.case2.service.impl.UserServiceImpl
roleService=com.itheima.case2.service.impl.RoleServiceImpl

3.RoleService接口

package com.itheima.case2.service;

import com.itheima.case2.pojo.Role;

import java.util.List;

public interface RoleService {
    //查询所有角色信息
    List<Role> findAllRoles();
}

4.RoleServiceImpl实现类

package com.itheima.case2.service.impl;

import com.itheima.case2.dao.RoleMapper;
import com.itheima.case2.pojo.Role;
import com.itheima.case2.service.RoleService;
import com.itheima.case2.utils.SqlSessionUtil;
import java.util.List;
public class RoleServiceImpl implements RoleService {
    //获取接口代理对象
    private RoleMapper roleMapper=SqlSessionUtil.getProxyMapper(RoleMapper.class);
    /*
        查询所有角色信息
     */
    @Override
    public List<Role> findAllRoles() {
        //返回查找的数据
        return roleMapper.findAllRoles();
    }
}

5.RoleMapper接口

package com.itheima.case2.dao;

import com.itheima.case2.pojo.Role;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface RoleMapper {
    //查询所有的角色信息
    @Select("select * from t_role")
    List<Role> findAllRoles();
}

6.页面效果

image-20201005113927664

3.前端修改页面的下拉列表角色信息的回显

第二步只是将角色表中所有的角色信息都显示到下拉框中,接下来我们要实现角色信息的回显,即将角色信息显示到下拉框中。

image-20201005114220560

1.当我们点击页面中修改按钮的时候会将当前行的信息都显示到修改的页面

image-20201005115621800

image-20201005115722245

2.代码如下:

image-20201005115820744

image-20201005120020623

由于我们之前查询用户信息时并没有查询就是id,所以页面并没有显示当前用户的角色信息。所以我们需要修改查询用户的映射文件,将当前用户封装到对应的实体类中。

3.UserMapper.xml映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
映射文件
namespace 指定接口的类全名
-->
<mapper namespace="com.itheima.case2.dao.UserMapper">
    <!--
    查询语句
    id: 接口中方法的名字
    resultType:返回的实体类的类型,类全名
    List<User> findUserByPage(@Param("startIndex") int startIndex, @Param("pageSize")int pageSize);
    -->
    <resultMap id="userAndRole" type="User" autoMapping="true">
        <!--t_user表的主键-->
        <id column="uid" property="id"/>
        <!--
            private List<String> roleIds;
            将查询的角色id放到roleIds集合中
        -->
        <collection property="roleIds" javaType="list" ofType="String">
            <id column="rid"/>
        </collection>
        <!--一对多,这里中间表只是起到一个桥梁的作用,并不需要查询该表数据-->
        <collection property="roles" javaType="list" ofType="Role" autoMapping="true">
        <!--t_role表的主键-->
            <id column="rid" property="id"/>
        </collection>
    </resultMap>
    <select id="findUserByPage" resultMap="userAndRole">
        select u.id uid,u.username,u.password,u.remark,u.email,u.createTime,u.updateTime,
               r.id rid,r.name,r.keyword,r.description
        from (select * from t_user limit #{startIndex},#{pageSize}) u
        inner join t_user_role ur
        inner join t_role r
        on u.id = ur.user_id and ur.role_id = r.id;
    </select>
</mapper>

image-20201005171628786

4.显示效果

image-20201005171741759

4.修改数据

1.需求

image-20201005172233397

点击确定按钮执行的代码:

image-20201005172412812

image-20201005172523631

2.前端代码

//修改用户确定 TODO
            handleUpdateConfirm() {
                this.$refs['uptForm'].validate((valid) => {
                    if (valid) {
                        //在if中书写将修改数据提交到后台的代码
                        //发送请求,提交修改的数据
                        axios.get("/user?methodName=updateUser&id="+this.updateUser.id+"&username="+this.updateUser.username+
                            "&email="+this.updateUser.email+"&password="+this.updateUser.password+"&roleIds="+
                            this.updateUser.roleIds).then(response=>{
                                // console.log(response);
                                //隐藏修改框
                                this.dialogUptFormVisible = false;
                        });
                    }
                });
            },

3.后台代码

3.0实体类
package com.itheima.case2.pojo;

import java.util.ArrayList;
import java.util.List;

/*
    用户
 */
public class User2 {
    private Integer id;
    private String username;
    private String password;
    private String email;
    private String createTime;
    private String updateTime;
    private String remark;
    private String roleIds;
    //一个用户可以保存多个角色
    private List<Role> roles = new ArrayList<Role>(0);

    public String getRoleIds() {
        return roleIds;
    }

    public void setRoleIds(String roleIds) {
        this.roleIds = roleIds;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getCreateTime() {
        return createTime;
    }

    public void setCreateTime(String createTime) {
        this.createTime = createTime;
    }

    public String getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(String updateTime) {
        this.updateTime = updateTime;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", email='" + email + '\'' +
                ", createTime='" + createTime + '\'' +
                ", updateTime='" + updateTime + '\'' +
                ", remark='" + remark + '\'' +
                ", roleIds=" + roleIds +
                ", roles=" + roles +
                '}';
    }
}

3.1web层
/*
        修改信息
     */
    public void updateUser(HttpServletRequest request, HttpServletResponse response) {
        Map<String, String[]> parameterMap = request.getParameterMap();

        User2 user = new User2();
        //将请求中的参数封装到user对象
        try {
            //接收的角色id是一个数组
            BeanUtils.populate(user, parameterMap);
            System.out.println(user);
            //调用service
            UserService userService = (UserService) BeansFactory.getSingleBean("userService");
            userService.updateUser(user);
//           response.getWriter().print("true");
            response.sendRedirect("/index.html");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
3.2service层
public interface UserService {
    //更新用户
    void updateUser(User2 user);
}
 @Override
    public void updateUser(User2 user) {
        //开启事务
        SqlSession session = SqlSessionUtil.getSession(false);
        UserMapper userMapper = session.getMapper(UserMapper.class);
        UserRoleMapper userRoleMapper = session.getMapper(UserRoleMapper.class);

        try {
            //1更新user库的信息
            userMapper.update(user);
            //2.删除中间表的数据
            userRoleMapper.deleteByUid(user.getId());
            //3.向中间表插入新的数据
            String s = user.getRoleIds();
            String[] arr = s.split(",");
            for (String rid : arr) {
                userRoleMapper.insert(user.getId(), Integer.parseInt(rid));
            }

            //提交事务
            session.commit();
        } catch (Exception e) {
            //回滚事务
            session.rollback();
            e.printStackTrace();
        }
    }
3.3dao层
public interface UserMapper {
    //更新用户信息
    @Update("update t_user set username=#{username},password=#{password},email=#{email} where id=#{id}")
    void update(User2 user);
}
public interface UserRoleMapper {
    //根据用户id删除中间表的数据
    @Delete("delete from t_user_role where user_id=#{id}")
    void deleteByUid(@Param("id") Integer id);
    //向中间表插入新的数据
    @Insert("insert into t_user_role values(#{id},#{rid})")
    void insert(@Param("id") Integer id, @Param("rid")Integer rid);
}
posted @   忘了鱼尾纱的猫  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
  1. 1 刘哈哈与大先生 刘心&大鹏
  2. 2 我们打着光脚在风车下跑,手上的狗尾巴草摇啊摇 等一下就回家 / -艾兜
  3. 3 哎呦 毛不易
  4. 4 夜、萤火虫和你 AniFace
哎呦 - 毛不易
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作曲 : 毛不易

作词 : 毛不易

编曲Arranger :赵兆 宋涛

刚刚下过雨潮湿的空气

刚刚下过雨潮湿的空气

熟悉的风景

收拾好行李出发去旅行

希望能遇见你

也许在路上或是在家里

某时某刻某地

莫名其妙 你打了个喷嚏

戴上太阳帽我走过海岛

浪花都带着笑

笑我慌慌张张笨手笨脚

找你都找不到

你的模样虽然我不知道

依然努力奔跑

会相遇吧 在下一个街角

我会带你住进城堡 门前开满了花花草草

暖风拂过你的裙角 蝴蝶在你身边围绕

等到月亮挂在树梢 我会轻轻把你拥抱

哎哟 好想长生不老

戴上太阳帽我走过海岛

浪花都带着笑

笑我慌慌张张笨手笨脚

找你都找不到

你的模样虽然我不知道

依然努力奔跑

会相遇吧 在下一个街角

我会带你住进城堡 门前开满了花花草草

暖风拂过你的裙角 蝴蝶在你身边围绕

等到月亮挂在树梢 我会轻轻把你拥抱

哎哟 好想长生不老

我会带你住进城堡 门前开满了花花草草

暖风拂过你的裙角 蝴蝶在你身边围绕

等到月亮挂在树梢 我会轻轻把你拥抱

哎哟 好想长生不老

我会带你回到故乡 回到出发之前的地方

你的未来我的过去 就在这里合二为一

明天刮风还是下雨 明天生活是悲是喜

哎哟 有你不费力气

哎哟 快让我遇见你

制作人Producer:李健 赵兆

制作人Producer:李健 赵兆

制作人Producer:李健 赵兆

键盘Keyboard:宋涛

吉他Guitar:劳国贤

合音Backing Vocal:梁古驰

混音Mixing:李游

母带Mastering:Joe LaPorta .(Sterling Sound. NY)

录音棚Studio:55TEC . Beijing

录音Recording:李游 李杨

人声录音Vocal Recording:李杨

人声编辑Vocal Editing:李杨

录音助理Recording Assistants:郭舒文 邢铜

音乐制作发行produced and distributed: 哇唧唧哇×智慧大狗

点击右上角即可分享
微信分享提示