【速通后端crud业务】-员工管理

本文共计8693字,预计阅读时间15分钟内
本文为笔者学习阶段笔记以及经验转化而来,适用于新手,有一定项目经验的可直接跳转“经验随笔”

前言:什么是crud
  • C:create(增)
    R:read   (查)
    U:update(改)
    D:delete (删)

增加员工

  • 设计

    用原员工类来封装也没毛病,但是一般要求使用DTO来封装数据

  • post请求

  • 接口: /admin/employee/addEmployee

  • 三层

    controller

    记得打个log

     /*
          * 新增员工
          */
         @PostMapping("/addEmployee")
         public Result save(@RequestBody EmployeeDTO employeeDTO){
             log.info("新增员工:{}",emmployeeDTO);
             employeeService.save(employeeDTO);
     ​
             return Result.success();
         }
    service

    使用对象属性拷贝来复制对象

    工具类:BeanUtils(org.springframeword.beans)

    设置状态、默认密码、创建时间、修改时间、修改人id(从jwt令牌中解析得到[之前创建令牌的时候就放了一个id的键值对])

    定义员工状态,最好使用StatusConstant里面定义的常量

    获取当前时间使用的是“LocalDateTime.now()”

    设置密码的写法:
    employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));

     public void save(EmployeeDTO employeeDTO) {
             Employee employee = new Employee();
             //把前面的拷贝到后面
             BeanUtils.copyProperties(employeeDTO,employee);
             employee.setStatus(StatusConstant.ENABLE);
             employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));
             employee.setCreateTime(LocalDateTime.now());
             employee.setUpdateTime(LocalDateTime.now());
             Long currentId = BaseContext.getCurrentId();
             employee.setCreateUser(currentId);
             employee.setUpdateUser(currentId);
             employeeMapper.insert(employee);
     ​
         }

    xml(Mapper层略)

     <insert id="insert">
             insert into employee (name,username,password,phone,sex,id_number,status,create_time,update_time,create_user,update_user)
             values (#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{status},#{createTime},#{updateTime},#{createUser},#{updateUser})
     ​
         </insert>
    
    

分页查询

  • get请求

  • 接口: /admin/employee/selectPage

PageHelper插件

底层基于mybatis拦截器

会将limit关键字动态地拼接进sql语句

pom.xml中引入插件

 <!--分页插件的pagehelper-->
 <dependency>
     <groupId>com.github.pagehelper</groupId>
     <artifactId>pagehelper-spring-boot-starter</artifactId>
     <version>1.4.6</version>
     <exclusions>
         <exclusion>
             <artifactId>mybatis</artifactId>
             <groupId>org.mybatis</groupId>
         </exclusion>
     </exclusions>
 </dependency>

时间格式的转换,详见“项目的基础(起步代码)”(暂未转换为博客文章)

注意是

<if test="name != null and name != ''">name like concat('%',#{name},'%')</if>

不是

 <if test="name != null">name like concat('%',#{name},'%')</if>
  • 不使用DTO和PageResult的写法

    • 三层

      controller
       

       /*
            * 分页查询
            * pageNum:当前页码
            * pageSize:每页显示的记录数
            */
           @GetMapping( "selectPage")//路径传参
           public Result selectPage(Employee employee,@RequestParam(defaultValue = "1") Integer pageNum,@RequestParam(defaultValue = "10") Integer pageSize
           ){
               //返回分页数据
               PageInfo<Employee> pageInfo = employeeService.selectPage(employee,pageNum,pageSize);
               return Result.success(pageInfo);
           }
      service

      使用对象属性拷贝来复制对象

      工具类:BeanUtils(导入org.springframeword.beans包)

      设置状态、修改时间

       public PageInfo<Employee> selectPage(Employee employee,Integer pageNum, Integer pageSize) {
               //pageinfo类由pagehelper提供(import com.github.pagehelper.PageInfo;)
               //三行实现分页查询
               PageHelper.startPage(pageNum,pageSize);
               List<Employee> list = aobjectMapper.selectAll(aobject);
               return PageInfo.of(list);
           }
      xml
       <select id="selectAll" resultType="com.bzh.entity.Employee">
               select * from Employee
               <where>
                   <if test="name != null and name != ''">name like concat('%',#{name},'%')</if>
               </where>
               order by id desc
       </select>
  • 使用EmployeePageQueryDTO和PageResult的写法

    (数据格式不是json时,不需要使用@RequestBody)

    • 三层

      controller
       /**
        * 分页查询
        * @param page
        * @param pageSize
        * @param name
        * @return
        */
       @GetMapping("/page")
       public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO){
           log.info("员工分页查询,参数为:{}",employeePageQueryDTO);
           PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO);
           return Result.success(pageResult);
       }
      service

      使用对象属性拷贝来复制对象

      工具类:BeanUtils(org.springframeword.beans)

      设置状态、修改时间、
       

       /**
        * 分页查询
        * @param employeePageQueryDTO
        * @return
        */
       public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
       ​
               PageHelper.startPage(employeePageQueryDTO.getPage(),employeePageQueryDTO.getPageSize());
               //得到(获取)page对象
               Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);
               long total = page.getTotal();
               //从page里面获取list集合
               List<Employee> records = page.getResult();
               return new PageResult(total, records);
           }

         ​  ​  ​  ​

      xml
      PageHelper底层基于mysql关键字limit来查询(select * from employee limit 0,10;) (如果不用pageHelper,就要依据pageNum和pageSize进行计算,然后动态地拼到sql语句中)

       <select id="pageQuery" resultType="com.sky.entity.Employee">
               select * from employee
               <where>
                   <if test="name != null and name != ''">name like concat('%',#{name},'%')</if>
               </where>
               order by create_time desc
           </select>

修改员工信息

账号的启用和禁用

  • post请求
  • 接口: /admin/employee/status/{status}

  • 传参方式: status采用路径传参 id采用地址栏传参

  • (这里1为启用,0为禁用)

controller层记得打log

  • 三层

    controller

    这个接口采用 POST 而不是 PUT 可能是因为:一、状态更新操作具有一定的业务意图,不完全依赖幂等性,POST 更符合这种“提交意图”的语义;二、开发者的习惯或所用框架对 POST 的支持更友好,使得 POST 更易于实现;三、接口的路径设计更倾向于 POST 的语义。总之,虽然 POST 和 PUT 都能完成状态更新的功能,但在这里选用 POST 有其合理性。

     @PostMapping("/status/{status}")
     public Result changeStatus(@PathViriable Integer status,Long id){
         log.info("启用禁用员工账号:{}{}",status,id);
         employeeService.changeStatus(status,id);
         return Result.success();
     }
    service

    使用对象属性拷贝来复制对象

     ​
     public void changeStatus(Integer status ,Long id){
         //
         Employee employee = new Employee();
         employee.setStatus (status);
         employee.setId(id);
         emPloyee.update(employee);
         employeeMapper.update(emp);
     }

    mapper

     /**
      * 根据主键动态更新信息
      * @param employee
      */
      void update(Employee employee);

xml

为了方法的复用性,将update语句写成动态的sql,根据 传入的参数修改不同的字段-

 ​
 <update id="update" parameterType="com.sky.entity.Employee">
     update employee
     <set>
         <if test="name != null">name = #{name},</if>
         <if test="username != null">username = #{username},</if>
         <if test="password != null">password = #{password},</if>
         <if test="phone != null">phone = #{phone},</if>
         <if test="sex != null">sex = #{sex},</if>
         <if test="idNumber != null">id_number = #{idNumber},</if>
         <if test="updateUser != null">update_user = #{updateUser},</if>
         <if test="status != null">status = #{status},</if>
         update_time = #{updateTime}
     </set>
     where id = #{id}
 </update>
 ​

编辑员工信息

查询回显接口

  • 根据id查询员工信息(回显[也可以前端实现])

  • 编辑员工信息

  • get请求

  • 接口: /admin/employee/{id}

  • 三层

    controller
     @GetMapping("/{id}")
     public Result<Employee> getById(@PathVariable Long id){
         
         Employee employee = employeeService.getById(id);
         
         return Result.success(employee);
     }
    service
     public Employee getById(Long id){
         Employee employee = employeeMapper.getById(id);
         employee.setPassword("****");
         return employee;
     }
    xml
    select * from employee where id = #{id}

更新信息接口

  • put请求

  • 接口: /admin/employee

更新对象的时候不要忘了在service层设置updateTime还有updateUser

  • 三层

    controller
     @PutMapping
     public Result update(@RequestBody EmployeeDTO employeeDTO){
         log.info("编辑员工信息:{}",employeeDTO);
         
         employeeService.update(employeeDTO);
         return Result.success();
     }
    service

    使用对象属性拷贝来复制对象

    工具类:BeanUtils(org.springframeword.beans)

     public void update(EmployeeDTO employeeDTO) {
         Employee employee = new Employee();
         BeanUtils.copyProperties(employeeDTO,employee);
         //update要传入的是employee对象,所以属性拷贝一下
         employee.setUpdateTime(LocalDateTime.now());
         employee.setUpdateUser(BaseContext.getCurrentId());
         employeeMapper.update(employee);
     }
    xml
     <update id="update">
             update employee
             <set>
                 <if test="name != null">name = #{name},</if>
                 <if test="username != null">username = #{username},</if>
                 <if test="password != null">password = #{password},</if>
                 <if test="phone != null">phone = #{phone},</if>
                 <if test="sex != null">sex = #{sex},</if>
                 <if test="idNumber != null">id_number = #{idNumber},</if>
                 <if test="updateUser != null">update_user = #{updateUser},</if>
                 <if test="status != null">status = #{status},</if>
                 update_time = #{updateTime}
             </set>
             where id = #{id}
         </update>

构建实体对象的builder写法

(前提:该实体类需要加上@Builder注解)

 Employee employee = new Employee();
     employee.setStatus (status);
     employee.setId(id);
     emPloyee.update(employee);

  • builder写法
    
     Employee employee = Employee.builder()
             .status(status)
             .id(id)
             .build();
    
    

经验随笔

  • xxServiceImpl记得implements xxService接口

  • controller层使用的是xxDTO类去接收前端的json数据

  • id是long类型,用Long id接收

  • controller层的几个常见接口

    @RestController @RequestMapping("/xxx") @Slf4j (@Api(tags = "员工管理相关接口"))

  • mapper层是interface类型,不是class类型 (public interface EmployeeMapper{})

  • 分页查询细节

    • 分页查询的sql有时候需要联表
      错误版本

      正确版本

    • 分页查询页也不要忘了加上查询条件(用where包裹动态sql)

  • 新增-insert

    • 新增的时候注意对照数据表格,看下是否有些字段需要我们在业务层手动填充

      • 注意看一下是否要设置status(最好设置1/0,前端不一定会设置)

      • 注意看一下是否要设置userId

         setUserId(BaseContext.getCurrentId())
      • 注意是否要填充createTime和updateTime

  • 注意给update和insert 的mapper方法上增加@AutoFill注解(自定义,用于自动填充createTime、updateTime)

  • 更新-update

    • 前端传来id、修改一两个字段的更新,不需要查询出来构建完整的对象再更新(因为动态sql里面有判断,不等于null的字段才会更新)

    • update语句中<if test="" >里面写的是属性名、而不是数据库字段,也就是说里面的属性名是驼峰命名格式

    • 非字符串类型的属性不用(不能)写 " and xx != '' ",(updateTime和createTime是LocalDateTime类型,不是String类型,不能写 <if test="createTime != null and createTime != "" "> 这样的判断)

    • 批量更新 (使用foreach标签包裹update语句)

  • 查询select

    • 查询语句末尾记得加上 order by xxx desc

    • 注意是否需要加上status的判断(是否只返回启用中的条目)

    • 查询类接口的返回值注意是否是返回VO类

    • 查询特定数据可能出现查不到的情况,需要分情况处理逻辑

  • 删除

    • 注意检查数据是否能够删除(如status = 1的数据可能不能删除)

      检查status的样例:

       ids.forEach(id -> {
               Setmeal setmeal = setmealMapper.getById(id);
               if(StatusConstant.ENABLE == setmeal.getStatus()){
                   //起售中的套餐不能删除
                   throw new DeletionNotAllowedException(MessageConstant.SETMEAL_ON_SALE);
               }
           });
    • 可以一次删除一个,也可以批量删除

  • sql语句细节

    • foreach语句条目不要 遗漏,这次遗漏了item="id"没看出来导致我调试了半天

    • sql语句中的大于号小于号需要替换

      “<”替换为“&lt”,“>”替换为“&gt;”

  • 附属信息(XDTO有,而X实体类没有的属性) 往往需要采用先删除后插入的方式实现更新(比如员工的工作经验、DishDTO的List<DishFlavor>数据)

  • 对于有附属信息的类的新增操作时 前端传过来的DishDTO里面没有dish的主键id值,需要再xml文件中执行新增操作时采取主键回填的方式获取到

    主键Id

    其中,keyProperty="x",x不能随便写,必须确认这个类中有x属性并且是主键

例如:此处的更新操作中,在更新附属信息之前,给List<DishFlavor>集合里面的DishFlavor补充好属性DishId(因为口味数据可能是新增出来的,dishId没放进去)

  • 对于比较复杂的查询sql,最好在数据库里面调试好、确保当前sql能够查询到想要的结果,再拷贝到xml文件中

对sql进行优化,提升复用性
案例

优化后

2. 

 list方法的复用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值