1、数据的自动填充
1.1 业务需求说明
需求: 数据库中每张表里 都包含创建时间/修改时间的字段. 如果每次操作表,都手动的去维护时间信息.则响应开发效率. 能否优化策略.
解决策略: MybatisPlus 实现自动填充功能.
1.2 MPAPI说明
1.2.1
- 实现元对象处理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
- 注解填充字段 @TableField(… fill = FieldFill.INSERT) 生成器策略部分也可以配置!
1.2.2 添加注解
说明: 新增操作 需要自动填充 created/updated.
修改操作 需要自动填充 updated
1.2.3 编辑配置类
编辑完成之后,测试代码自动填充是否正常
@Component //将对象交给Spring容器管理
public class MyMetaObjectHandler implements MetaObjectHandler
{
@Override
public void insertFill(MetaObject metaObject)
{
//当数据库做新增操作时,自动调用 API调用 不需要问为什么
//metaObject对象 是MP自动填充的配置 有默认行为
Date date = new Date();
this.setFieldValByName("created",date,metaObject);
this.setFieldValByName("updated",date,metaObject);
}
//当数据库做修改操作时,自动调用
@Override
public void updateFill(MetaObject metaObject)
{
Date date = new Date();
this.setFieldValByName("updated",date,metaObject);
}
}
2、用户修改
2.1 用户修改-数据回显
2.1.1 页面JS说明
//1. 点击修改的按钮
<template slot-scope="scope">
<el-button type="primary" icon="el-icon-edit" size="small" @click="updateUserBtn(scope.row)"></el-button>
<el-button type="danger" icon="el-icon-delete" size="small" @click="deleteUser(scope.row)"></el-button>
</template>
//2. 按钮事件
async updateUserBtn(user){
this.updateDialogVisible = true
const {data: result} = await this.$http.get("/user/"+user.id)
if(result.status !== 200) return this.$message.error("用户查询失败")
this.updateUserModel = result.data
},
2.1.2 查询用户的业务接口
- 请求路径: /user/{id}
- 请求类型: GET
- 返回值: SysResult对象
参数名称 | 参数说明 | 备注 |
---|---|---|
status | 状态信息 | 200表示服务器请求成功 201表示服务器异常 |
msg | 服务器返回的提示信息 | 可以为null |
data | 服务器返回的业务数据 | 返回user对象 |
- JSON格式如下
{
"status":200,
"msg":"服务器调用成功!",
"data":{
"created":"2021-02-18T11:17:23.000+00:00",
"updated":"2021-05-17T11:33:46.000+00:00",
"id":1,
"username":"admin",
"password":"a66abb5684c45962d887564f08346e8d",
"phone":"13111112222",
"email":"1235678@qq.com",
"status":true,
"role":null
}
}
2.1.3 编辑UserController
2.1.4 编辑UserServiceImpl
2.1.5 数据回显
2.2 用户修改-更新操作
2.2.1 页面分析
//1.修改的JS
<span slot="footer" class="dialog-footer">
<el-button @click="updateDialogVisible = false" >取 消</el-button>
<el-button type="primary" @click="updateUser">确 定</el-button>
</span>
// 2、新增的JS
updateUser(){
//1.预校验数据
this.$refs.updateUserRef.validate(async valid => {
if(!valid) return this.$message.error("表单验证没有通过")
//根据接口文档要求封装数据
let user = {}
user.id = this.updateUserModel.id
user.phone = this.updateUserModel.phone
user.email = this.updateUserModel.email
const {data: result} = await this.$http.put(`/user/updateUser`,user)
if(result.status !== 200) return this.$message.error("用户修改失败")
this.$message.success("用户更新成功")
this.updateDialogVisible = false
this.getUserList()
})
},
2.2.2 业务接口文档说明
- 请求路径: /user/updateUser
- 请求类型: PUT
- 请求参数: User对象结构
参数名称 | 参数说明 | 备注 |
---|---|---|
ID | 用户ID号 | 不能为null |
phone | 手机信息 | 不能为null |
邮箱地址 | 不能为null |
- 返回值: SysResult对象
参数名称 | 参数说明 | 备注 |
---|---|---|
status | 状态信息 | 200表示服务器请求成功 201表示服务器异常 |
msg | 服务器返回的提示信息 | 可以为null |
data | 服务器返回的业务数据 | 返回user对象 |
- JSON格式如下
{
"status":200,
"msg":"服务器调用成功!",
"data":{}
}
2.2.3 编辑UserController
2.2.3 编辑UserServiceImpl
2.3 用户删除操作
2.3.1 页面JS分析
//1.页面删除按钮
<template slot-scope="scope">
<el-button type="primary" icon="el-icon-edit" size="small" @click="updateUserBtn(scope.row)"></el-button>
<el-button type="danger" icon="el-icon-delete" size="small" @click="deleteUser(scope.row)"></el-button>
</template>
//2.发起ajax请求
async deleteUser(user){
//1.消息确认框
const result = await this.$confirm('此操作将永久删除 '+user.username+', 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).catch(error => error)
//如果确认 confirm 如果取消 cancel
if(result !== 'confirm'){
return this.$message.info("删除取消")
}
const {data: result2} = await this.$http.delete(`/user/${user.id}`)
if(result2.status !== 200) return this.$message.error("删除失败")
this.$message.success("删除成功")
//重新加载 数据
this.getUserList()
}
},
2.3.2 业务接口文档
- 请求路径: /user/{id}
- 请求类型: delete
- 请求参数:
参数名称 | 参数说明 | 备注 |
---|---|---|
ID | 用户ID号 | 不能为null |
- 返回值: SysResult对象
参数名称 | 参数说明 | 备注 |
---|---|---|
status | 状态信息 | 200表示服务器请求成功 201表示服务器异常 |
msg | 服务器返回的提示信息 | 可以为null |
data | 服务器返回的业务数据 | 返回user对象 |
2.3.3 编辑UserController
2.3.4 编辑UserServiceImpl
3、关于事务说明
3.1 什么是事务
说明: 如果后台服务器执行正常,则业务正确,事务提交. 如果业务执行失败.事务应该回滚.
3.2 现有代码的业务测试
说明: 如图如果程序执行过程中有报错信息.应该实现事务的回滚. 但是发现现有代码有2个问题
1: 没有添加事物.
2. 后台服务器报错之后,用户没有提示.
3.3 添加事物-@Transactional注解
3.4 全局异常的处理机制
3.4.1 常规操作
说明: 一般控制异常信息. 通常情况下需要添加try-catch 用法.
弊端: 所有的方法都需要try-catch的控制. 必然导致代码的结构复杂.
解决方案: Spring内部提供了一种规则 全局异常的处理机制.
3.4.2 全局异常处理机制
/**
* Spring为了整合全局异常的处理,提供了以下注解
* 1、RestControllerAdvice 定义全局异常的处理类型 返回是JSON
* 2、ExceptionHandler 标识拦截异常的类型,如果类型匹配则执行方法
*/
@RestControllerAdvice // 定义全局异常的处理类 返回值JSON串
public class MyExceptionAdvice
{
/**
* 写法:
* 1、运行时异常
* 2、自定义异常信息
* 3、拦截所有异常(Exception.class)
* @param e
* @return
*/
@ExceptionHandler(RuntimeException.class)
public Object Exception(Exception e)
{
e.printStackTrace(); //输出异常信息
//需求: 如果遇到异常,应该提示用户201/失败信息.
return SysResult.fail();
}
}
4、 商品分类业务实现
4.1 实现商品分类页面跳转
编辑路由,实现商品分类操作
import Vue from 'vue'
import VueRouter from 'vue-router'
// 引入指定的路由组件
import Login from '../components/Login.vue'
import ElementUI from '../components/ElementUI.vue'
import Home from '../components/Home.vue'
import User from '../components/user/user.vue'
import Item from '../components/items/Item.vue'
import Welcome from '../components/Welcome.vue'
import ItemCat from '../components/items/ItemCat.vue'
//使用路由机制
Vue.use(VueRouter)
const routes = [
{path: '/', redirect: '/login'},
{path: '/login', component: Login},
{path: '/elementUI', component: ElementUI},
{path: '/home',component: Home, redirect: '/welcome', children:[
{path: '/welcome',component: Welcome},
{path: '/user',component: User},
{path: '/item',component: Item},
{path: '/itemCat',component: ItemCat}
]}
]
// 配置路由对象
const router = new VueRouter({
routes
})
/**
* 配置路由导航守卫 控制权限
* 参数说明:
* 1、to 要访问的路径
* 2、from 从哪个路径跳转来的
* 3、next 是一个函数 next() 表示放行 next('/login')表示强制跳转
*/
router.beforeEach((to,from,next) => {
// 1、当用户访问登录页面,则直接放行,如果不需要执行后续操作 执行return
if(to.path === '/login') return next()
// 2、当用户访问其他页面时 需要校验是否有token
const token = window.sessionStorage.getItem('token')
// 3、如果数据为null 则访问登录页面
if(!token) return next('/login')
// 4、如果数据不为null 则放行
next()
})
export default router
4.2 ItemCat 说明
4.2.1 表设计说明
4.2.2 ItemCat POJO说明
4.2.3 商品分类业务说明
-
京东官网商品分类对象
-
.如何维护父子关系
表: id与parent_id
对象: this–children -
三级菜单查询
-
如何利用对象封装3级菜单结构?
一级菜单
children-----> 二级菜单信息
children-------> 三级菜单信息
4.3 商品分类列表实现
4.3.1 业务需求
当用户点击商品分类列表时,应该采用三级商品分类的结构实现列表的展现.
4.3.2 页面JS说明
//1. 生命周期函数说明
created() {
//默认获取商品分类列表数据
this.findItemCatList()
},
//2.业务方法说明
async findItemCatList() {
const { data: result }
= await this.$http.get("/itemCat/findItemCatList/3")
if (result.status !== 200) return this.$message.error("获取商品分类列表失败!!")
this.itemCatList = result.data
},
4.3.3 业务接口文档说明
- 请求路径: /itemCat/findItemCatList/{level}
- 请求类型: get
- 请求参数: level
参数名称 | 参数说明 | 备注 |
---|---|---|
level | 查询级别 | 1查询一级分类 2查询1-2 级商品分类 3查询1-2-3级商品分类 |
- 业务说明: 查询3级分类菜单数据 要求三层结构嵌套
- 返回值: SysResult对象
参数名称 | 参数说明 | 备注 |
---|---|---|
status | 状态信息 | 200表示服务器请求成功 201表示服务器异常 |
msg | 服务器返回的提示信息 | 可以为null |
data | 服务器返回的业务数据 | 返回user对象 |
4.3.3 编辑ItemCatController
4.3.4 编辑ItemCatService
/**
* 业务: 查询3级商品分类信息
* 1. 一级中嵌套二级集合
* 2. 二级菜单嵌套三级集合.
*
* 1.0版本: for循环嵌套结构 暂时不考虑level 最好理解的
* 常识:
* 1.用户第一次查询数据库 需要建立链接.
* 2.第二次查询 从链接池中动态获取链接 所以速度更快!!!
*
* 思考: 该业务查询了多少次数据库??? 第一层循环10个 第二层循环10 总查询数=10*10=100次
* 如何优化查询策略!!!!
* @param level
* @return
*/
@Override
public List<ItemCat> findItemCatList(Integer level) {
//性能问题:!!!!!
long startTime = System.currentTimeMillis();
//1.查询一级商品分类信息
QueryWrapper<ItemCat> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("parent_id", 0);
List<ItemCat> oneList = itemCatMapper.selectList(queryWrapper);
//2.查询二级商品分类信息 遍历一级集合
for(ItemCat oneItemCat : oneList){
queryWrapper.clear(); //清空条件
queryWrapper.eq("parent_id", oneItemCat.getId());
List<ItemCat> twoList = itemCatMapper.selectList(queryWrapper);
//3.查询三级商品分类信息 遍历
for(ItemCat twoItemCat : twoList){
queryWrapper.clear();
queryWrapper.eq("parent_id", twoItemCat.getId());
List<ItemCat> threeList = itemCatMapper.selectList(queryWrapper);
//将三级封装给二级
twoItemCat.setChildren(threeList);
}
//3.将二级记录封装给一级
oneItemCat.setChildren(twoList);
}
//记录程序的结束时间
long endTime = System.currentTimeMillis();
System.out.println("查询耗时:"+(endTime - startTime)+"毫秒");
return oneList;
}
}