Java - stage03 - day11 - day12 - 商城用户管理 - MybatisPlus

1.1 子路由

路由占位符

<!-- 定义主页面结构-->
<el-main>
  <!-- 定义路由展现页面-->
  <router-view></router-view>
</el-main>

子路由定义语法

1.2 用户列表实现

1.2.1 页面JS分析

  1. 生命周期函数
//利用钩子函数实现数据查询
mounted(){
  this.getUserList()
}
  1. 获取数据JS
async getUserList(){
        const {data: result} = await this.$http.get('/user/list',{
           params: this.queryInfo
        })
        if(result.status !== 200) return this.$message.error("用户列表查询失败")
        this.userList = result.data.rows
        this.total = result.data.total
        console.log("总记录数:"+this.total)
      }

1.2.2 接口文档

1.2.3 编辑分页对象PageResult

package com.jt.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class PageResult {
    private String query;       //查询数据
    private Integer pageNum;    //页数
    private Integer pageSize;   //条数
    private Long total;         //总数
    private Object rows;        //查询结果
}

1.2.4 前后端数据关联

说明:在后端服务器需要额外的查询总数total及分页的结果rows

1.2.4 编辑UserController

1.3 用户状态修改

1.3.1 业务说明

  1. 说明:当点击开关,则实现状态信息的修改。status=true/false
  2. 请求路径
    http://localhost:8091/user/status/1/false

1.3.2 VUE.JS 作用域插槽

<template slot-scope="scope">
   <el-switch v-model="scope.row.status" @change="updateStatus(scope.row)"
     active-color="#13ce66" inactive-color="#ff4949">
   </el-switch>
</template>

1.3.3 页面JS分析

  1. 前端模板
<el-table-column prop="status" label="状态">
             <template slot-scope="scope">
                <el-switch v-model="scope.row.status" @change="updateStatus(scope.row)"
                  active-color="#13ce66" inactive-color="#ff4949">
                </el-switch>
             </template>
           </el-table-column>
  1. 添加修改事件
async updateStatus(user){
  //实现用户状态修改  注意使用模版字符串  ES6中提出的新用法 ${key}
  //const {data: result} = await this.$http.put('/user/status/'+user.id+'/'+user.status)
  const {data: result} = await this.$http.put(`/user/status/${user.id}/${user.status}`)
  if(result.status !== 200) return this.$message.error("用户状态修改失败!")
  this.$message.success("用户状态修改成功!")
},

1.3.4 接口文档

  • 请求路径 /user/status/{id}/{status}
  • 请求类型 PUT
  • 请求参数: 用户ID/状态值数据
参数名称参数类型参数说明备注信息
idInteger用户ID号不能为null
statusboolean参数状态信息不能为null
  • 返回值结果: SysResult对象
{"status":200,"msg":"服务器调用成功!","data":null}

1.3.5 后端Java

  1. Controller
/**
 * 需求: 根据ID修改状态
 * URL: /user/status/{id}/{status}
 * @param user
 * @return
 */
@PutMapping("/status/{id}/{status}")
public SysResult updateUserStatus(User user) {
    return SysResult.success(userService.updateUserStatus(user));
}
  1. Mapper
<update id="updateUserStatus">
    update user set status=#{status} where id=#{id}
</update>

1.4 用户新增

1.4.1 页面

 <!-- 编辑用户新增对话框 visible.sync 控制对话框的显示-->
    <el-dialog title="添加用户" :visible.sync="dialogVisible" width="50%" @close="closeDialog">

      <!-- 定义用户提交表单数据-->
      <el-form :model="addUserModel" :rules="rules" ref="addUserRef" label-width="100px" class="demo-ruleForm">
        <el-form-item label="用户名" prop="username">
          <el-input v-model="addUserModel.username"></el-input>
        </el-form-item>
        <el-form-item label="密码" prop="password">
          <el-input v-model="addUserModel.password" type="password"></el-input>
        </el-form-item>
        <el-form-item label="密码确认" prop="password2">
          <el-input v-model="addUserModel.password2" type="password"></el-input>
        </el-form-item>
        <el-form-item label="电话" prop="phone">
          <el-input v-model="addUserModel.phone"></el-input>
        </el-form-item>
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="addUserModel.email"></el-input>
        </el-form-item>
      </el-form>

      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="addUserBtn">确 定</el-button>
      </span>
    </el-dialog>

1.4.2 JS

<el-button type="primary" @click="addUserBtn">确 定</el-button>
 //校验用户数据 valid 表示校验是否通过
      addUserBtn(){
        this.$refs.addUserRef.validate(async valid => {
          //如果校验成功 valid=true 如果失败 valid=false
          //如果校验失败 则停止数据
          if(!valid) return

          const {data: result} = await this.$http.post('/user/addUser',this.addUserModel)
          if(result.status !== 200) return this.$message.error("用户新增失败")
          this.$message.success("用户新增成功")
          //关闭对话框
          this.dialogVisible = false
          //重新获取用户列表
          this.getUserList()
        })
      },

1.4.3 接口

1.4.4 后端

  1. Controller
 /**
 * 用户新增
 *  URL:  /user/addUser
 *  参数:  JS对象经过浏览器解析变为JSON串
 *        {"username":"111","password":"222","password2":"222","email":"22@qq.com","phone":"13111111111"}
 *  返回值: SysResult对象
 *      对象转化为JSON @ResponseBody
 *      JSON转化为对象 @RequestBody  规则
 */
@PostMapping("/addUser")
public SysResult saveUser(@RequestBody User user){

    userService.saveUser(user);
    return SysResult.success();
}
  1. UserServiceImpl
/**
 *   业务:实现业务数据封装
 *   1.密码加密
 *   2.设定默认状态
 *   3.设定默认时间
 */
@Override
public void saveUser(User user) {
    String password = user.getPassword();
    String MD5Pass = DigestUtils.md5DigestAsHex(password.getBytes());
    //获取当前时间
    Date date = new Date();
    user.setPassword(MD5Pass).setStatus(true)
            .setCreated(date).setUpdated(date);
    userMapper.saveUser(user);
}
  1. UserMapper.xml
<!--实现user入库操作-->
<insert id="saveUser">
    insert into user value (null,#{username},#{password},#{phone},#{email},#{status},#{created},#{updated})
</insert>

1.5 数据更新回显

1.5.1 页面 JS 分析

  1. 按钮
<template slot-scope="scope">
   <el-button type="primary" icon="el-icon-edit" size="small" @click="updateUserBtn(scope.row)"></el-button>
</template>
  1. JS事件
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.6 用户修改

1.6.1 前端

<span slot="footer" class="dialog-footer">
  <el-button @click="updateDialogVisible = false" >取 消</el-button>
  <el-button type="primary" @click="updateUser">确 定</el-button>
</span>
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()
  })
},

1.6.2 后端

  1. UserController
    @PutMapping("/updateUser")
    public SysResult updateUser(@RequestBody User user) {
        userService.updateUser(user);
        return SysResult.success();
    }
  1. UserMapper
    @Update("update user set phone=#{phone},email=#{email},updated=#{updated} where id=#{id}")
    void updateUser(User user);

1.7 用户删除操作

1.7.1 页面JS

<template slot-scope="scope">
   <el-button type="danger" icon="el-icon-delete" size="small" @click="deleteUser(scope.row)"></el-button>
</template>
async deleteUser(user){
         //1.消息确认框
         const result =  await this.$confirm('此操作将永久删除 '+user.username+', 是否继续?', '提示', {
                   confirmButtonText: '确定',
                   cancelButtonText: '取消',
                   type: 'warning'
                 }).catch(error => error)

         //如果确认  confirm  如果取消 cancel
         if(result !== 'confirm'){
            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. 全局异常处理 / 返回错误信息

2.1 异常说明

将异常报错信息返回给用户

2.2 异常处理

    @DeleteMapping("/{id}")
    public SysResult deleteById(@PathVariable Integer id) {

        //防止与MP方法冲突 业务方法最好添加业务名称
        try {
            userService.deleteUserById(id);
            return SysResult.success();
        }catch(Exception e) {
            e.printStackTrace();
            return SysResult.fail();
        }
    }

说明:
如果业务代码中添加了大量的 try - catch 则会导致业务复杂度变高,不便于维护。

2.3 Spring的全局异常处理机制

package com.jt.exception;

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;
import java.sql.SQLException;

//1. 标识该类是全局异常处理机制,返回值都是JSON串
//  通知: AOP中的技术,解决特定问题
//  特点: 该异常处理机制,只拦截 Controller 层抛出的异常
@RestControllerAdvice
@ControllerAdvice
public class SystemExe {
    /**
     * 说明: 需要为全局异常定义一个方法
     * 要求: 返回统一的业务数据 SysResult
     * 拦截: 指定遇到某种异常实现 AOP 处理
     */
    @ExceptionHandler({RuntimeException.class, SQLException.class})
    public SysResult fail(Exception e) {
        e.printStackTrace();
        return SysResult.fail();
    }
}

3. Spring事务控制

3.1 事务特性

  1. 原子性:事务要么同时成功,要么同时失败(不可分割)
  2. 隔离性:多个事务之间相互独立,互不干扰
  3. 一致性:保证数据的一致 ( 脏读 / 幻读 / 不可重复读 )
  4. 持久性:一旦事务提交,就应该持久化保存

3.2 Spring中默认的事务策略

说明:Spring整合Mybatis之后,业务层执行没有事务的支持,业务出现异常,Mysql也会正常提交

3.3 Spring添加事务

    /**
     * 问题: 出现异常时数据是否真的被删除
     * 解决方案: @Transactional
     * 作用:
     *      1. 默认条件下,只拦截运行时异常
     *      2. rollbackFor: 指定异常的类型回滚 rollbackFor = RuntimeException.class
     *      3. noRollbackFor: 指定异常不回滚 noRollbackFor = RuntimeException.class
     */
//    @Transactional(rollbackFor = RuntimeException.class)
    @Transactional
    @Override
    public void deleteUserById(Integer id) {
//        int a = 1/0;
        userMapper.deleteUserById(id);
//        int a = 1/0;
    }

4 MybatisPlus

4.1 准备新测试环境

在这里插入图片描述

4.2 ORM知识回顾

核心概念:对象关系映射,以对象的方式操作数据库
mybatis回顾:Mybatis是半自动化的ORM映射框架
半自动:Sql是自己手写的,但是结果集映射是自动的
MybatisPlus:全自动的ORM映射框架,对Mybatis的扩展

4.3 MybatisPlus

4.3.1 MP介绍

MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

4.3.2 导入Jar包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>Latest Version</version>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

4.3.3 MybatisPlus配置

#SpringBoot整合mybatisPlus
mybatis-plus:
  #指定别名包
  type-aliases-package: com.jt.pojo
  #加载指定的xml映射文件
  mapper-locations: classpath:/mybatis/mappers/*.xml
  #开启驼峰映射
  configuration:
    map-underscore-to-camel-case: true

4.3.4 Pojo类

package com.jt.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;

@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@TableName("user")     //对象与表名映射
public class User implements Serializable {

    @TableId(type = IdType.AUTO)    //组件自增
    /**
     * 规则:
     *  1.如果数据库中的字段与表中的属性名称一致,则可以省略不写@TableField
     *  2.如果对象与表名一致,则名称可以省略
     */
//    @TableField("id")
    private Integer id;
    private String name;
    private Integer age;
    private String sex;
}

4.5 SpringBoot测试API

需要在某个位置,直接从Spring容器中获取某个对象,该操作Spring提供了专门的单元测试注解

package com.jt.springboot_ssm;

import com.jt.mapper.UserMapper;
import com.jt.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

//只要@Test测试方法执行,则整个Spring容器启动,可以根据自身的需要实现依赖注入
//注意事项:该注解只能在测试类中使用,测试类的包路径必须在主启动类的同包及子包下
@SpringBootTest
class SpringbootSsmApplicationTests {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private UserService userService;

    @Test
    void contextLoads() {
//        userMapper.insert();
    }
}

4.6 MybatisPlus 工作原理

核心:以对象的方式操作数据库
调用步骤:
1. 用户执行userMapper.insert(user);
2. 根据继承关系 BaseMapper.inesrt(user);
3. MP在内部生成SQL之后交给Mybatis调用最终实现数据库操作
核心问题: MP是如何动态生成SQL语句的
例子:
Sql1:insert into user(字段名……) value(属性值……)
原理:
1. 用户调用接口方法 userMapper.insert(User)方法
2. 根据UserMapper的接口找到父级接口BaseMapper
3. 根据父级接口动态获取当前接口的泛型对象T
4. 根据泛型T 获取指定的注解@TableName(“demo_user”),之后获取表名demo_user
5. 根据泛型对象T,获取其中的属性,之后再找到属性的注解@TableField(“id”),之后再次获取注解的值, 即字段名称.
6. 根据字段名称,获取对应属性的值.
7. 根据Sql拼接 形成最终的可以执行的Sql.
8. MP将生成的Sql交给Mybatis执行入库操作.

4.6.1 MybatisPlus 入门案例

@Test
public void insertUser() {
    User user = new User();
    user.setId(null).setName("MybatisPlus").setAge(10).setSex("男");
    userMapper.insert(user);
}

MybatisPlus 新增自动填充

@Data
@Accessors(chain=true)
public class BasePojo implements Serializable{
	//新增操作时 自动填充
	@TableField(fill = FieldFill.INSERT)
	private Date created;
	//新增和修改操作时自动填充
	@TableField(fill = FieldFill.INSERT_UPDATE)
	private Date updated;
}

4.7.1 根据Id查询数据

/**
     * 学习技巧: MP设计思想!!!!  对象
     * 查询Id=1的用户
     */
    @Test
    public void selectById() {
        int id = 1;
        User user = userMapper.selectById(id);
        System.out.println(user);
    }

4.7.2 根据name和sex查询数据

 /**
     * 查询name="大乔",sex="女"的用户
     * 规则: 根据对象中不为null的属性进行业务操作
     * 语法:
     *      1.QueryWrapper条件构造器 动态拼接where条件
     *      2.默认的关系连接符 and
     * 例子:
     *      select * from demo_user where xx=xx and xx=xx
     */
    @Test
    public void selectByNS() {
       User user = new User();
       user.setName("大乔").setSex("女");
       QueryWrapper<User> queryWrapper = new QueryWrapper<>(user);
       List<User> list = userMapper.selectList(queryWrapper);
        System.out.println(list);
    }

4.7.3 利用QueryMapper查询数据

    /**
     * 根据对象中不为null的属性进行查询
     * 语法:
     *      1.QueryWrapper 条件构造器    动态拼接where条件
     *      2.默认的关系连接符 and
     *      3.根据对象中部位null的属性进行业务操作
     * 例子:
     *      select * from demo_user where xx=xx and xx=xx
     */
    @Test
    public void selectByNS() {
        User user = new User();
        user.setName("大乔").setSex("女");
        QueryWrapper<User> queryWrapper = new QueryWrapper<>(user);
        List<User> list = userMapper.selectList(queryWrapper);
        System.out.println(list);
    }

    /**
     * 方式2:利用条件构造器,构建条件
     * 说明:
     *      1. eq =
     *      2. gt >
     *      3. lt <
     *      4. ge (gteq) >=
     *      5. le <=
     *      6. ne !=
     */
    @Test
    public void selectByNS2() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name", "大乔")
                    .eq("sex", "女");
//                  .or()
        List<User> list = userMapper.selectList(queryWrapper);
        System.out.println(list);
    }
    
/**
     * 需求: 查询age>18岁的用户,并且sex=男.
     */
    @Test
    public void selectByAS() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.gt("age",18)
                    .eq("sex","男");
        List<User> list = userMapper.selectList(queryWrapper);
        System.out.println(list);
    }

4.7.4 like关键字

/**
     * 需求: 查询name中包含"君",性别="女"
     * Sql:  where name like "%君%"
     * 需求2: 查询name中以"君"结尾的,性别="女"  like "%君"
     */
    @Test
    public void selectLike() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //queryWrapper.like("name","君")   //"%君%"
        queryWrapper.likeLeft("name","君") //"%君"
                    .eq("sex","女");
        List<User> list = userMapper.selectList(queryWrapper);
        System.out.println(list);
    }

4.7.5 in关键字

 /**
     * 需求: 查询id=1,3,4,5的数据 并且按照年龄降序排列
     * 规则: 基本类型有没有方法? 所以使用包装类型
     *      面向对象开发
     */
    @Test
    public void selectIds() {
        Integer[] ids = {1,3,4,5};
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("id",ids)
                    .orderByDesc("age");
        List<User> list = userMapper.selectList(queryWrapper);
        System.out.println(list);
    }

4.7.6 查询第一列字段

/**
     * 需求:
     *  只想获取第一列数据(主键),sex="女"
     * 用法: .selectObjs(queryWrapper);
     * 实际用途: 做关联查询时可以使用
     */
    @Test
    public void selectObjs() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("sex","女");
        List list = userMapper.selectObjs(queryWrapper);
        System.out.println(list);
    }

4.7.7 动态Sql实现

/**
     * 说明: 根据不为null的属性当做where 动态sql实现
     * 需求: 查询age>18岁,sex=女的用户
     * 参数说明:
     *  1. boolean condition, true时,当前的条件才会成立
     *                        false  该条件不拼接.
     *  2.R column 字段信息
     *  3.Object val 值
     * 判断字符串API:
     *      Spring提供的API StringUtils.hasLength(sex);
     */
    @Test
    public void selectList2() {
        Integer age = null;
        String  sex = "女";
        //boolean flag = sex !=null && "".equals(sex);
        boolean flag = StringUtils.hasLength(sex);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.gt(age!=null, "age",age )
                    .eq(flag,"sex",sex);
        List<User> list = userMapper.selectList(queryWrapper);
        System.out.println(list);
    }

5 总结

5.1 注解总结

  1. 全局异常处理机子和
    1. @RestControllerAdvice 标识全局异常处理
    2. @ExceptionHandler({RuntimeException.class}) 拦截指定的异常类型
  2. 事务控制注解
    @Transactional 控制事务 更新操作
  3. MP映射注解
    1. @TableName(“demo_user”) //对象与表名映射
    2. @TableId(type = IdType.AUTO) //主键自增
    3. @TableField(“age”) //实现属性与字段映射
  4. 测试类注解
    @SpringBootTest 可以引入注入spring容器中的对象之后进行单元测试
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值