<resultMap>,异常处理,@Mapkey,case when,if

发布于 9 天前 0 次阅读 1357 字 预计阅读时间: 6 分钟 JAVA


resultType

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

  • 如果查询返回的字段名与实体的属性名可以直接对应上,用resultType 。
  • 如果查询返回的字段名与实体的属性名对应不上,或实体属性比较复杂,可以通过resultMap手动封装 。
package site.suiyue.pojo;

import lombok.Data;

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 exprList;
}
<resultMap id="empResultMap" type="site.suiyue.pojo.Emp">
    <id column="id" property="id"/>
    <result column="username" property="username"/>
    <result column="name" property="name"/>
    <result column="password" property="password"/>
    <result column="phone" property="phone"/>
    <result column="entry_date" property="entryDate"/>
    <result column="gender" property="gender"/>
    <result column="image" property="image"/>
    <result column="job" property="job"/>
    <result column="salary" property="salary"/>
    <result column="dept_id" property="deptId"/>
    <result column="create_time" property="createTime"/>
    <result column="update_time" property="updateTime"/>

    <collection property="exprList" ofType="site.suiyue.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>

<select id="findById" 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_job
    from emp e left join emp_expr ee on e.id = ee.emp_id
    where e.id = #{id}
</select>

异常处理

1.建立exception包

2.编写“自定义业务异常”

继承 RuntimeException,这样在业务代码中抛出时不需要显式地在方法签名上写 throws

package site.suiyue.exception;
public class BusinessException extends RuntimeException {
    private Integer code;

    public BusinessException(Integer code, String message) {
        super(message);
        this.code = code;
    }

    public Integer getCode() {
        return code;
    }
}

3.编写“全局异常处理器”

使用 @RestControllerAdvice 拦截所有 Controller 抛出的异常。
@RestControllerAdvice = @ControllerAdvice + @ResponseBody
通过@ExceptionHandler注解当中的value属性来指定我们要捕获的是哪一类型的异常。
处理异常的方法返回值会转换为json后再响应给前端

package site.suiyue.exception;
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 捕获自定义的业务异常
     * 比如:密码错误、库存不足等
     */
    @ExceptionHandler(BusinessException.class)
    public Result handleBusinessException(BusinessException e) {
        log.warn("业务逻辑异常: {}", e.getMessage());
        return Result.error(e.getCode(), e.getMessage());
    }

    /**
     * 捕获 Spring 校验参数失败的异常 (如 @NotBlank 校验不通过)
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleValidationException(MethodArgumentNotValidException e) {
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        log.warn("参数校验失败: {}", message);
        return Result.error(400, message);
    }

    /**
     * 兜底处理:捕获所有不可预知的运行时异常
     * 比如:空指针、数据库连接超时、数组越界
     */
    @ExceptionHandler(Exception.class)
    public Result handleException(Exception e) {
        // 记录详细堆栈日志,方便排查问题
        log.error("系统运行异常: ", e); 
        // 给前端一个友好的提示,不暴露具体代码错误
        return Result.error(500, "服务器开小差了,请稍后再试");
    }
}

4.在业务代码中使用

@GetMapping("/{id}")
public Result findById(@PathVariable Integer id) {
    Emp emp = empService.getById(id);
    
    if (emp == null) {
        // 直接抛出,ExceptionHandler 会自动抓取并返回给前端 
        //{"code":404, "msg":"员工不存在", "data":null}
        throw new BusinessException(404, "员工不存在");
    }
    
    return Result.success(emp);
}

@Mapkey

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

@MapKey("pos")
List<Map<String,Object>> countEmpJobData();
<!-- 统计各个职位的员工人数 -->
<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(*) total
    from emp group by job
    order by total
</select>
<select id="reportStudentDegreeData" resultType="java.util.Map">
    select count(*) as value,
           case when degree=1 then '初中'
            when degree=2 then '高中'
            when degree=3 then '大学'
            when degree=4 then '硕士'
            when degree=5 then '博士'
            else '其他'
            end as name
    from student group by degree;
</select>

case when:

  • 语法一: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。

if/ifnull

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

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

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