JAVA学习-实战3 多表查询下的员工管理

待って こんなすぐに见つけないで

员工管理

查询所有员工信息,并且查询其部门信息。涉及到的表:emp(员工表),dept(部门表)

准备工作如下:

准备员工表emp和员工工作经历表emp_expr
准备封装实体类Emp和EmpExpr
准备三层架构的代码结构:EmpController,EmpService/EmpserviceImpl,EmpMapper

相应的实体类如下:

//Emp 员工数据类
public class EmpData {
    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; //部门名称
}
//EmpExpr 员工工作经历类
public class EmpExprData {
    private Integer id; //ID
    private Integer empId; //员工ID
    private LocalDate begin; //开始时间
    private LocalDate end; //结束时间
    private String company; //公司名称
    private String job; //职位
}

分页查询

在数据较多的时候需要将数据分页然后一页一页地浏览

原始方法实现分页查询

使用MYSQL语句的limit完成这个功能

-- 查询第1页的5条数据
select emp.*,dept.name from emp left join dept on emp.dept_id = dept.id limit 0,5;
-- 查询第2页的5条数据
select emp.*,dept.name from emp left join dept on emp.dept_id = dept.id limit 5,5;

-- 可以看出,起始索引=(页码-1)*5

前端传递给后端的是两个参数:页码Page每页展示数据量PageSize

后端传递给前端的是两个参数:当前页的数据列表List总记录量total

这里我封装了存储分页查询结果的实体类:

public class PageResultData<T> {
    private Long total; //数据总量
    private List<T> rows; //指定页返回的数据
}

使用原始分页的查询后,每一层的代码如下所示:

//EmpController 控制层代码
@RestController
public class EmpController {

    @Autowired
    private EmpService empService;

    @GetMapping("/emplist")
    public ResultData getPage(@RequestParam(defaultValue = "1") Integer page, //@RequestParam表示默认值
                              @RequestParam(defaultValue = "5") Integer pageSize) {
        System.out.printf("Now is page=%d , pageSize = %d\n",page,pageSize);
        PageResultData<EmpData> pageResultData = empService.selectByPage(page,pageSize);
        return ResultData.success(pageResultData);
    }
}
//EmpService 服务层接口就不写了,只写实现类
@Service
public class EmpServiceImpl implements EmpService {

    @Autowired
    private EmpMapper empMapper;

    @Override
    public PageResultData<EmpData> selectByPage(Integer page,Integer pageSize) {

        Long total=empMapper.countAll();

        int startIndex = (page-1)*pageSize; //通过页码以及页面数据量获得起始索引
        int needNumber = pageSize;
        List<EmpData> resultList= empMapper.selectByPage(startIndex,needNumber);

        return new PageResultData<>(total,resultList);
    }
}
//EmpMapper 持久层接口
@Mapper
public interface EmpMapper {
    public Long countAll();
    public List<EmpData>  selectByPage(@Param("startIndex") Integer startIndex,@Param("needNumber") Integer needNumber);
}
//xml配置映射文件
<mapper namespace="org.example.mapper.EmpMapper">
    <select id="countAll" resultType="long"> //查询总量的SQL语句
        select count(*) from emp left join dept on emp.dept_id=dept.id;
    </select>
    <select id="selectByPage" resultType="org.example.pojo.EmpData"> //使用左连接获得用户基本数据以及对应的部门名称
        select emp.id,
        emp.username,
        emp.password,
        emp.name,
        emp.gender,
        emp.phone,
        emp.job,
        emp.salary,
        emp.image,
        emp.entry_date entryDate,
        emp.dept_id deptId,
        emp.create_time createTime,
        emp.update_time updateTIme,
        dept.name deptName
        from emp left join dept on emp.dept_id=dept.id order by emp.id limit #{startIndex},#{needNumber};
    </select>
</mapper>

控制层代码处使用了@RequestParam(default="")注解设置默认值,如果实际参数传递为null的话,那么控制层直接传递默认值

PageHelper实现分页查询

PageHelper是MyBatis提供的第三方实现分页查询的工具,简化分页操作提升开发效率

使用方法如下,作用于逻辑处理层:

@Service
public class EmpServiceImpl implements EmpService {

    @Autowired
    private EmpMapper empMapper;

    @Override
    public PageResultData<EmpData> selectByPageHelper(Integer page,Integer pageSize) {

        PageHelper.startPage(page,pageSize); //1.设置分页参数
        List<EmpData> resultList= empMapper.selectAll(); //2.执行查询,无需传递参数

        Page<EmpData> pageList = (Page<EmpData>) resultList; //3.解析查询结果,并封装
        return new PageResultData<>(pageList.getTotal(), pageList.getResult());//这里调用Page的方法就可以直接获取
    }
}

这样写的话就无需向下传递分页参数了。

其中,Page<T>是分页机制插件中的一个核心类,其会自动被创建并填充数据。它继承自 ArrayList,因此可以像使用普通列表一样操作其中的数据。同时,它还包含了许多用于分页展示的属性,如总记录数、总页数、当前页码等。

并且,当创建 PageResultData 对象时,基于Java的泛型类型推断机制,编译器会根据构造函数的参数类型自动推断泛型参数的类型,因此在返回的new后无需再指定具体的数据类名。

Mapper持久层以及XML配置文件的定义如下:

//Mapper持久层
@Mapper
public interface EmpMapper {

    public Long countAll();
}

//XML配置文件
<select id="selectAll" resultType="org.example.pojo.EmpData">
    select emp.id,
    emp.username,
    emp.password,
    emp.name,
    emp.gender,
    emp.phone,
    emp.job,
    emp.salary,
    emp.image,
    emp.entry_date entryDate,
    emp.dept_id deptId,
    emp.create_time createTime,
    emp.update_time updateTIme,
    dept.name deptName
    from emp left join dept on emp.dept_id=dept.id order by emp.id
</select>

可以看到这里均无需传递和调用分页参数


这里介绍一下PageHelper的机制 因为我使用的时候踩雷了,正好学习一下

先说雷吧,当时XML的配置文件是这么写的

<select id="selectAll" resultType="org.example.pojo.EmpData">
    select emp.id,
    emp.username,
    emp.password,
    emp.name,
    emp.gender,
    emp.phone,
    emp.job,
    emp.salary,
    emp.image,
    emp.entry_date entryDate,
    emp.dept_id deptId,
    emp.create_time createTime,
    emp.update_time updateTIme,
    dept.name deptName
    from emp left join dept on emp.dept_id=dept.id order by emp.id;
</select>

其实这是一个坏习惯,因为XML配置文件中的SQL语句是不需要我们写;的,自己添加会有问题

结果报错了,控制台输出错误信息有这么两行关系信息

Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LIMIT 10, 5' at line 16 
### SQL: select emp.id,         emp.username,         emp.password,         emp.name,         emp.gender,         emp.phone,         emp.job,         emp.salary,         emp.image,         emp.entry_date entryDate,         emp.dept_id deptId,         emp.create_time createTime,         emp.update_time updateTIme,         dept.name deptName         from emp left join dept on emp.dept_id=dept.id order by emp.id;  LIMIT ?, ?

可以看到,SQL系统接受的SQL语句结果居然是...order by emp.id; LIMIT ?, ?,所以处理报错了

这和PageHelper的工作机制有关系

PageHelper的工作机制是基于‌拦截器技术‌。当调用 PageHelper.startPage(page, pageSize) 时,PageHelper会将分页参数存储到当前线程的ThreadLocal中。随后,当执行紧跟其后的MyBatis查询方法时,PageHelper的拦截器会拦截这个查询请求。

PageHelper的拦截器会在SQL执行前检查当前线程是否有分页参数,如果有,则会动态修改原始SQL语句,在其末尾添加分页子句(如MySQL的LIMIT子句)。这个过程是‌针对紧随其后的第一个查询方法‌。

也就是说存在如下关键点:

单次分页生效‌:PageHelper 的分页作用仅对调用 startPage() 后‌紧跟的那一个 MyBatis 查询方法‌生效。如果在 startPage() 和 selectAll() 之间有其他代码,或者有多个查询方法,只有第一个查询会应用分页。
‌>SQL重写机制‌:PageHelper 通过拦截器动态地在 SQL 语句中添加分页条件,而不是修改原始 SQL 语句。
‌>线程绑定‌:PageHelper 使用 ThreadLocal 存储分页参数,确保分页参数与当前线程绑定。在查询执行完毕后,PageHelper 会在 finally 代码段中自动清除 ThreadLocal 中的存储对象。

那么如果是如下情况:

@Override
public PageResultData<EmpData> selectByPageHelper(Integer page,Integer pageSize) {
    PageHelper.startPage(page,pageSize);
    List<EmpData> resultList= empMapper.selectAll();
    List<EmpData> resultList2= empMapper.selectAll();
}

只有resultList 会添加分页相关子句,因为它是紧跟在 PageHelper.startPage(page, pageSize) 调用之后的查询方法。而resultList2如果不是紧跟在 startPage()之后执行的查询方法,则不会应用分页逻辑。


条件分页查询

分页的基础上,完成如下要求:

1.输入员工姓名,进行模糊查询
2.输入员工性别,进行精确查询
3.输入开始时间和结束时间,进行时间范围内的查询

代码设计如下:

//控制层方法
@GetMapping("/emplistwithrequest")
public ResultData selectByRequest(
        String name, Integer gender,
        @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate,
        @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate,
        @RequestParam(defaultValue = "1") Integer page,
        @RequestParam(defaultValue = "5") Integer pageSize) {
    System.out.println("Now Find name is "+name+",gender is "+gender+",time between "+startDate+" and "+endDate);
    System.out.printf("Now is page=%d , pageSize = %d\n",page,pageSize);
    PageResultData<EmpData> resultData = empService.selectByRequest(name,gender,startDate,endDate,page,pageSize);
    return ResultData.success(resultData);
}
//逻辑处理层实现类方法
@Override
public PageResultData<EmpData> selectByRequest(String name, Integer gender, LocalDate startDate, LocalDate endDate, Integer page, Integer pageSize){
    PageHelper.startPage(page,pageSize);
    List<EmpData> resultList= empMapper.selectByRequest(name,gender,startDate,endDate);

    Page<EmpData> pageList = (Page<EmpData>) resultList;
    return new PageResultData<>(pageList.getTotal(), pageList.getResult());
}
//持久层方法
@Mapper
public interface EmpMapper {
    public List<EmpData> selectByRequest(@Param("name") String name, @Param("gender") Integer gender, @Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate);
}
//XML映射文件
<select id="selectByRequest" resultType="org.example.pojo.EmpData">
    select emp.id,
    emp.username,
    emp.password,
    emp.name,
    emp.gender,
    emp.phone,
    emp.job,
    emp.salary,
    emp.image,
    emp.entry_date entryDate,
    emp.dept_id deptId,
    emp.create_time createTime,
    emp.update_time updateTIme,
    dept.name deptName
    from emp left join dept on emp.dept_id=dept.id
    where emp.name like '%#{name}%' and emp.gender = #{gender} and emp.entry_date between #{startDate} and #{endDate}
    order by emp.id
</select>

这里传入日期时使用了@DateTimeFormat(pattern=)注解用于指定传入日期的格式

运行,然后执行,出现了报错数据:

 Error querying database.  Cause: org.apache.ibatis.type.TypeException: Could not set parameters for mapping: ParameterMapping{property='endDate', mode=IN, javaType=class java.lang.Object, jdbcType=null, numericScale=null, resultMapId='null', jdbcTypeName='null', expression='null'}. Cause: org.apache.ibatis.type.TypeException: Error setting non null for parameter #4 with JdbcType null . Try setting a different JdbcType for this parameter or a different configuration property. Cause: org.apache.ibatis.type.TypeException: Error setting non null for parameter #4 with JdbcType null . Try setting a different JdbcType for this parameter or a different configuration property. Cause: java.sql.SQLException: Parameter index out of range (4 > number of parameters, which is 3).

可以看到,这里说的我们传入了4个参数,但是只能接受3个参数。因为XML配置文件中'%#{name}%'在经过SQL系统编译后,转换成了了?占位符,也就是'%?%',但是这样的话就变成了一个常量字符串的模糊查询而不是一个带占位符的模糊查询

所以我们需要使用SQL的字符串拼接函数concat来解决这个问题

<select id="selectByRequest" resultType="org.example.pojo.EmpData">
    select emp.id,emp.username,emp.password,emp.name,emp.gender,emp.phone,emp.job,emp.salary,emp.image,emp.entry_date entryDate,
    emp.dept_id deptId,emp.create_time createTime,emp.update_time updateTIme,dept.name deptName
    from emp left join dept on emp.dept_id=dept.id
    where emp.name like concat('%',#{name},'%') and emp.gender = #{gender} and emp.entry_date between #{startDate} and #{endDate}
    order by emp.id
</select>

修改之后可以正常运行

条件分页查询优化

传递参数实体封装

在控制层中传递参数过于会显得程序过于臃肿,为了优化结构选择封装传递参数为实体类

//封装实体类
public class EmpQueryParam {
    private Integer page = 1;
    private Integer pageSize = 5;
    private String name;
    private Integer gender;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate startDate;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate endDate;
}
//控制层方法
@Override
public PageResultData<EmpData> selectByRequest(EmpQueryParam empQueryParam){
    PageHelper.startPage(empQueryParam.getPage(), empQueryParam.getPageSize());
    List<EmpData> resultList= empMapper.selectByRequest(empQueryParam);

    Page<EmpData> pageList = (Page<EmpData>) resultList;
    return new PageResultData<>(pageList.getTotal(), pageList.getResult());
}
//持久层方法 务必保证实体类属性名和XML映射配置文件的参数名一致
@Mapper
public interface EmpMapper {

    public List<EmpData> selectByRequest(EmpQueryParam empQueryParam);
}

选择传递参数

这是我们的XML文件中用于查询的SQL语句

<select id="selectByRequest" resultType="org.example.pojo.EmpData">
    select emp.id,emp.username,emp.password,emp.name,emp.gender,emp.phone,emp.job,emp.salary,emp.image,emp.entry_date entryDate,
    emp.dept_id deptId,emp.create_time createTime,emp.update_time updateTIme,dept.name deptName
    from emp left join dept on emp.dept_id=dept.id
    where emp.name like concat('%',#{name},'%') and emp.gender = #{gender} and emp.entry_date between #{startDate} and #{endDate}
    order by emp.id
</select>

考虑一下现实条件,如果我们传递参数只传递了gender,那么不代表我们想让其他的参数为null直接带入SQL语句,而是不希望查询中出现这些参数的限制

也就是这样

<select id="selectByRequest" resultType="org.example.pojo.EmpData">
    select emp.id,emp.username,emp.password,emp.name,emp.gender,emp.phone,emp.job,emp.salary,emp.image,emp.entry_date entryDate,
    emp.dept_id deptId,emp.create_time createTime,emp.update_time updateTIme,dept.name deptName
    from emp left join dept on emp.dept_id=dept.id
    <!-- 限制条件只有gender一个参数传递进来-->
    where emp.gender = #{gender}  
    order by emp.id
</select>

这时候就需要使用动态SQL

动态SQL—:随着用户的输入或者外部条件的变化而变化的SQL语句

动态SQL使用<if>进行条件判断,如果为true则拼接SQL

使用''修饰条件判断,可以自动生成关键字where,并且去除多余的or和and

<select id="selectByRequest" resultType="org.example.pojo.EmpData">
    select emp.id,emp.username,emp.password,emp.name,emp.gender,emp.phone,emp.job,emp.salary,emp.image,emp.entry_date entryDate,
    emp.dept_id deptId,emp.create_time createTime,emp.update_time updateTIme,dept.name deptName
    from emp left join dept on emp.dept_id=dept.id
    <where>
        <if test="name!=null and name!=''">
            emp.name like concat('%',#{name},'%')
        </if>
        <if test="gender!=null">
            and emp.gender = #{gender}
        </if>
        <if test="startDate!=null and endDate!=null">
            and emp.entry_date between #{startDate} and #{endDate}
        </if>
    </where>ss
            order by emp.id
</select>

接下来执行查询,变更查询条件,如果我们传递的日期上下限都是null,那么可以看到控制台输出信息如下:

JDBC Connection [HikariProxyConnection@740245810 wrapping com.mysql.cj.jdbc.ConnectionImpl@32df3016] will not be managed by Spring
==>  Preparing: SELECT count(0) FROM emp LEFT JOIN dept ON emp.dept_id = dept.id WHERE emp.name LIKE concat('%', ?, '%') AND emp.gender = ?
==> Parameters: 阮(String), 1(Integer)
<==    Columns: count(0)
<==        Row: 4
<==      Total: 1
==>  Preparing: select emp.id,emp.username,emp.password,emp.name,emp.gender,emp.phone,emp.job,emp.salary,emp.image,emp.entry_date entryDate, emp.dept_id deptId,emp.create_time createTime,emp.update_time updateTIme,dept.name deptName from emp left join dept on emp.dept_id=dept.id where emp.name like concat('%',?,'%') and emp.gender = ? order by emp.id LIMIT ?
==> Parameters: 阮(String), 1(Integer), 5(Integer)
<==    Columns: id, username, password, name, gender, phone, job, salary, image, entryDate, deptId, createTime, updateTIme, deptName
<==        Row: 20, ruanxiaoer, 123456, 阮小二, 1, 13309090020, 2, 10800, https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg, 2018-01-01, 2, 2023-10-20 16:35:33, 2023-10-20 16:36:13, 教研部
<==        Row: 21, ruanxiaowu, 123456, 阮小五, 1, 13309090021, 5, 5200, https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg, 2015-01-01, 3, 2023-10-20 16:35:33, 2023-10-20 16:36:15, 咨询部
<==        Row: 22, ruanxiaoqi, 123456, 阮小七, 1, 13309090022, 5, 5500, https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg, 2016-01-01, 3, 2023-10-20 16:35:33, 2023-10-20 16:36:17, 咨询部
<==        Row: 23, ruanji, 123456, 阮籍, 1, 13309090023, 5, 5800, https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg, 2012-01-01, 3, 2023-10-20 16:35:33, 2023-10-20 16:36:19, 咨询部
<==      Total: 4

可以看到,控制台输出的SQL系统预编译语句并没有日期的限制条件

新增员工

这个直接写代码就行了,和之前新增部门一样,而且由于员工数据同时包括了员工个人信息和工作经历,所以直接在下面一起写了

这里我就只写关键的代码了

//逻辑处理层代码
@Override
public void insertNewEmpData(EmpData empData) {
    empData.setCreateTime(LocalDateTime.now());
    empData.setUpdateTime(LocalDateTime.now());
    empData.setPassword("123456");
    empMapper.insertNewEmp(empData);
}
//XML映射配置代码
<!-- 使用数据库主键-->
<insert id="insertNewEmp" parameterType="org.example.pojo.EmpData" useGeneratedKeys="true" keyProperty="id">
    insert into emp(username,password,name,gender,phone,job,salary,image,entry_date,dept_id,create_time,update_time)
    values (
        #{username},#{password},#{name},#{gender},#{phone},#{job},#{salary},#{image},#{entryDate},#{deptId},#{createTime},#{updateTime}
    )
</insert>

新增员工工作经历

由于一个员工会存在多段工作经历,所以这涉及到了SQL批量插入。我们不知道一次性会插入多少,所以这涉及到了动态插入,也就是动态SQL又要来了

<insert id="insertNewEmpExpr" parameterType="org.example.pojo.EmpExprData" useGeneratedKeys="true" keyProperty="id">
    insert into emp_expr(emp_id,begin,end,company,job) values
    <foreach collection="empExprDataList" item="empExprData" separator=",">
        (#{empExprData.empId},#{empExprData.begin},#{empExprData.end},#{empExprData.company},#{empExprData.job})
    </foreach>
</insert>

这里使用了<foreach>,其相关属性如下:

collection 遍历集合名称
item 集合元素名称
separator 每一次遍历使用的分隔符,例如使用就是(...),(...)
open 遍历结束前拼接的片段
end 遍历结束后拼接的片段

逻辑处理层代码定义如下:

@Override
public void insertNewEmpData(EmpData empData) {
    List<EmpExprData> empExprDataList=empData.getExprList();
    if(!CollectionUtils.isEmpty(empExprDataList)) {
        for(EmpExprData empExprData : empExprDataList) {
            empExprData.setEmpId(empData.getId());
        }
        System.out.println("Now we get It ! empExprMapper.insertNewEmpExpr!");
        empExprMapper.insertNewEmpExpr(empExprDataList);
    }
}

这里有两处需要详细说明一下

①使用CollectionUtils.isEmpty()方法判断集合是否为空,而不是单纯地使用集合对象自带的list.isEmpty()方法,因为可能集合对象本身为null,这样的话调用会出现异常
②由于我们在插入员工个人数据的XML映射配置文件中使用了useGeneratedKeys="true" keyProperty="id"这对属性组合,可以方便地获取插入数据库后的主键值。当useGeneratedKeys设置为true时,MyBatis会通过JDBC获取数据库生成的主键,并将其赋值给Java对象的id属性

删除员工

删除员工由于删除一人相当于是特殊情况下的删除多人,所以我们直接设计删除多人的接口,也就是批量删除

接口请求路径为:/empdelete?id=1,2,3,...,请求方法为DELETE方法

我们实现两个方法:根据Id批量删除员工个人基本信息根据Id批量删除员工工作经历信息

//逻辑处理层方法实现如下:
@Transactional(rollbackFor = {Exception.class})
@Override
public void deleteEmpByIds(List<Integer> empIdList) {
    //1.根据Id批量删除员工个人基本信息
    empMapper.deleteEmpByIds(empIdList);
    //2.根绝Id批量删除员工工作经历信息
    empExprMapper.deleteEmpExprByIds(empIdList);
}
//XML配置文件实现如下:
<delete id="deleteEmpByIds" parameterType="list">
    delete from emp where id in
    <foreach collection="empIdList" item="Id" separator="," open="(" close=")">
        #{Id}
    </foreach>
</delete>

<delete id="deleteEmpExprByIds" parameterType="list">
    delete from emp_expr where emp_id in
    <foreach collection="empIdList" item="Id" separator="," open="(" close=")">
        #{Id}
    </foreach>
</delete>

逻辑持久层处,我们使用了@Transactional关键字保证删除后的数据一致性

XML配置文件这里,使用<foreach>属性中的openclose使得实际拼接的数据格式是这样的:(1,2,3,4,5)

根据ID查询员工信息

//逻辑处理层方法定义如下:
@Override
public EmpData getEmpInfoById(Integer Id) {
    return empMapper.selectEmpInfoById(Id);
}
//XML配置文件如下:
<resultMap id="EmpInfoResultMap" type="org.example.pojo.EmpData">
  <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" />
  <collection property="exprList" ofType="org.example.pojo.EmpExprData">
      <id column="expr_id" property="id" />
      <result column="emp_id" property="empId" />
      <result column="expr_begin" property="begin" />
      <result column="expr_end" property="end" />
      <result column="expr_company" property="company" />
      <result column="expr_job" property="job" />
  </collection>
</resultMap>
<select id="selectEmpInfoById" parameterType="int" resultMap="EmpInfoResultMap">
  select emp.*,emp_expr.id expr_id,emp_expr.emp_id emp_id,
  emp_expr.begin expr_begin,emp_expr.end expr_end,emp_expr.company expr_company,emp_expr.job expr_job
  from emp left join emp_expr on emp.id = emp_expr.emp_id
  where emp.id = #{Id}
</select>

XML配置文件里,由于返回类型的EmpData中需要携带EmpExprData的列表,如果直接和SQL返回结果对接不好对接,所以使用了<resultMap>属性

在MyBatis的XML配置文件中,<resultMap>是一个核心元素,用于定义数据库查询结果与Java对象属性之间的映射关系。它解决了数据库字段名与Java对象属性名不一致的问题,并支持复杂的对象映射。

其中,<resultMap id=是其唯一标识,用于其他语句的引用;type标识其返回类型;column后接数据库列名,property后接JAVA对象属性,用于表示映射关系;<collection>表示封装的集合。

更新员工信息

更新员工信息分为以下步骤:

1.更新员工个人基本信息
2.更新员工工作经历信息,其中根据实际开发需要,又分为两步:

  1. 删除旧的员工工作经历信息
  2. 插入新的员工工作经历信息
//逻辑处理层方法定义如下:
@Transactional(rollbackFor = {Exception.class})
@Override
public void updateEmpInfoByRequest(EmpData empData) {
    //更新员工个人基本信息
    empData.setUpdateTime(LocalDateTime.now());
    empMapper.updateEmpInfoByRequest(empData);
    System.out.println("更新员工信息成功");
    //先删除旧有的工作经历,再添加新的工作经历
    empExprMapper.deleteEmpExprByIds(Collections.singletonList(empData.getId()));
    System.out.println("删除员工旧工作经历信息成功");
    List<EmpExprData> empExprDataList = empData.getExprList();
    if(!CollectionUtils.isEmpty(empExprDataList)) {
        for(EmpExprData empExprData : empExprDataList) {
            empExprData.setEmpId(empData.getId());
        }
//            System.out.println("Now we get It ! empExprMapper.insertNewEmpExpr!");
        empExprMapper.insertNewEmpExpr(empExprDataList);
    }
    System.out.println("添加员工新工作经历信息成功");
}
//XML配置文件如下,由于工作经历信息的删除和插入用的都是已实现的方法,这里不再展示
<update id="updateEmpInfoByRequest" parameterType="org.example.pojo.EmpData">
    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>
    where id = #{id}
</update>

XML配置文件处,这里为了保证代码严谨,使用了动态SQL;动态SQL使用<if>进行条件判断,如果为true则拼接SQL。


posted @ 2026-03-23 12:59  tcswuzb  阅读(4)  评论(0)    收藏  举报