一、部门管理

1、准备工作

(1)开发规范

【1】开发模式:需求分析-接口设计-前后端并行开发-测试-联调

【2】Restful风格

基础Rest风格开发的功能接口也叫RestFul风格接口

特点:url定位资源,http动词描述操作(get查询 post新增 put修改 delete)

image

Apifox

一款集成了Api文档、Api调试、Api Mock、Api测试的一体化协作平台

作用:接口文档管理、接口请求测试、Mock服务

2、查询部门

image

 

(1)实现思路

  • Controller层,负责接收前端发起的请求,并调用service查询部门数据,然后响应结果。
  • Service层,负责调用Mapper接口方法,查询所有部门数据。
  • Mapper层,执行查询所有部门数据的操作

(2)限制请求方式

@RequestMapping(value="/depts",method=RequestMethod.GET)
企业开发中多用下面的方式:
@GetMapping("/depts")
@DeleteMapping
@PutMapping
@PostMapping
//@RequestMapping(value = "/depts",method = RequestMethod.GET)
    @GetMapping("/depts")//企业开发中主要用这个
    public Result list() {
        List<Dept> deptList = deptService.findAll();
        return Result.success(deptList);
    }

 

 (3)数据封装

image

 实体类属性名和数据库表查询返回的字段名一致,mybatis会自动封装。如果实体类属性名和数据库表查询返回的字段名不一致,不能自动封装。

解决方案:
  • 手动结果映射
@Results({@Result(column = "create_time", property = "createTime"),
          @Result(column = "update_time", property = "updateTime")})
@Select("select id, name, create_time, update_time from dept")
public List<Dept> findAll();
  • 起别名
@Select("select id, name, create_time createTime, update_time updateTime from dept")
public List<Dept> findAll();
  • 开启驼峰命名(推荐)
在application.yml中做如下配置,开启开关(xxx_abc-->xxxAbc)
mybatis:
  configuration:
    map-underscore-to-camel-case: true

(4)前后端联调

反向代理是一种网络架构,通过代理服务器为后端的服务器做代理,客户端的请求直接请求代理服务器,然后转给后端的服务器(安全、灵活、负载均衡)

image

1). 安全:由于后端的tomcat服务器一般都会搭建集群,会有很多的服务器,把所有的tomcat暴露给前端,让前端直接请求tomcat,对于后端服务器是比较危险的。
2). 灵活:基于nginx的反向代理实现,更加灵活,后端想增加、减少服务器,对于前端来说是无感知的,只需要在nginx中配置即可。
3). 负载均衡:基于nginx的反向代理,可以很方便的实现后端tomcat的负载均衡操作。

 前端请求地址是http://localhost:90/api/depts

image

  •  location:用于定义匹配特定uri请求的规则。
  • ^~ /api/:表示精确匹配,即只匹配以/api/开头的路径。
  • rewrite:该指令用于重写匹配到的uri路径。
  • proxy_pass:该指令用于代理转发,它将匹配到的请求转发给位于后端的指令服务器。

3、删除部门

image

 

(1)简单参数接收

方式一: 原始的HttpServletRequest对象获取请求参数。不推荐,代码繁琐

   //方式一: 原始的HttpServletRequest对象获取请求参数。不推荐,代码繁琐
   @DeleteMapping("/depts")
    public Result delete(HttpServletRequest request)
    {
        String idStr=request.getParameter("id");
        Integer id=Integer.parseInt(idStr);
        System.out.println("部门id":+id);
        deptService.delete(id);
        return Result.success();
    }

方式二:通过spring提供的@RequestParam注解,将请求参数绑定给方法形参

注意事项:一旦声明了@RequestParam,该参数在请求时必须传递(默认required为true),不传递报错。如果参数可选,可以将required属性设置为false
    //方式二:通过spring提供的@RequestParam注解,将请求参数绑定给方法形参
    //注意事项:一旦声明了@RequestParam,该参数在请求时必须传递(默认required为true),不传递报错。
    //如果参数可选,可以将required属性设置为false
    @DeleteMapping("/depts")
    public Result delete(@RequestParam(value = "id",required = false) Integer deptId)
    {
        deptService.delete(deptId);
        System.out.println("部门id:"+deptId);
        return Result.success();
    }

方式三:如果请求参数名和形参变量名相同,可以省略@RequestParam(推荐)

 //方式三:如果请求参数名和形参变量名相同,可以省略@RequestParam
    @DeleteMapping("/depts")
    public Result delete(Integer id)
    {
        deptService.deleteById(id);
        System.out.println("部门id:"+id);
        return Result.success();
    }

4、新增

image

 

(1)json参数接收

  • JSON格式的参数,通常会使用一个实体对象进行接收 。
  • 规则:JSON数据的键名与方法形参对象的属性名相同,并需要使用@RequestBody注解标识。

@RequestBody 将json格式的请求参数封装到一个对象中

//新增部门
    @PostMapping("/depts")
    public Result add(@RequestBody Dept dept) {
        System.out.println("新增部门:" + dept);
        deptService.add(dept);
        return Result.success();
    }

5、修改

(1)查询回显

/depts/{id}

路径参数

通过请求URL直接传递参数,使用{…}来标识该路径参数,需要使用 @PathVariable获取路径参数.
如果路径参数名与controller方法形参名称一致,@PathVariable注解的value属性是可以省略的。

image

 

image

 

    /**
     * 根据Id查询部门
     */
    //如果路径参数名与controller方法形参名称一致,@PathVariable注解的value属性是可以省略的。
    @GetMapping("/depts/{id}")
    public Result getInfo(@PathVariable Integer id) {
        System.out.println("获取的id:" + id);
        Dept dept = deptService.getById(id);
        return Result.success(dept);
    }

 

(2)修改数据

/**
     * 修改部门
     */
    @PutMapping("/depts")
    public Result update(@RequestBody Dept dept) {
        deptService.update(dept);
        return Result.success();
    }

完整的请求路径:类上的@RequestMapping 的value属性+方法上的@RequestMapping的value属性

@RequestMapping注解可以加在类上(可选)和方法上

package com.ithema.controller;

import com.ithema.pojo.Dept;
import com.ithema.pojo.Result;
import com.ithema.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController//表示当前类是请求处理类
@RequestMapping("/depts")
public class DeptController {
    @Autowired
    private DeptService deptService;

    //@RequestMapping(value = "/depts",method = RequestMethod.GET)

    /**
     * 查询所有部门
     */
    @GetMapping//企业开发中主要用这个
    public Result list() {
        List<Dept> deptList = deptService.findAll();
        return Result.success(deptList);
    }
 
    //方式三:如果请求参数名和形参变量名相同,可以省略@RequestParam

    /**
     * 根据id删除部门
     */
    ///depts?id=1
    @DeleteMapping
    public Result delete(Integer id) {
        deptService.deleteById(id);
        System.out.println("部门id:" + id);
        return Result.success();
    }

    /**
     * 新增部门
     */
    //JSON格式的参数,用一个实体对象进行接收,
    //JSON数据的键名与方法形参对象的属性名相同,并需要使用@RequestBody注解标识。
    @PostMapping
    public Result add(@RequestBody Dept dept) {
        System.out.println("新增部门:" + dept);
        deptService.add(dept);
        return Result.success();
    }


    /**
     * 根据Id查询部门
     */
    //如果路径参数名与controller方法形参名称一致,@PathVariable注解的value属性是可以省略的。
    @GetMapping("/{id}")
    public Result getInfo(@PathVariable Integer id) {
        System.out.println("获取的id:" + id);
        Dept dept = deptService.getById(id);
        return Result.success(dept);
    }

    /**
     * 修改部门
     */
    @PutMapping
    public Result update(@RequestBody Dept dept) {
        deptService.update(dept);
        return Result.success();
    }

}

6、日志技术

image

  • JUL:这是JavaSE平台提供的官方日志框架,也被称为JUL。配置相对简单,但不够灵活,性能较差。
  • Log4j:一个流行的日志框架,提供了灵活的配置选项,支持多种输出目标。
  • Logback:基于Log4j升级而来,提供了更多的功能和配置选项,性能由于Log4j。
  • Slf4j:(Simple Logging Facade for Java)简单日志门面,提供了一套日志操作的标准接口及抽象类,允许应用程序使用不同的底层日志框架。

Logback入门

准备工作:引入logback的依赖(springboot中无需引入,在springboot中已经传递了此依赖)
 引入配置文件 logback.xml (资料中已经提供,拷贝进来,放在 src/main/resources 目录下; 或者直接AI生成)
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d 表示日期,%thread 表示线程名,%-5level表示级别从左显示5个字符宽度,%logger显示日志记录器的名称, %msg表示日志消息,%n表示换行符 -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern>
        </encoder>
    </appender>

    <!-- 系统文件输出 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 日志文件输出的文件名, %i表示序号 -->
            <FileNamePattern>D:/tlias-%d{yyyy-MM-dd}-%i.log</FileNamePattern>
            <!-- 最多保留的历史日志文件数量 -->
            <MaxHistory>30</MaxHistory>
            <!-- 最大文件大小,超过这个大小会触发滚动到新文件,默认为 10MB -->
            <maxFileSize>10MB</maxFileSize>
        </rollingPolicy>

        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d 表示日期,%thread 表示线程名,%-5level表示级别从左显示5个字符宽度,%msg表示日志消息,%n表示换行符 -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern>
        </encoder>
    </appender>

    <!-- 日志输出级别 -->
    <root level="info">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE" />
    </root>
</configuration>

 

 
public class LogTest {
    
    //定义日志记录对象
    private static final Logger log = LoggerFactory.getLogger(LogTest.class);

    @Test
    public void testLog(){
        log.debug("开始计算...");
        int sum = 0;
        int[] nums = {1, 5, 3, 2, 1, 4, 5, 4, 6, 7, 4, 34, 2, 23};
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
        }
        log.info("计算结果为: "+sum);
        log.debug("结束计算...");
    }

}

(2)Logback配置文件

配置文件名:logback.xml

该配置文件是对logback日志框架输出的日志进行控制,可以来配置输出的格式、位置以及开关等

常用的两种输出日志的位置:控制台、系统文件

image

开启日志(ALL),关闭日志(OFF)

<!-- 日志输出级别 -->
<root level="ALL">
    <!--输出到控制台-->
    <appender-ref ref="STDOUT" />
    <!--输出到文件-->
    <appender-ref ref="FILE" />
</root>

image

(3)Logback日志级别

日志级别指的是日志信息的类型,日志都会分级别,常见的日志级别如下(优先级由低到高):

 image

(4)@Slf4j以及lombook日志的占位符log.info("xxx{}",id)

 可以在配置文件logback.xml中,灵活的控制输出那些类型的日志。(大于等于配置的日志级别的日志才会输出)

<!-- 日志输出级别 -->
<root level="info">
    <!--输出到控制台-->
    <appender-ref ref="STDOUT" />
    <!--输出到文件-->
    <appender-ref ref="FILE" />
</root>
@Slf4j//塞肋fourj  相当于
private static final Logger log= LoggerFactory.getLogger(当前类.class);
package com.ithema.controller;

import com.ithema.pojo.Dept;
import com.ithema.pojo.Result;
import com.ithema.service.DeptService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
//塞肋fourj
@Slf4j
@RestController//表示当前类是请求处理类
@RequestMapping("/depts")
public class DeptController {
   //private static final Logger log= LoggerFactory.getLogger(DeptController.class);
    @Autowired
    private DeptService deptService; @DeleteMapping
    public Result delete(Integer id) {
        deptService.deleteById(id);
        //System.out.println("部门id:" + id);
        log.info("部门d:{}",id);//拼接字符串,{}占位符。几个变量几个占位符
        return Result.success();
    }
}

二、多表关联(用户管理)

一对一

一对多

多对多

1、多表关系

(1)一对多

【1】外键约束

fk开头:fk_emp_dept_id

-- 创建表时指定
create table 表名(
字段名 数据类型,
……
[constraint] [外键名称] foreign key (外键字段名) references 主表(字段名));

-- 建表后,添加外键
alter table 表名 add constraint 外键名 foreign key(外键名) references 主表(字段名);

 

create table user_info(
    id int primary key auto_increment comment 'id',
    name varchar(20) comment '名字',
    dept_id int comment '部门id',
    constraint fk_user_dept foreign key(dept_id)  references dept(id)
)

 

在视图修改:

image

 【2】现在企业大多数弃用物理外键,采用逻辑外键

image

(2)一对一

在任意一方加入外键,关联另一方的主键,并且设置外键作为唯一的(unique)

例如用户与身份证信息的关系

 (3)多对多

例如 学生和课程

建立 第三张中间表,中间表至少包含两个外键,分别关联两方主键

2、多表查询

(1)连接查询

【1】内连接

隐式内连接(推荐)

select *  from user ,dept where user.dept_id=dept.id

显示内连接

select *  from user [inner] join dept on  user.dept_id=dept.id

【2】外连接

左右连接

(2)子查询

子查询的外部语句可以是insert、update、delete、select的任何一个。最常见的是select。

分类:

标量子查询:子查询返回的结果是单个值

列子查询:子查询返回的是一列

行子查询:子查询返回的是一行

-- 行子查询
-- 查询与 "李忠" 的薪资 及 职位都相同的员工信息 ;
select *from emp where salary = (select salary from emp where name = '李忠') and job = (select job from emp where name = '李忠');

-- 优化 select * from emp where (salary,job)=(5000,5); select * from emp where (salary,job)=(select salary,job from emp where name = '李忠');

 

表子查询:子查询返回的结果是多行多列

-- 获取每个部门中薪资最高的员工信息
select  * from emp where  (dept_id,salary)in
(select dept_id, max(salary) as salary  from emp group by dept_id);

select * from emp e,(select dept_id, max(emp.salary)  max_sal from emp group by  dept_id) as t
where e.dept_id=t.dept_id and e.salary=t.max_sal

 

-- 5. 查询工资 低于本部门平均工资的员工信息 。
-- 5.1查询每个部门的平均薪资
select avg(salary) as avg_sal ,emp.dept_id from emp  group by dept_id ;
-- 5.2查询工资,低于平均工资
select  emp.* from emp ,(select avg(salary) as avg_sal ,emp.dept_id from emp  group by dept_id ) t
where emp.dept_id=t.dept_id and salary<t.avg_sal

3、分页查询

(1)原始方式

  • 前端传递参数:page:当前页码,pageSize 每页展示记录数。Controller 的方法设置默认值 @RequestParam(defaultValue = "1")
  • 后端返回结果:total:总记录数,rows:结果列表
package com.ithema.controller;

import com.ithema.pojo.PageResult;
import com.ithema.pojo.Result;
import com.ithema.service.EmpService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * 员工管理控制器
 */
@RestController
@Slf4j
@RequestMapping("/emps")
public class EmpController {
    @Autowired
    private EmpService empService;

    @GetMapping
    public Result page(@RequestParam(defaultValue = "1") Integer page,  @RequestParam( defaultValue = "10")Integer pageSize)//@RequestParam(value = "start")
    {
        log.info("分页查询:{},{}", page, pageSize);
        PageResult pageResult = empService.page(page, pageSize);
        return Result.success(pageResult);
    }
}

 

package com.ithema.service.impl;

import com.ithema.mapper.EmpMapper;
import com.ithema.pojo.Emp;
import com.ithema.pojo.PageResult;
import com.ithema.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class EmpServiceImpl implements EmpService {
    @Autowired
    private EmpMapper empMapper;

    /**
     * 分页查询员工数据
     *
     * @param page     页码
     * @param pageSize 每页记录数
     */
    @Override
    public PageResult page(Integer page, Integer pageSize) {
        Long total = empMapper.count();
        Integer start = (page - 1) * pageSize;
        List<Emp> list = empMapper.list(start, pageSize);
        return new PageResult( total, list);
    }
}

 

package com.ithema.mapper;

import com.ithema.pojo.Emp;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
 * 员工管理
 */
@Mapper
public interface EmpMapper {
    @Select("")
    List<Emp> findAll();
    /**
     * 查询总记录数
     */
    @Select("select count(*) from emp left join dept d on d.id = emp.dept_id")
    long count();
    /**
     * 分页查询员工数据
     */
    @Select("select emp.* ,d.name as deptName from emp left join dept d on d.id = emp.dept_id  order by emp.update_time desc limit #{start},#{pageSize}")
    List<Emp> list(Integer start, Integer pageSize);
}

 

(2)PageHelper分页插件

PageHelper是第三方提供的在Mybatis框架汇中用来实现分页的插件,用来简化分页操作,提高开发效率

【1】使用步骤

image

 

<!--分页插件PageHelper-->
<dependency>
       <groupId>com.github.pagehelper</groupId>
       <artifactId>pagehelper-spring-boot-starter</artifactId>
       <version>1.4.7</version>
</dependency>

 【2】PageHelper实现机制

PageHelper会拦截Sql的执行,改造sql语句。最终生成两条sql:

select count(0) from emp ……-- 把order by去掉

select ……from ……limit ?,?

最后将执行的sql语句结果封装到Page<T>中

注意事项:定义的sql语句结尾不能加分号

                 PageHelper仅仅能对紧跟在其后面的第一个查询语句进行分页处理

package com.ithema.mapper;

import com.ithema.pojo.Emp;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
 * 员工管理
 */
@Mapper
public interface EmpMapper {
   
    @Select("select emp.* ,d.name as deptName from emp left join dept d on d.id = emp.dept_id  order by emp.update_time desc")
    List<Emp> list();
}

 

package com.ithema.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.ithema.mapper.EmpMapper;
import com.ithema.pojo.Emp;
import com.ithema.pojo.PageResult;
import com.ithema.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class EmpServiceImpl implements EmpService {
    @Autowired
    private EmpMapper empMapper;

    /**
     * 分页查询员工数据
     *
     * @param page     页码
     * @param pageSize 每页记录数
     * 注意事项:
     *                 1、定义的sal语句结尾不能加分号
     *                 2、PageHelper仅仅能对紧跟在其后的第一个查询语句进行分页处理
     */
    @Override
    public PageResult page(Integer page, Integer pageSize) {
        //1、设置分页参数PageHepler
        PageHelper.startPage(page, pageSize);
        //2、执行查询
        List<Emp> list = empMapper.list();
        //3、解析查询结果并封装
        Page<Emp> p = (Page<Emp>) list;
       return   new PageResult<Emp>(p.getTotal(), p.getResult());
    }
}

4、条件分页查询

(1)DateTimeFormat

日期类型的参数在前端传递的时候可能多样,通过注解@DateTimeFormat("yyyy-MM-dd")来指定前端传递的日期格式

(2)contact

用于sql语句拼接。#{}不能出现在引号之内。所以 like concat('%', #{name}, '%')

(3)动态sql

随着用户的输入或外部条件的变化而变化的SQL语句,我们称之为动态SQL

<if>判断条件是否成立,如果条件为true,则拼接sql

<where>根据查询条件,来生成where关键字,并会自动去除条件前多余的and或者or;若where之后的条件都不成立,则不会生成where 

<?xml version="1.0" encoding="UTF-8" ?><!--Mybatis中文网-入门-探究映射sql-粘贴-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ithema.mapper.EmpMapper">
    <select id="list" resultType="com.ithema.pojo.Emp">
        select emp.* ,d.name as deptName from emp left join dept d on d.id = emp.dept_id
        <where>
            <if test="gender != null">
                emp.gender = #{gender}
            </if>
            <if test="name != null and name!=''">
                and emp.name like concat('%',#{name},'%')
            </if>
            <if test="start!=null and end!=null">
                and emp.entry_date between #{start} and #{end}
            </if>
        </where>
        order by emp.update_time desc
    </select>
</mapper>

 

package com.ithema.pojo;

import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;

import java.time.LocalDate;
@Data
public class EmpQueryParam {
    private Integer page=1;//当前页码
    private Integer pageSize=10;//每页展示记录数
    private String name;// 姓名
    private Integer gender;// 性别
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate start;//入职开始日期
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate end;//入职结束日期
}
package com.ithema.controller;

import com.ithema.pojo.EmpQueryParam;
import com.ithema.pojo.PageResult;
import com.ithema.pojo.Result;
import com.ithema.service.EmpService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * 员工管理控制器
 */
@RestController
@Slf4j
@RequestMapping("/emps")
public class EmpController {
    @Autowired
    private EmpService empService;
   
    @GetMapping
    public Result page(EmpQueryParam  param)
    {
        log.info("分页查询:{}", param);
        PageResult pageResult = empService.page(param);
        return Result.success(pageResult);
    }
}

 

 

package com.ithema.service.impl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.ithema.mapper.EmpMapper;
import com.ithema.pojo.Emp;
import com.ithema.pojo.EmpQueryParam;
import com.ithema.pojo.PageResult;
import com.ithema.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class EmpServiceImpl implements EmpService {
    @Autowired
    private EmpMapper empMapper;

    /**
     * 分页查询员工数据
     *
     * @param page     页码
     * @param pageSize 每页记录数
     * 注意事项:
     *                 1、定义的sal语句结尾不能加分号
     *                 2、PageHelper仅仅能对紧跟在其后的第一个查询语句进行分页处理
     */   
    @Override
    public PageResult page(EmpQueryParam param) {
        PageHelper.startPage(param.getPage(), param.getPageSize());
        List<Emp> list = empMapper.list(param);
        Page<Emp> p = (Page<Emp>) list;
        return new PageResult<Emp>(p.getTotal(), p.getResult());
    }
}

 

package com.ithema.mapper;

import com.ithema.pojo.Emp;
import com.ithema.pojo.EmpQueryParam;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;

/**
 * 员工管理
 */
@Mapper
public interface EmpMapper {    
    /**
     * 分页查询员工数据
     */
    List<Emp> page( EmpQueryParam param);
}

 5、新增员工

(1)插入数据之后,获取主键值:主键返回@Options(useGeneratedKeys = true, keyProperty = "id")

useGeneratedKeys :使用Mybaits中生成的主键,

keyProperty :生成的主键赋值给哪个属性

(2)<foreach>动态SQL标签

  • 作用:遍历集合/数组
  • 属性:

collection:集合名称

item集合中的元素

separator:每次遍历还是用的分分隔符(可选)

open:遍历开开始前拼接的片段(可选)

close:遍历结束后拼接的片段(可选)

image

 

image

package com.ithema.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

@Data
public class Emp {
    private Integer id; //ID,主键
    private String username; //用户名
    private String password; //密码
    private String name; //姓名
    private Integer gender; //性别, 1:男, 2:女
    private String phone; //手机号
    private Integer job; //职位, 1:班主任,2:讲师,3:学工主管,4:教研主管,5:咨询师
    private Integer salary; //薪资
    private String image; //头像
    private LocalDate entryDate; //入职日期
    private Integer deptId; //关联的部门ID
    private LocalDateTime createTime; //创建时间
    private LocalDateTime updateTime; //修改时间
    private String deptName;//部门名称
    private List<EmpExpr>   exprList;//工作经历
}

 

   /**
     * 新增员工
     */
    @PostMapping
    public Result save(@RequestBody Emp emp) {
        log.info("新增员工:{}", emp);
        empService.save(emp);
        return Result.success();
    }
package com.ithema.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.ithema.mapper.EmpExprMapper;
import com.ithema.mapper.EmpMapper;
import com.ithema.pojo.Emp;
import com.ithema.pojo.EmpQueryParam;
import com.ithema.pojo.PageResult;
import com.ithema.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.List;

@Service
public class EmpServiceImpl implements EmpService {
    @Autowired
    private EmpMapper empMapper;
    @Autowired
    private EmpExprMapper empExprMapper;
 
    @Override
    public PageResult page(EmpQueryParam param) {
        PageHelper.startPage(param.getPage(), param.getPageSize());
        List<Emp> list = empMapper.list(param);
        Page<Emp> p = (Page<Emp>) list;
        return new PageResult<Emp>(p.getTotal(), p.getResult());
    }
    @Override
   public void save(  Emp  emp )
    {
        //1、保存员工的基本信息
        emp.setCreateTime( LocalDateTime.now());
        emp.setUpdateTime( LocalDateTime.now());
        empMapper.insert(emp);//使用Mybatis生成主键功能,emp中的id属性会自动赋值

        //2、保存员工的工作经历
        if(!CollectionUtils.isEmpty(emp.getExprList()))
        {
            emp.getExprList().forEach(expr -> {
                expr.setEmpId(emp.getId());
            });
            empExprMapper.insertBatch(emp.getExprList());
        }
    }
}
package com.ithema.mapper;

import com.ithema.pojo.Emp;
import com.ithema.pojo.EmpQueryParam;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
 * 员工管理
 */
@Mapper
public interface EmpMapper {
  
    List<Emp> list(EmpQueryParam param);
    //使用Mybaits中生成的主键,当前获取的主键往对象的哪个属性中来封装
    @Options(useGeneratedKeys = true, keyProperty = "id")
    @Insert("insert into emp(username,name,gender,phone,image,job,entry_date,dept_id,create_time,update_time) values(#{username},#{name},#{gender},#{phone},#{image},#{job},#{entryDate},#{deptId},#{createTime},#{updateTime})")
    void insert(Emp emp);
}
package com.ithema.mapper;

import com.ithema.pojo.EmpExpr;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface EmpExprMapper {

    void insertBatch(List<EmpExpr> exprList);
}
<?xml version="1.0" encoding="UTF-8" ?><!--Mybatis中文网-入门-探究映射sql-粘贴-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ithema.mapper.EmpMapper">
    <select id="list" resultType="com.ithema.pojo.Emp">
        select emp.* ,d.name as deptName from emp left join dept d on d.id = emp.dept_id
        <where>
            <if test="gender != null">
                emp.gender = #{gender}
            </if>
            <if test="name != null and name!=''">
                and emp.name like concat('%',#{name},'%')
            </if>
            <if test="start!=null and end!=null">
                and emp.entry_date between #{start} and #{end}
            </if>
        </where>
        order by emp.update_time desc
    </select>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?><!--Mybatis中文网-入门-探究映射sql-粘贴-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ithema.mapper.EmpExprMapper">
    <insert id="insertBatch">
        insert into emp_expr(emp_id,company,begin,end,job) values
        <foreach collection="exprList" item="expr" separator=",">
            (#{expr.empId},#{expr.company},#{expr.begin},#{expr.end},#{expr.job})
        </foreach>
    </insert>

</mapper>

 

或者

image

 

(3)判断集合是否为空 CollectionUtils.isEmpty(emp.getExprList())

三、事务管理

1、介绍&操作

(1)要么同时成功,要么同时失败

注意:默认MySQL的事务是自动提交的,也就是说,当执行一条DML语句,MySQL会立即隐式的提交事务

(2)操作步骤

【1】开始事务start transanction/begin

【2】提交事务commit

【3】回滚事务rollback

2、Spring事务管理

(1)控制事务@Transactional  [trænˈzækʃən(ə)l]

作用:方法运行前,给开启事务,运行完毕后根据运行的结果来提交或者回滚事务

位置:方法上(方法中多次进行数据库的操作),类上,接口上(所有的实现类中的方法都进行事务操作)

(2)事务进阶

【1】rollbackfor(用于控制出现何种远异常类型,回滚事务)

  • 在Spring的事务管理中,默认只有运行时异常 RuntimeException才会回滚。
  • 如果还需要回滚指定类型的异常,可以通过rollbackFor属性来指定。
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void save(Emp emp) {
        //1、保存员工的基本信息
        emp.setCreateTime(LocalDateTime.now());
        emp.setUpdateTime(LocalDateTime.now());
        empMapper.insert(emp);//使用Mybatis生成主键功能,emp中的id属性会自动赋值

        //2、保存员工的工作经历
        if (!CollectionUtils.isEmpty(emp.getExprList())) {
            emp.getExprList().forEach(expr -> {
                expr.setEmpId(emp.getId());
            });
            empExprMapper.insertBatch(emp.getExprList());
        }
    }

在application.yml中配置Mybatis日志

spring:
  application:
    name: tlias-web-management
  #mysql连接配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/tlias
    username: root
    password: root
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true #开启驼峰命名映射开关
#spring事务管理日志
logging:
  level:
    org.springframework.jdbc.support.JdbcTransactionManager: debug

 

增加扩展grep console

image

 运行后,控制台输出如下:

image

 

【2】propagation 事务传播行为:当一个事务B方法被另一个事务方法A调用时,这个事务B方法用哪个该如何进行事务控制

image

主要用前两个。REQUIRED有事务就加入,没有就创建(大部分场景);REQUIRED_NEW(希望两个方法在独立的事务中运行,互不影响)

例如:新增员工时无论成功还是失败都记录操作日志

image 

插入日志不会随着save事务回滚。

3、四大特性(ACID)

(1)原子性(Atomicity):事务是不可分割的最小单元,要么全部成功,要么全部失败。

(2)一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。

(3)隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。

(4)持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。

四、文件上传

1、简介

image

2、本地存储

(1)MultipartFile 常见方法

【1】String getOriginalFilename(); //获取原始文件名

【2】void transferTo(File dest); //将接收的文件转存到磁盘文件中

long getSize(); //获取文件的大小,单位:字节

byte[] getBytes(); //获取文件内容的字节数组

InputStream getInputStream(); //获取接收到的文件内容的输入流 

package com.ithema.controller;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

@RestController
public class UploadController {
    private static final String UPLOAD_DIR = "D:/upload/";

    @PostMapping("/upload")
    public String upload(MultipartFile file) throws IOException {
        String originalFilename = file.getOriginalFilename();
        String newName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
        File targetFile = new File(UPLOAD_DIR + newName);
        // 如果目标目录不存在,则创建它
        if (!targetFile.getParentFile().exists()) {
            targetFile.getParentFile().mkdirs();
        }
        file.transferTo(targetFile);
        return "上传成功";
    }
}

(2)文件大小受限制

【1】在SpringBoot中,文件上传时默认单个文件最大为1M

在application.yml中配置:输入multipart,再选

image

 

image

3、阿里云OSS

阿里云对象存储OSS(Object Storage Service)是一款海量、安全、低成本、高可靠的云存储服。使用OSS可以通过网络随时存储和调用包括文本、图片、音频视频等在内的各种软件。

第三方服务的通用思路:

准备工作--参照官方SDK编写入门程序--集成使用

SDK: Software Development Kit。软件开发工具包,包括辅助软件开发的依赖(jar包)、代码实例等,都可以叫做SDK。

 

(1)准备

imageBucket:存储空间是用户用于存储对象(Object,就是文件)的容器,所有的对象都必须隶属于某个存储空间。

(2)入门

阿里云的OSS的SDK

https://help.aliyun.com/zh/oss/user-guide/oss-sdk-quick-start?spm=a2c4g.11186623.0.0.7d426a752P5s5F#0cf90ff8b28eg

 

(3)集成

package com.ithema.controller;

import com.aliyuncs.exceptions.ClientException;
import com.ithema.pojo.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

@RestController
public class UploadController {
    private static final String UPLOAD_DIR = "D:/upload/";
    private static final Logger log = LoggerFactory.getLogger(UploadController.class);
    private AliyunOSSOperator aliyunOSSOperator;
    // region 本地存储
    /*@PostMapping("/upload")
    public String upload(MultipartFile file) throws IOException {
        String originalFilename = file.getOriginalFilename();
        String newName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
        File targetFile = new File(UPLOAD_DIR + newName);
        // 如果目标目录不存在,则创建它
        if (!targetFile.getParentFile().exists()) {
            targetFile.getParentFile().mkdirs();
        }
        file.transferTo(targetFile);
        return "上传成功";
    }*/
    // endregion

    @PostMapping("/upload")
    public Result upload(MultipartFile file) throws IOException, ClientException {
        log.info("文件上传:{}", file);
        String url = aliyunOSSOperator.uploadFile(file.getBytes(), file.getOriginalFilename());
        return Result.success(url);

    }

}

 

4、功能优化-注入外部配置文件 

(1)@Value("${key}")一个属性一个属性的注入

将灵活变化的参数,配置在配置文件中,通过@Value注解来注入外部配置的属性。

适用于属性较少,不复用

image

(2)@ConfigurationPorperties批量将多个属性注入到bean对象中

适用于:属性较多,考虑复用。

image

package com.ithema.pojo;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliyunOSSProperties {
    private String endpoint;
    private String bucketName;
    private String region;
}

 

package com.ithema.utils;

import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyuncs.exceptions.ClientException;
import com.ithema.pojo.AliyunOSSProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

@Component
public class AliyunOSSOperator {
    //方式一:通过@Value一个属性一个属性的注入
//    @Value("${aliyun.oss.endpoint}")
//    private String endpoint;
//    @Value("${aliyun.oss.bucketName}")
//    private String bucketName;
//    @Value("${aliyun.oss.region}")
//    private String region;
    //方式二:通过ConfigurationProperties

    @Autowired
    private AliyunOSSProperties aliyunOSSProperties;
    public String uploadFile(byte[] content, String originalFilename) throws ClientException {

        String endpoint = aliyunOSSProperties.getEndpoint();
        String bucketName = aliyunOSSProperties.getBucketName();
        String region = aliyunOSSProperties.getRegion();
        // 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
        EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();

        // 填写Object完整路径,例如202406/1.png。Object完整路径中不能包含Bucket名称。
        //获取当前系统日期的字符串,格式为 yyyy/MM
        String dir = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM"));
        //生成一个新的不重复的文件名
        String newFileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
        String objectName = dir + "/" + newFileName;
        // 创建OSSClient实例。
        ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
        clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
        OSS ossClient = OSSClientBuilder.create()
                .endpoint(endpoint)
                .credentialsProvider(credentialsProvider)
                .clientConfiguration(clientBuilderConfiguration)
                .region(region)
                .build();
        try {
            ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(content));
        } finally {
            ossClient.shutdown();
        }
        return endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + objectName;
    }
}

 

 

package com.ithema.controller;

import com.aliyuncs.exceptions.ClientException;
import com.ithema.pojo.Result;
import com.ithema.utils.AliyunOSSOperator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

@Slf4j
@RestController
public class UploadController {
    //private static final Logger log = LoggerFactory.getLogger(UploadController.class);
    @Autowired
    private AliyunOSSOperator aliyunOSSOperator;
    // region 本地存储
    /*@PostMapping("/upload")
    public String upload(MultipartFile file) throws IOException {
        String originalFilename = file.getOriginalFilename();
        String newName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
        File targetFile = new File(UPLOAD_DIR + newName);
        // 如果目标目录不存在,则创建它
        if (!targetFile.getParentFile().exists()) {
            targetFile.getParentFile().mkdirs();
        }
        file.transferTo(targetFile);
        return "上传成功";
    }*/
    // endregion

    @PostMapping("/upload")
    public Result upload(MultipartFile file) throws IOException, ClientException {
        log.info("文件上传:{}", file);
        String url = aliyunOSSOperator.uploadFile(file.getBytes(), file.getOriginalFilename());
        return Result.success(url);

    }

}

 

 五、删除员工 修改与员工。全局异常处理,员工统计

1、删除员工

DELETE    /emp?id=1,2,3

image

 推荐第二种方式,方便后续的操作

 

2、修改

(1)查询回显

Mybatis中封装查询结果resultType和resultMap

如果查询返回的字段名与实体的属性名可以直接对象上,用resultType

如果查询返回的字段名与实体的属性名对应不上,或者实体属性比较复杂,可以通过resultMap手动封装

 <mapper namespace="com.ithema.mapper.EmpMapper">
   <!--自定义结果集-->
    <resultMap id="empResultType" type="com.ithema.pojo.Emp">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="name" property="name"/>
        <result column="gender" property="gender"/>
        <result column="phone" property="phone"/>
        <result column="job" property="job"/>
        <result column="salary" property="salary"/>
        <result  column="image" property="image"/>
        <result column="salary" property="salary"/>
        <result column="image" property="image"/>
        <result column="entry_date" property="entryDate"/>
        <result column="dept_id" property="deptId"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
        <result column="dept_name" property="deptName"/>
        <!--封装exprList-->
        <collection property="exprList" ofType="com.ithema.pojo.EmpExpr">
            <id column="ee_id" property="id"/>
            <result column="emp_id" property="empId"/>
            <result column="begin" property="begin"/>
            <result column="end" property="end"/>
            <result column="company" property="company"/>
            <result column="job" property="job"/>
        </collection>
    </resultMap>
    <select id="getById" resultMap="empResultType">
        select p.*, t.name as dept_name, ee.id as ee_id, ee.emp_id,ee.job, ee.begin, ee.end, ee.company
        from emp p
                 left join emp_expr ee on p.id = ee.emp_id
                 left join dept t on t.id = p.dept_id
        where p.id = #{id}

    </select>
</mapper>

 

package com.ithema.controller;

import com.ithema.pojo.Emp;
import com.ithema.pojo.EmpQueryParam;
import com.ithema.pojo.PageResult;
import com.ithema.pojo.Result;
import com.ithema.service.EmpService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * 员工管理控制器
 */
@RestController
@Slf4j
@RequestMapping("/emps")
public class EmpController {
    @Autowired
    private EmpService empService;    
    
    /**
     * 根据id查询员工
     */
    @GetMapping("/{id}")
    public Result getInfo(@PathVariable Integer id)
    {
        log.info("查询员工:{}", id);
        Emp emp = empService.getInfo(id);
        return Result.success(emp);
    }
}

 

//EmpServiceImpl
 @Override
    public Emp getInfo(Integer id) {
       return  empMapper.getById(id);
    }

(2)修改数据

<set> 标签:自动生成set关键字,自动去除更新字段后多余的逗号

 <update id="updateById">
        UPDATE emp
        <set>
            <!-- 只有当参数不为 null 时,才拼接该字段 -->
            <if test="username != null and username!=''">
                username = #{username},
            </if>
            <if test="password != null and password!=''">
                password = #{password},
            </if>
            <if test="name != null and name!=''">
                name = #{name},
            </if>
            <if test="gender != null">
                gender = #{gender},
            </if>
            <if test="phone != null and phone!=''">
                phone = #{phone},
            </if>
            <if test="job != null">
                job = #{job},
            </if>
            <if test="salary != null">
                salary = #{salary},
            </if>
            <if test="image != null and image!=''">
                image = #{image},
            </if>
            <if test="entryDate != null">
                entry_date = #{entryDate},
            </if>
            <if test="deptId != null">
                dept_id = #{deptId},
            </if>
            <if test="updateTime!=null">
                update_time = #{updateTime},
            </if>
        </set>
        WHERE id = #{id}
    </update>

 

    @PutMapping
    public Result update(@RequestBody Emp emp) {
        log.info("修改员工:{}", emp);
        empService.update(emp);
        return Result.success();
    }

 

    //EmpServiceImpl
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(Emp emp) {
        emp.setUpdateTime(LocalDateTime.now());
        empMapper.updateById(emp);
        empExprMapper.deleteByEmpIds(Arrays.asList(emp.getId()));
        if (!emp.getExprList().isEmpty()) {
            emp.getExprList().forEach(expr -> {
                expr.setEmpId(emp.getId());
            });
            empExprMapper.insertBatch(emp.getExprList());
        }
    }

 

3、全局异常处理

(1)@RestControllerAdvice  表示当前类为全局异常处理器

@RestControllerAdvice = @ControllerAdvice + @ResponseBody 处理异常的方法返回值会转换为json后再响应给前端

(2)@ExceptionHandler  指定可以捕获哪种类型的异常进行处理

package com.ithema.exception;
import com.ithema.pojo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MultipartException;

//全局异常处理类
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    //捕获异常
    @ExceptionHandler
    public Result handleException(Exception e) {
        log.error("出错啦,请联系管理员",e);
        return Result.error("服务器异常");
    }
    @ExceptionHandler//(value = DuplicateKeyException.class)
    public Result handleDuplicateKeyException(DuplicateKeyException   e)
    {
        log.error("出错啦,请联系管理员",e);
        int i=e.getMessage().indexOf("Duplicate entry");
        String mesage=e.getMessage().substring(i);
        String[] arr=mesage.split(" ");
        return Result.error(arr[2]+"已存在");
    }
}

4、员工统计

报表组件官网:https://echarts.apache.org/zh/index.html 

(1)case 流程控制函数

  • 语法一:case

          when cond1 then res1

                                 when cond2 then res2 

         else res end ;

  • 语法二(仅适用于等值匹配):case expr when val1 then res1

                       when val2 then res2 

                       else res end ;

-- 统计每种职位对应的人数
select count(1) num,
       case job
           when 1 then '班主任'
           when 2 then '讲师'
           when 3 then '学工主管'
           when 4 then '教研主管'
           when 5 then '咨询师'
           else '其他'
           end  pose
from emp
group by job
order by num;
-- 方法二
select (case
            when job = 1 then '班主任'
            when job = 2 then '讲师'
            when job = 3 then '学工主管'
            when job = 4 then '教研主管'
            when job = 5 then '咨询师'
            else '其他'
    end) as pos
from emp
group by job

(2)if、ifnull流程控制函数

if(expr,value1,value2) 如果expr表达式成立取value1,否则取value2

ifnull(expr,value) 如果expr不为null,取自身,否则取value

       select
            if(gender = 1, '男性员工', '女性员工')  name, count(*)  value
        from emp
        group by gender

(3)柱状图

返回结果

{
  "code": 1,
  "msg": "success",
  "data": {
    "jobList": ["教研主管","学工主管","其他","班主任","咨询师","讲师"],
    "dataList": [1,1,2,6,8,13]
  }
}

具体代码:

package com.ithema.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class JobOption {
    private List jobList;//职位列表
    private List dataList;//数据列表
}

 

 /**
     * 统计员工职位人数
     */
    @RequestMapping("/empJobData")
    public Result getJobData() {
        log.info("员工职位数据统计");
        JobOption empJobData = reportService.getEmpJobData();
        return Result.success(empJobData);
    }  

 

 @Override
    public JobOption getEmpJobData() {
        List<Map<String, Object>> list = empMapper.countEmpJobData();
        List jobList = list.stream().map(map -> map.get("pos")).toList();
        List dataList = list.stream().map(map -> map.get("num")).toList();
        return new JobOption(jobList, dataList);
    }

 

(4)饼状图

返回结果

{
  "code": 1,
  "msg": "success",
  "data": [
    {"name": "男性员工","value": 5},
    {"name": "女性员工","value": 6}
  ]
}

具体代码

    /**
     * 统计员工性别人数
     */
    @RequestMapping("/empGenderData")
    public Result getGenderData() {
        log.info("员工性别数据统计");
        List<Map<String, Object>> empGenderData = reportService.getEmpGenderData();
        return Result.success(empGenderData);
    }

 

  @Override
    public List<Map<String, Object>> getEmpGenderData() {
        return empMapper.getEmpGenderData();
    }

 

<!--    统计员工职位人数-->
    <select id="countEmpJobData"  resultType="java.util.Map">
        select (case
                    when job = 1 then '班主任'
                    when job = 2 then '讲师'
                    when job = 3 then '学工主管'
                    when job = 4 then '教研主管'
                    when job = 5 then '咨询师'
                    else '其他'
            end) as pos,count(1) as num
        from emp
        group by job order by num
    </select>
<!--    统计员工性别人数-->
    <select id="getEmpGenderData" resultType="java.util.Map">
        select
            if(gender = 1, '男性员工', '女性员工') name,count(*) value
        from emp
        group by gender
    </select>