综合案例
综合案例
# 修改web综合案例
要求如下:
1. 后端项目要求
1). 基于springboot框架
2). Restful风格
3). 对异常进行统一的处理
4). 使用AOP统计查询功能的执行时长
5). 对增删改方法进行事务管理
2. 前端项目要求
1). Restful风格
之前完成的web综合案例功能: 【前后端分离】
-
用户
-
添加用户
-
前端:
- POST请求方式
- 请求参数:json
-
后端:
- 允许跨域
- @RequestBody 接收前端发送的json数据
-
解决的问题:
1、需要获取所有的角色 (查询功能)
-
-
修改用户
-
前端
-
请求方式:PUT
-
请求参数:要修改用户数据(json)
axios.put("http://localhost:8080/user", 请求参数) 修改成功: 重新获取用户列表数据、 隐藏修改用户的窗口
-
-
后端
-
RESTful风格
-
修改用户: 用户的基本数据、用户的角色数据 第1步:修改用户的基本数据(用户表) 第2步:删除用户的所有角色数据, 添加用户的角色数据 (用户角色表)
-
-
-
删除用户
-
前端:
-
请求方式:DELETE
axios.delete("http://localhost:8080/user/用户id") 删除成功: 重新获取用户列表数据
-
-
后端:
-
RESTful风格
-
删除用户时: (用户表和用户角色表有主外键关系,不能直接删除主键表中的用户数据) 第1步:删除用户角色表中的数据 (外键表) 第2步:删除用户表中的数据(主键表)
-
-
-
查询用户
- 分页查询
- 搜索查询(模糊查询)
-
-
角色
-
权限
# 思路 : 需求分析 -> 业务逻辑 -> 实现阶段(技术选型,搭建架构,具体代码) -> 测试
0. 技术选型 (基于springboot框架)
1). web场景
2). mybatis场景 ,druid场景
3). test场景
1. 入手点的逻辑链: 数据库 -> dao -> service -> controller
1). 先修改数据库的参数(application.yml)
I. url : 修改仓库名
webdemo
II. type
需要导入druid场景
III. pojo (直接复制)
a. po(persistence object) : 持久化对象(数据库映射pojo)
b. vo(values object) : 值对象(前端映射pojo)
注意要修改type-aliases-package(pojo包扫描)
2). dao层
I. dao包下的接口直接复制
注意在启动类中加上@MapperScan
II. resources中的映射文件
需要要修改application.yml中的mapper-locations位置
3). service层
I. 接口
II. 实现类
由于spring的IOC思想,使用dao层无需再用工具类创建,只要从ioc容器中获取即可
然后实现类对象也注意放到IOC容器中
4). controller层
I. restful风格
II. 查询传递参数不要用json格式(有无搜索条件,注意重载!!!)
3. 对异常进行统一的处理
1). 异常统一处理器
1). 作用: 整个工程中出现的异常无需catch,在异常处理器可以进行统一的处理
2). 思想: AOP[切面 = 切入点(controlle层方法) + 通知(手动编写)]
2). 实现方案:
I. ProjectExceptionAdvice (注解配置)
II. 此bean需要被扫描到,不然不会生效 (在springboot中只需要启动类所在包之下)
4. 使用AOP统计查询功能的执行时长
1). 实现思路: 切面=切入点+通知
I. 切入点 : find开头方法(查询方法)
II. 通知 : 环绕通知,切入点执行之前记录时间,之后也记录时间,然后计算得到执行时间
2). 实现方案:
I. TimeAdvice (切面类)
1). 注意通知方法异常需要抛出, 给异常统一处理器处理
2). 要导入aop场景启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
3). 此bean需要被扫描到
5. 对增删改方法进行事务管理(声明式事务)
1). @EnableTransactionManagement // 开启事务支持
加在启动类上
2).@Transactional (接口方法上添加)
# 单元测试
0. dao层findUserByPage方法为例 (查询: 分页,动态搜索)
1. 上策 : 搞清楚是什么东西
1). 查询: 分页,动态搜索
2). 动态搜索的sql:
I. 没有搜索
SELECT * FROM
(SELECT * FROM t_user
LIMIT #{offset},#{pageSize}) tu,
t_user_role tur,t_role tr
WHERE tu.id=tur.user_id AND tr.id = tur.role_id;
II. 有搜索
SELECT * FROM
(SELECT * FROM t_user
where username like concat('%',#{queryParams.username},'%')
LIMIT #{offset},#{pageSize}) tu,
t_user_role tur,t_role tr
WHERE tu.id=tur.user_id AND tr.id = tur.role_id;
3). 判定条件
QueryPageBean.queryParams里的key(username)是否有对应的value值
// queryParams.put("username","h") // 有搜索条件
// queryParams.put("username","") // 没有搜索条件
2. 中策: 看不懂的删掉
扩展知识:SpringBoot解决跨域请求问题
-
前端和后端一旦分离开发,就存在:ajax跨域
-
解决跨问题的方案:
-
前端解决
在vue脚手架工程: config/index.jsp , 配置请求代理: //配置代理(解决前端ajax跨域) proxyTable: { "/": { target: "http://localhost:8080", changeOrigin: true }, //当浏览器发起的请求是:"/user"时,就会由vue代理转换为:"http://localhost:8080/user" "/user": { target: "http://localhost:8080/user", changeOrigin: true //允许跨域 } }
-
后端解决
//在springboot中,存在一个注解: @CrossOrigin @RestController @RequestMapping("/user") @CrossOrigin //该Controller中的所有方法,都允许跨域 public class UserController{ } @RestController @RequestMapping("/user") public class UserController{ @GetMapping @CrossOrigin //允许当前方法跨域 public List<User> findUser(){ .../ } }
-
扩展知识:PageHelper的短板
PageHelper:mybatis的分页助手
- 可以实现mybatis的分页查询功能
PageHelper底层机制:
-
利用AOP思想来完成分页操作
//PageHelper不会破坏原有方法的执行 在查询方法执行之前,设置分页: PageHelper.startPage( 页码 , 每页条数); 执行dao层的查询功能 把查询的结果,封装到PageInfo对象中
-
在执行dao层的查询之前,进行设置:分页
-
在执行dao层的查询时,会对原有的sql查询语句,进行修改:
- 在原sql语句后面,追加:LIMIT 起始位置,每页条数
-
示例:
原sql语句:
select * from t_book
设置PageHelper分页后,会对执行的sql语句进行修改(在sql语句的末尾,追加limit):
select * from t_book limit 起始位置,每页条数
- 结论:PageHelper通常是在SQL语句的末尾追加LIMIT
PageHelper短板:
-
如果不是在原SQL的末尾添加LIMIT(例:在sql语句的中间位置要添加limit,PageHelper就变得不好用了)
-
select u.id as userid, u.username, u.password, u.email, u.remark, u.createTime, u.updateTime, r.id as roleid, r.name, r.description, r.keyword from (select * from t_user LIMIT 起始位置,每页条数) AS u, t_role AS r, t_user_role AS ur where u.id = ur.user_id and r.id = ur.role_id