当前位置: 首页 > news >正文

【免费Web系列】JavaWeb实战项目案例六

 

  这是Web第一天的课程大家可以传送过去学习 http://t.csdnimg.cn/K547r  

员工信息-删除&修改

前面我们已经实现了员工信息的条件分页查询以及新增操作。 关于员工管理的功能,还有两个需要实现:

  • 删除员工

  • 修改员工

除了员工管理的功能之外,我们还要再完成员工信息统计的功能开发。

首先我们先完成 "删除员工" 的功能开发,再完成 "修改员工" 的功能开发。再来完成员工信息统计的接口开发 。

综上所述,我们今天的课程内容包含以下四个部分:

  • 删除员工

  • 修改员工

  • 异常处理

  • 员工信息统计

1. 删除员工

3.3.1 需求

当我们勾选列表前面的复选框,然后点击 "批量删除" 按钮,就可以将这一批次的员工信息删除掉了。也可以只勾选一个复选框,仅删除一个员工信息。

问题:我们需要开发两个功能接口吗?一个删除单个员工,一个删除多个员工

答案:不需要。 只需要开发一个功能接口即可(删除多个员工包含只删除一个员工)

3.3.2 接口文档

删除员工

  • 基本信息

    请求路径:/emps

    请求方式:DELETE

    接口描述:该接口用于批量删除员工的数据信息

  • 请求参数

    参数格式:查询参数

    参数说明:

    参数名类型示例是否必须备注
    ids数组 array1,2,3必须员工的id数组

    请求参数样例:

    /emps?ids=1,2,3
  • 响应数据

    参数格式:application/json

    参数说明:

    参数名类型是否必须备注
    codenumber必须响应码,1 代表成功,0 代表失败
    msgstring非必须提示信息
    dataobject非必须返回的数据

    响应数据样例:

    {"code":1,"msg":"success","data":null
    }

3.3.3 思路分析

3.3.4 功能开发

3.3.4.1 Controller接收参数

EmpController 中增加如下方法 delete ,来执行批量删除员工的操作。

方式一:在Controller方法中通过数组来接收

多个参数,默认可以将其封装到一个数组中,需要保证前端传递的参数名 与 方法形参名称保持一致。

/**
* 批量删除员工
*/
@DeleteMapping
public Result delete(Integer[] ids){log.info("批量删除部门: ids={} ", Arrays.asList(ids));return Result.success();
}

方式二:在Controller方法中通过集合来接收

也可以将其封装到一个List<Integer> 集合中,如果要将其封装到一个集合中,需要在集合前面加上 @RequestParam 注解。

/**
* 批量删除员工
*/
@DeleteMapping
public Result delete(@RequestParam List<Integer> ids){log.info("批量删除部门: ids={} ", ids);empService.deleteByIds(ids);return Result.success();
}

两种方式,选择其中一种就可以,我们一般推荐选择集合,因为基于集合操作其中的元素会更加方便。

3.3.4.2 Service

1). 在接口中 EmpService 中定义接口方法 deleteByIds

/**
* 批量删除员工
*/
void deleteByIds(List<Integer> ids);

2). 在实现类 EmpServiceImpl 中实现接口方法 deleteByIds

在删除员工信息时,既需要删除 emp 表中的员工基本信息,还需要删除 emp_expr 表中员工的工作经历信息

@Transactional
@Override
public void deleteByIds(List<Integer> ids) {//1. 根据ID批量删除员工基本信息empMapper.deleteByIds(ids);
​//2. 根据员工的ID批量删除员工的工作经历信息empExprMapper.deleteByEmpIds(ids);
}

由于删除员工信息,既要删除员工基本信息,又要删除工作经历信息,操作多次数据库的删除,所以需要进行事务控制。

3.3.4.3 Mapper

1). 在 EmpMapper 接口中增加 deleteByIds 方法实现批量删除员工基本信息

/**
* 批量删除员工信息
*/
void deleteByIds(List<Integer> ids);

2). 在 EmpMapper.xml 配置文件中, 配置对应的SQL语句

<!--批量删除员工信息-->
<delete id="deleteByIds">delete from emp where id in<foreach collection="ids" item="id" open="(" close=")" separator=",">#{id}</foreach>
</delete>

3). 在 EmpExprMapper 接口中增加 deleteByEmpIds 方法实现根据员工ID批量删除员工的工作经历信息

/**
* 根据员工的ID批量删除工作经历信息
*/
void deleteByEmpIds(List<Integer> empIds);

4). 在 EmpExprMapper.xml 配置文件中, 配置对应的SQL语句

<!--根据员工的ID批量删除工作经历信息-->
<delete id="deleteByEmpIds">delete from emp_expr where emp_id in<foreach collection="empIds" item="empId" open="(" close=")" separator=",">#{empId}</foreach>
</delete>

3.3.5 功能测试

功能开发完成后,重启项目工程,打开 Apifox,发起DELETE请求:

控制台SQL语句:

3.3.6 前后端联调

打开浏览器,测试后端功能接口:

2. 修改员工

需求:修改员工信息

在进行修改员工信息的时候,我们首先先要根据员工的ID查询员工的详细信息用于页面回显展示,然后用户修改员工数据之后,点击保存按钮,就可以将修改的数据提交到服务端,保存到数据库。 具体操作为:

  1. 根据ID查询员工信息

  2. 保存修改的员工信息

2.1 查询回显

2.1.1 接口文档

根据ID查询员工数据

  • 基本信息

    请求路径:/emps/{id}
    ​
    请求方式:GET
    ​
    接口描述:该接口用于根据主键ID查询员工的信息
  • 请求参数

    参数格式:路径参数

    参数说明:

    参数名类型是否必须备注
    idnumber必须员工ID

    请求参数样例:

    /emps/1
  • 响应数据

    参数格式:application/json

    参数说明:

    名称类型是否必须备注
    codenumber必须响应码, 1 成功 , 0 失败
    msgstring非必须提示信息
    dataobject必须返回的数据
    |- idnumber非必须id
    |- usernamestring非必须用户名
    |- namestring非必须姓名
    |- passwordstring非必须密码
    |- entryDatestring非必须入职日期
    |- gendernumber非必须性别 , 1 男 ; 2 女
    |- imagestring非必须图像
    |- jobnumber非必须职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管, 5 咨询师
    |- salarynumber非必须薪资
    |- deptIdnumber非必须部门id
    |- createTimestring非必须创建时间
    |- updateTimestring非必须更新时间
    |- exprListobject[]非必须工作经历列表
    |- idnumber非必须ID
    |- companystring非必须所在公司
    |- jobstring非必须职位
    |- beginstring非必须开始时间
    |- endstring非必须结束时间
    |- empIdnumber非必须员工ID

    响应数据样例:

    {"code": 1,"msg": "success","data": {"id": 2,"username": "zhangwuji","password": "123456","name": "张无忌","gender": 1,"image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2022-09-02-00-27-53B.jpg","job": 2,"salary": 8000,"entryDate": "2015-01-01","deptId": 2,"createTime": "2022-09-01T23:06:30","updateTime": "2022-09-02T00:29:04","exprList": [{"id": 1,"begin": "2012-07-01","end": "2019-03-03""company": "百度科技股份有限公司""job": "java开发","empId": 2},{"id": 2,"begin": "2019-3-15","end": "2023-03-01""company": "阿里巴巴科技股份有限公司""job": "架构师","empId": 2}]}
    }

2.1.2 实现思路

在查询回显时,既需要查询出员工的基本信息,又需要查询出该员工的工作经历信息。

我们可以先通过一条SQL语句,查询出指定员工的基本信息,及其员工的工作经历信息。SQL如下:

select e.*,ee.id ee_id,ee.begin ee_begin,ee.end ee_end,ee.company ee_company,ee.job ee_job
from emp e left join emp_expr ee on e.id = ee.emp_id where e.id = 39;

具体的实现思路如下:

2.1.3 代码实现

1). EmpController 添加 getInfo 用来根据ID查询员工数据,用于页面回显

/*** 查询回显*/
@GetMapping("/{id}")
public Result getInfo(@PathVariable Integer id){log.info("根据id查询员工的详细信息");Emp emp  = empService.getInfo(id);return Result.success(emp);
}

2). EmpService 接口中增加 getInfo 方法

/*** 根据ID查询员工的详细信息*/
Emp getInfo(Integer id);

3). EmpServiceImpl 实现类中实现 getInfo 方法

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

4). EmpMapper 接口中增加 getById 方法

/*** 根据ID查询员工详细信息*/
Emp getById(Integer id);

5). EmpMapper.xml 配置文件中定义对应的SQL

<!--自定义结果集ResultMap-->
<resultMap id="empResultMap" type="com.itheima.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="entry_date" property="entryDate" /><result column="dept_id" property="deptId" /><result column="create_time" property="createTime" /><result column="update_time" property="updateTime" />
​<!--封装exprList--><collection property="exprList" ofType="com.itheima.pojo.EmpExpr"><id column="ee_id" property="id"/><result column="ee_company" property="company"/><result column="ee_job" property="job"/><result column="ee_begin" property="begin"/><result column="ee_end" property="end"/><result column="ee_empid" property="empId"/></collection>
</resultMap>
​
<!--根据ID查询员工的详细信息-->
<select id="getById" resultMap="empResultMap">select e.*,ee.id ee_id,ee.emp_id ee_empid,ee.begin ee_begin,ee.end ee_end,ee.company ee_company,ee.job ee_jobfrom emp e left join emp_expr ee on e.id = ee.emp_idwhere e.id = #{id}
</select>

在这种一对多的查询中,我们要想成功的封装的结果,需要手动的基于 <resultMap> 来进行封装结果。

  • Mybatis中封装查询结果,什么时候用 resultType,什么时候用resultMap ?

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

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

2.1.4 Apifox测试

重新启动服务,基于Apifox进行接口测试。

2.1.5 前后端联调测试

打开浏览器,进行前后端联调测试。

2.2 修改员工

查询回显之后,就可以在页面上修改员工的信息了。

当用户修改完数据之后,点击保存按钮,就需要将数据提交到服务端,然后服务端需要将修改后的数据更新到数据库中 。

而此次更新的时候,既需要更新员工的基本信息; 又需要更新员工的工作经历信息 。

2.2.1 接口文档
  • 基本信息

    请求路径:/emps
    ​
    请求方式:PUT
    ​
    接口描述:该接口用于修改员工的数据信息
  • 请求参数

    参数格式:application/json

    参数说明:

    名称类型是否必须备注
    idnumber必须id
    usernamestring必须用户名
    namestring必须姓名
    gendernumber必须性别, 说明: 1 男, 2 女
    imagestring非必须图像
    deptIdnumber非必须部门id
    entryDatestring非必须入职日期
    jobnumber非必须职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管, 5 咨询师
    salarynumber非必须薪资
    exprListobject[]非必须工作经历列表
    |- idnumber非必须ID
    |- companystring非必须所在公司
    |- jobstring非必须职位
    |- beginstring非必须开始时间
    |- endstring非必须结束时间
    |- empIdnumber非必须员工ID

    请求数据样例:

    {"id": 2,"username": "zhangwuji","name": "张无忌","gender": 1,"image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2022-09-02-00-27-53B.jpg","job": 2,"salary": 8000,"entryDate": "2015-01-01","deptId": 2,"exprList": [{"id": 1,"begin": "2012-07-01","end": "2015-06-20""company": "中软国际股份有限公司""job": "java开发","empId": 2},{"id": 2,"begin": "2015-07-01","end": "2019-03-03""company": "百度科技股份有限公司""job": "java开发","empId": 2}]}

  • 响应数据

    参数格式:application/json

    参数说明:

    参数名类型是否必须备注
    codenumber必须响应码,1 代表成功,0 代表失败
    msgstring非必须提示信息
    dataobject非必须返回的数据

    响应数据样例:

    {"code":1,"msg":"success","data":null
    }

2.2.2 实现思路

2.2.3 代码实现

1). EmpController 增加 update 方法接收请求参数,响应数据

/**
* 更新员工信息
*/
@PutMapping
public Result update(@RequestBody Emp emp){log.info("修改员工信息, {}", emp);empService.update(emp);return Result.success();
}

2). EmpService 接口增加 update 方法

/**
* 更新员工信息
* @param emp
*/
void update(Emp emp);

3). EmpServiceImpl 实现类实现 update 方法

@Transactional
@Override
public void update(Emp emp) {//1. 根据ID更新员工基本信息emp.setUpdateTime(LocalDateTime.now());empMapper.updateById(emp);
​//2. 根据员工ID删除员工的工作经历信息 【删除老的】empExprMapper.deleteByEmpIds(Arrays.asList(emp.getId()));
​//3. 新增员工的工作经历数据 【新增新的】Integer empId = emp.getId();List<EmpExpr> exprList = emp.getExprList();if(!CollectionUtils.isEmpty(exprList)){exprList.forEach(empExpr -> empExpr.setEmpId(empId));empExprMapper.insertBatch(exprList);}
}

4). EmpMapper 接口中增加 updateById 方法

/**
* 更新员工基本信息
*/
void updateById(Emp emp);

5). EmpMapper.xml 配置文件中定义对应的SQL语句,基于动态SQL更新员工信息

<!--根据ID更新员工信息-->
<update id="updateById">update emp<set><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>

2.2.4 Apifox测试

重新启动服务,打开 Apifox 进行接口测试。

2.2.5 前后端联调测试

点击保存之后,查看更新后的数据。

3. 异常处理

3.1 当前问题

当我们在修改部门数据的时候,如果输入一个在数据库表中已经存在的手机号,点击保存按钮之后,前端提示了错误信息,但是返回的结果并不是统一的响应结果,而是框架默认返回的错误结果 。

状态码为500,表示服务器端异常,我们打开idea,来看一下,服务器端出了什么问题。

上述错误信息的含义是,emp员工表的phone手机号字段的值重复了,因为在数据库表emp中已经有了13309090001这个手机号了,我们之前设计这张表时,为phone字段建议了唯一约束,所以该字段的值是不能重复的。

而当我们再将该员工的手机号也设置为 13309090001,就违反了唯一约束,此时就会报错。

我们来看一下出现异常之后,最终服务端给前端响应回来的数据长什么样。

响应回来的数据是一个JSON格式的数据。但这种JSON格式的数据还是我们开发规范当中所提到的统一响应结果Result吗?显然并不是。由于返回的数据不符合开发规范,所以前端并不能解析出响应的JSON数据 。

接下来我们需要思考的是出现异常之后,当前案例项目的异常是怎么处理的?

  • 答案:没有做任何的异常处理

当我们没有做任何的异常处理时,我们三层架构处理异常的方案:

  • Mapper接口在操作数据库的时候出错了,此时异常会往上抛(谁调用Mapper就抛给谁),会抛给service。

  • service 中也存在异常了,会抛给controller。

  • 而在controller当中,我们也没有做任何的异常处理,所以最终异常会再往上抛。最终抛给框架之后,框架就会返回一个JSON格式的数据,里面封装的就是错误的信息,但是框架返回的JSON格式的数据并不符合我们的开发规范。

3.2 解决方案

那么在三层构架项目中,出现了异常,该如何处理?

  • 方案一:在所有Controller的所有方法中进行try…catch处理

    • 缺点:代码臃肿(不推荐)

  • 方案二:全局异常处理器

    • 好处:简单、优雅(推荐)

3.3 全局异常处理器

我们该怎么样定义全局异常处理器?

  • 定义全局异常处理器非常简单,就是定义一个类,在类上加上一个注解@RestControllerAdvice,加上这个注解就代表我们定义了一个全局异常处理器。

  • 在全局异常处理器当中,需要定义一个方法来捕获异常,在这个方法上需要加上注解@ExceptionHandler。通过@ExceptionHandler注解当中的value属性来指定我们要捕获的是哪一类型的异常。

@RestControllerAdvice
public class GlobalExceptionHandler {
​//处理异常@ExceptionHandlerpublic Result ex(Exception e){//方法形参中指定能够处理的异常类型e.printStackTrace();//打印堆栈中的异常信息//捕获到异常之后,响应一个标准的Resultreturn Result.error("对不起,操作失败,请联系管理员");}
}

@RestControllerAdvice = @ControllerAdvice + @ResponseBody

处理异常的方法返回值会转换为json后再响应给前端

重新启动SpringBoot服务,打开浏览器,再来测试一下 修改员工 这个操作,我们依然设置已存在的 "13309090001" 这个手机号:

此时,我们可以看到,出现异常之后,异常已经被全局异常处理器捕获了。然后返回的错误信息,被前端程序正常解析,然后提示出了对应的错误提示信息。

以上就是全局异常处理器的使用,主要涉及到两个注解:

  • @RestControllerAdvice //表示当前类为全局异常处理器

  • @ExceptionHandler //指定可以捕获哪种类型的异常进行处理

3.4 如何抛出自定义异常信息

 package com.itheima.exception;
​
public class BusinessException extends RuntimeException{public BusinessException() {super();}public BusinessException(String message) {super(message);}
}
package com.itheima.exception;
​
import com.itheima.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;
​
/*** 全局异常处理器*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
​@ExceptionHandlerpublic Result handleException(Exception e){log.error("程序运行出错啦 .... ", e);return Result.error("对不起, 系统访问异常, 请联系管理员 ~");}
​@ExceptionHandlerpublic Result handleBussinessException(BusinessException e){log.error("程序运行出错啦 .... ", e);return Result.error(e.getMessage());}
​
}

4. 员工信息统计

员工管理的增删改查功能我们已开发完成,接下来,我们再来完成员工信息统计的接口开发。 对于这些图形报表的开发,其实呢,都是基于现成的一些图形报表的组件开发的,比如:Echarts、HighCharts等。

而报表的制作,主要是前端人员开发,引入对应的组件(比如:ECharts)即可。 服务端开发人员仅为其提供数据即可。

官网:Apache ECharts

4.1 职位统计

4.1.1 需求

对于这类的图形报表,服务端要做的,就是为其提供数据即可。 我们可以通过官方的示例,看到提供的数据其实就是X轴展示的信息,和对应的数据。

4.1.2 接口文档

1). 基本信息

请求路径:/report/empJobData

请求方式:GET

接口描述:统计各个职位的员工人数

2). 请求参数

3). 响应数据

参数格式:application/json

参数说明:

参数名类型是否必须备注
codenumber必须响应码,1 代表成功,0 代表失败
msgstring非必须提示信息
dataobject非必须返回的数据
|- jobListstring[]必须职位列表
|- dataListnumber[]必须人数列表

响应数据样例:

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

为了封装上面需要给前端返回的数据,在pojo包下再创建一个实体类 JobOption,封装给前端返回的结果:

/*** 员工职位人数统计*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class JobOption {private List jobList; //职位列表private List dataList; //人数列表
}

4.1.3 代码实现

1). 定义ReportController,并添加方法。

@Slf4j
@RequestMapping("/report")
@RestController
public class ReportController {
​@Autowiredprivate ReportService reportService;
​/*** 统计各个职位的员工人数*/@GetMapping("/empJobData")public Result getEmpJobData(){log.info("统计各个职位的员工人数");JobOption jobOption = reportService.getEmpJobData();return Result.success(jobOption);}
}

2). 定义ReportService接口,并添加接口方法。

public interface ReportService {/*** 统计各个职位的员工人数* @return*/JobOption getEmpJobData();
}

3). 定义ReportServiceImpl实现类,并实现方法

@Service
public class ReportServiceImpl implements ReportService {
​@Autowiredprivate EmpMapper empMapper;@Overridepublic JobOption getEmpJobData() {List<Map<String,Object>> list = empMapper.countEmpJobData();List<Object> jobList = list.stream().map(dataMap -> dataMap.get("pos")).toList();List<Object> dataList = list.stream().map(dataMap -> dataMap.get("total")).toList();return new JobOption(jobList, dataList);}
}

4). 定义EmpMapper 接口

统计的是员工的信息,所以需要操作的是员工表。 所以代码我们就写在 EmpMapper 接口中即可。

/*** 统计各个职位的员工人数*/
@MapKey("pos")
List<Map<String,Object>> countEmpJobData();

如果查询的记录往Map中封装,可以通过@MapKey注解指定返回的map中的唯一标识是那个字段。【也可以不指定】

5). 定义EmpMapper.xml

<!-- 统计各个职位的员工人数 -->
<select id="countEmpJobData" resultType="java.util.Map">select(case job when 1 then '班主任' when 2 then '讲师' when 3 then '学工主管' when 4 then '教研主管' when 5 then '咨询师' else '其他' end)  pos,count(*) totalfrom emp group by joborder by total
</select>

case流程控制函数:

  • 语法一:case when cond1 then res1 [ when cond2 then res2 ] else res end ;

    • 含义:如果 cond1 成立, 取 res1。 如果 cond2 成立,取 res2。 如果前面的条件都不成立,则取 res。

  • 语法二(仅适用于等值匹配):case expr when val1 then res1 [ when val2 then res2 ] else res end ;

    • 含义:如果 expr 的值为 val1 , 取 res1。 如果 expr 的值为 val2 ,取 res2。 如果前面的条件都不成立,则取 res。

4.1.4 Apifox测试

重新启动服务,打开Apifox进行测试。

4.1.5 联调测试

4.2 性别统计

4.2.1 需求

对于这类的图形报表,服务端要做的,就是为其提供数据即可。 我们可以通过官方的示例,看到提供的数据就是一个json格式的数据。

4.2.2 接口文档

1). 基本信息

请求路径:/report/empGenderData

请求方式:GET

接口描述:统计员工性别信息

2). 请求参数

3). 响应数据

参数格式:application/json

参数说明:

参数名类型是否必须备注
codenumber必须响应码,1 代表成功,0 代表失败
msgstring非必须提示信息
dataobject[]非必须返回的数据
|- namestring非必须性别
|- valuenumber非必须人数

响应数据样例:

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

4.2.3 代码实现

1). 在ReportController,添加方法。

/*** 统计员工性别信息*/
@GetMapping("/empGenderData")
public Result getEmpGenderData(){log.info("统计员工性别信息");List<Map> genderList = reportService.getEmpGenderData();return Result.success(genderList);
}

2). 在ReportService接口,添加接口方法。

/*** 统计员工性别信息*/
List<Map> getEmpGenderData();

3). 在ReportServiceImpl实现类,实现方法

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

4). 定义EmpMapper 接口

统计的是员工的信息,所以需要操作的是员工表。 所以代码我们就写在 EmpMapper 接口中即可。

/*** 统计员工性别信息*/
@MapKey("name")
List<Map> countEmpGenderData();

5). 定义EmpMapper.xml

<!-- 统计员工的性别信息 -->
<select id="countEmpGenderData" resultType="java.util.Map">selectif(gender = 1, '男', '女') as name,count(*) as valuefrom emp group by gender ;
</select>

if函数语法:if(条件, 条件为true取值, 条件为false取值)

ifnull函数语法:ifnull(expr, val1) 如果expr不为null,取自身,否则取val1

4.2.4 Apifox测试

4.2.5 联调测试

相关文章:

  • 队列——一种操作受限的线性表
  • 【python学习】Anaconda的介绍、下载及conda和pip换源方式(切换到国内镜像源)
  • docker使用docker logs命令查看容器日志的几种方式
  • 出现 Transaction rolled back because it has been marked as rollback-only 解决方法
  • 联邦学习【01】杨强第三章横向联邦学习复现
  • Lombok一文通
  • 杂牌记录仪TS视频流恢复方法
  • PHPstudy情况下上传图片马需要的.htaccess文件
  • MYSQL视图
  • MySQL嵌套,别名,分组查询
  • 安全基础二
  • L9110S电机控制模块
  • 书生·浦语大模型全链路开源体系-笔记作业2
  • 设计模式——结构型模式——责任链模式
  • vue 引用第三方库 Swpier轮播图
  • 【407天】跃迁之路——程序员高效学习方法论探索系列(实验阶段164-2018.03.19)...
  • C++类中的特殊成员函数
  •  D - 粉碎叛乱F - 其他起义
  • express + mock 让前后台并行开发
  • Git同步原始仓库到Fork仓库中
  • hadoop入门学习教程--DKHadoop完整安装步骤
  • JavaSE小实践1:Java爬取斗图网站的所有表情包
  • JS字符串转数字方法总结
  • Redis 中的布隆过滤器
  • text-decoration与color属性
  • 给自己的博客网站加上酷炫的初音未来音乐游戏?
  • 关键词挖掘技术哪家强(一)基于node.js技术开发一个关键字查询工具
  • 那些被忽略的 JavaScript 数组方法细节
  • 嵌入式文件系统
  • 让你成为前端,后端或全栈开发程序员的进阶指南,一门学到老的技术
  • 微信开放平台全网发布【失败】的几点排查方法
  • 我的业余项目总结
  • C# - 为值类型重定义相等性
  • 关于Android全面屏虚拟导航栏的适配总结
  • ​力扣解法汇总1802. 有界数组中指定下标处的最大值
  • (C#)一个最简单的链表类
  • (JSP)EL——优化登录界面,获取对象,获取数据
  • (十一)c52学习之旅-动态数码管
  • (一)Java算法:二分查找
  • (原創) 是否该学PetShop将Model和BLL分开? (.NET) (N-Tier) (PetShop) (OO)
  • (转)JAVA中的堆栈
  • (转)ORM
  • (转)用.Net的File控件上传文件的解决方案
  • .NET 服务 ServiceController
  • .NET/C# 获取一个正在运行的进程的命令行参数
  • .NET:自动将请求参数绑定到ASPX、ASHX和MVC(菜鸟必看)
  • /*在DataTable中更新、删除数据*/
  • @Data注解的作用
  • @RequestMapping用法详解
  • [ C++ ] 继承
  • [ActionScript][AS3]小小笔记
  • [Android Pro] listView和GridView的item设置的高度和宽度不起作用
  • [AutoSar]BSW_OS 01 priority ceiling protocol(PCP)
  • [BJDCTF2020]The mystery of ip1
  • [C# WPF] 如何给控件添加边框(Border)?