京淘项目搭建
一、数据的自动填充
1、业务需求说明
需求:数据库中每张表里,都包含创建时间/修改时间的字段。如果每次操作表,都手动的去维护时间信息,则响应开发效率,能否优化策略。
解决策略:MybatisPlus 实现自动填充功能。
2、MPAPI说明
2.1)语法规则
- 实现元对象处理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
- 注解填充字段 @TableField(… fill = FieldFill.INSERT) 生成器策略部分也可以配置!
2.2)添加注解
说明:
新增操作,需要自动填充,created/updated.
修改操作,需要自动填充,updated
package com.jt.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
//pojo基类,完成2个任务,2个日期,实现序列化
@Data
@Accessors(chain=true)
public class BasePojo implements Serializable{
@TableField(fill = FieldFill.INSERT)
private Date created; //表示入库时需要赋值
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updated; //表示入库/更新时赋值.
}
2.3)编辑配置类
编辑完成之后,测试代码自动填充是否正常
@Component //将对象交给Spring容器管理
public class MyMetaObjectHandler implements MetaObjectHandler {
//当数据库做新增操作时,自动调用 API调用 不需要问为什么
//metaObject对象 是MP自动填充的配置 有默认行为
@Override
public void insertFill(MetaObject metaObject) {
//获取当前时间
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);
}
}
二、用户操作
1、用户修改–数据回显
1.1)页面URL说明
//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
},
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
}
}
1.3)编辑UserController
需求: 根据ID查询User信息
/**
* 业务: 根据ID查询用户信息
*
* URL: http://localhost:8091/user/2
* 参数: id
* 返回值: SysResult(User对象)
*/
@GetMapping("/{id}")
public SysResult getUserById(@PathVariable Integer id){
User user = userService.getUserById(id);
return SysResult.success(user);
}
/**
* 需求: 实现用户修改操作
* 请求类型: PUT
* URL: /user/updateUser
* 参数: User对象~~~~ user的JSON串 (id,phone,email)
* 返回值: SysResult对象
*/
@PutMapping("/updateUser")
public SysResult updateUser(@RequestBody User user){
userService.updateUser(user);
return SysResult.success();
}
1.4)编辑UserServiceImpl
@Override
public User getUserById(Integer id) {
return userMapper.selectById(id);
}
//参数: id主键 电话/邮箱
//语法: 根据对象中不为null的属性当作set条件 id当作唯一where条件
@Override
public void updateUser(User user) {
userMapper.updateById(user);
}
1.5)数据回显
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.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.3)编辑UserController
/**
* 业务: 根据ID删除用户信息
* URL: /user/{id}
* 参数: ID
* 类型: Delete
* 返回值: SysResult对象
*/
@DeleteMapping("/{id}")
public SysResult deleteUserById(@PathVariable Integer id){
userService.deleteUserById(id);
return SysResult.success();
}
2.4 编辑UserServiceImpl
/* 删除用户 */
@Override
public void deleteUser(Integer id) {
userMapper.deleteById(id);
}
三、关于事务说明
1、什么是事务
说明:如果后台服务器执行正常,则业务正确,事务提交. 如果业务执行失败.事务应该回滚.
2、现有代码的业务测试
说明:如图如果程序执行过程中有报错信息,应该实现事务的回滚,但是发现现有代码有2个问题
1:没有添加事物。
2:后台服务器报错之后,用户没有提示
3、添加事物—@Transactional注解
/**
* 关于Spring中的事务策略说明
* 0.Spring内部有事务的管理器,默认开启.
* 1.Spring只负责拦截运行时异常.
* 异常类型: 1.运行时异常 2.检查异常/编译异常 由程序员自己控制
* 2.属性1: rollbackFor 可以配置特殊的异常类型.遇到某种异常回滚.
* 属性2: noRollbackFor 可以配置异常类型 遇到某种异常不回滚.
* @param id
*/
@Override
@Transactional //Spring默认提供的事务控制的注解
public void deleteUser(Integer id){
userMapper.deleteById(id);
}
4、全局异常的处理机制
4.1)常规操作
说明:一般控制异常信息,通常情况下需要添加try-catch 用法.
弊端:所有的方法都需要try-catch的控制,必然导致代码的结构复杂.
解决方案:Spring内部提供了一种规则全局异常的处理机制
4.2)全局异常处理机制
package com.jt.advice;
import com.jt.vo.SysResult;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* Spring为了整合全局异常的处理 开发了如下的注解
* 1.@RestControllerAdvice //定义全局异常的处理类 返回值JSON串
* 2.@ExceptionHandler 标识拦截的异常的类型,如果类型匹配,则执行方法
*/
@RestControllerAdvice
public class MyExceptionAdvice {
//写法:1.运行时异常(通用) 2.自定义异常信息 3.拦截所有异常Exception.class
@ExceptionHandler(RuntimeException.class)
public Object exception(Exception e){
e.printStackTrace(); //输出异常信息
//需求: 如果遇到异常,应该提示用户201/失败信息.
return SysResult.fail();
}
}
四、商品分类业务实现
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'
//使用路由机制 通过children实现路由嵌套, redirect重定向
Vue.use(VueRouter)
const routes = [
{path: '/', redirect: '/login'},
{path: '/login', component: Login},
{path: '/elementUI', component: ElementUI},
//children组件的跳转 在home组件内部进行填充
{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 回调函数 放行/跳转
*/
router.beforeEach((to,from,next) => {
//1.如果用户访问 /login 请求应该放行 终止程序
if(to.path === '/login') {
return next()
}
//2.如果用户访问不是login 则需要校验是否登录 检查是否有token
let token = window.sessionStorage.getItem('token')
if(token !== null && token.length > 0){
return next()
}else {
//没有token 应该跳转到登录页面 "/login"
return next("/login")
}
//if(token) if的一种简化写法 解析: 如果token不为null
//if(!token) 解析: token为null
})
export default router
2、ItemCat 说明
2.1)表设计说明
2.2)ItemCat POJO说明
package com.jt.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@TableName("item_cat")
@Data
@Accessors(chain = true)
public class ItemCat extends BasePojo{
@TableId(type = IdType.AUTO)
private Integer id; //定义主键
private Integer parentId; //定义父级菜单
private String name; //分类名称
private Boolean status; //分类状态 0 停用 1 正常
private Integer level; //商品分类等级 1 2 3
@TableField(exist = false)
private List<ItemCat> children;
}
2.3)商品分类业务说明
2.3.1)京东官网商品分类介绍
2.3.2)如何维护父子关系
表:id 与 parent_id
对象:this–children
2.3.3)三级菜单说明–Sql查询
2.3.4)如何利用对象封装3级菜单结构?
一级菜单
children-----> 二级菜单信息
children-------> 三级菜单信息
3、商品分类列表实现
3.1)业务需求
当用户点击商品分类列表时,应该采用三级商品分类的结构实现列表的展现.
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
},
3.3 业务接口文档说明
请求路径:/itemCat/findItemCatList/{level}
请求类型:get
请求参数:level
参数名称 | 参数说明 | 备注 |
---|---|---|
level | 查询级别 | 1查询一级分类 2查询1-2 级商品分类 3查询1-2-3级商品分类 |
业务说明:查询3级分类菜单数据 要求三层结构嵌套
返回值:SysResult对象
参数名称 | 参数说明 | 备注 |
---|---|---|
status | 状态信息 | 200表示服务器请求成功 201表示服务器异常 |
msg | 服务器返回的提示信息 | 可以为null |
data | 服务器返回的业务数据 | 3级商品分类信息 |
4、编辑ItemCatController
package com.jt.controller;
import com.jt.pojo.ItemCat;
import com.jt.service.ItemCatService;
import com.jt.vo.SysResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@CrossOrigin
@RequestMapping("/itemCat")
public class ItemCatController {
@Autowired
private ItemCatService itemCatService;
/**
* 需求: 查询3级商品分类列表信息
* URL: /itemCat/findItemCatList/{level}
* 参数: level 查询的层级
* 返回值: SysResult对象(List<ItemCat>)
*/
@GetMapping("/findItemCatList/{level}")
public SysResult findItemCatList(@PathVariable Integer level){
List<ItemCat> itemCatList = itemCatService.findItemCatList(level);
return SysResult.success(itemCatList);
}
}
5、编辑ItemCatService—for循环
1.0版本: for循环嵌套结构 暂时不考虑level 最好理解的
package com.jt.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jt.mapper.ItemCatMapper;
import com.jt.pojo.ItemCat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ItemCatServiceImpl implements ItemCatService{
@Autowired
private ItemCatMapper itemCatMapper;
/**
* 业务: 查询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;
}
}