说明:在周志明著作《凤凰框架》的最后一章,提到软件研发的一个概念——“架构腐化”,大意是代码越来越多,脏代码无法避免。延缓架构腐化需要研发团队都有较高的水平,对项目架构有整体的把握,并且富有责任心,及时清理不必要的代码。
本文介绍一种项目中方法无显性调用,但不可删除的代码场景,作为以后清理代码的预防针——不是所有没调用的代码都能删除。
场景
一个分页查询接口,传入一个DTO对象,DTO中包含前端传递的条件参数,查询返回符合条件的记录。
基础操作
分页查询接口
@PostMapping("/page")
public List<User> list(@RequestBody UserDTO userDTO) {
return userService.list(userDTO);
}
DTO 对象
import lombok.Data;
import java.io.Serializable;
@Data
public class UserDTO implements Serializable {
private Integer age;
private Integer pageNo;
private Integer pageSize;
}
Service 实现类,计算偏移量,传给 Mapper
@Override
public List<User> list(UserDTO userDTO) {
Integer offset = (userDTO.getPageNo() - 1) * userDTO.getPageSize();
return userMapper.list(userDTO, offset);
}
查询 SQL,查询大于某年龄的记录
<select id="list" resultType="com.hezy.pojo.User">
select * from tb_user
<where>
<if test="userDTO.age != null ">
and age > #{userDTO.age}
</if>
</where>
limit #{offset}, #{userDTO.pageSize}
</select>
查询结果
骚操作
上面代码,偏移量 offset 是在 Service 实现类里计算的,其实可以放到 DTO 对象里,如下:
import lombok.Data;
import java.io.Serializable;
@Data
public class UserDTO implements Serializable {
private Integer age;
private Integer pageNo;
private Integer pageSize;
/**
* 计算偏移量
*/
public Integer getOffset() {
return (pageNo - 1) * pageSize;
}
}
然后你以为是在 Service 实现类里调用方法吗?其实根本不用,直接在 Mapper.xml 中使用
(Service 实现类,去掉计算 offset)
@Override
public List<User> list(UserDTO userDTO) {
return userMapper.list(userDTO);
}
(Mapper 不需要传 offset)
List<User> list(@Param("userDTO") UserDTO userDTO);
(Mapper.xml 直接用 对象.offset
获取)
<select id="list" resultType="com.hezy.pojo.User">
select * from tb_user
<where>
<if test="userDTO.age != null ">
and age > #{userDTO.age}
</if>
</where>
limit #{userDTO.offset}, #{userDTO.pageSize}
</select>
敲的时候,编辑器都没有提示对象中有这个字段
看,还是能查出来
分析
回过头来看这个 DTO ,getOffset() 方法没有显性调用,但却能在 Mapper.xml 中产生 offset 变量。
两种解释:
(1)Mybatis 框架在实例化 DTO 时,调用了对象里的 getter() 方法,产生了未被定义的属性;
(2)Mapper.xml 中 #{DTO.xx} 时,实际上就是 DTO.getXx(),因为 DTO 有定义方法,所以能获取到值;
不管是哪种,这种写法都是行得通的。并且这种写法十分高效,可以将不属于 Service 层的一些计算赋值逻辑放入到 DTO 中,直接绑定到 Mapper.xml 中。
如查询距离当前时间 30 天以内的记录,就可以在 DTO 中定义一个方法,如下:
/**
* 30 天之前的日期
*/
public Date getBeginDate() {
return Date.from(LocalDate.now().minusDays(30).atStartOfDay().toInstant(ZoneOffset.UTC));
}
Mapper.xml 直接用
<select id="list" resultType="com.hezy.pojo.User">
select * from tb_user
<where>
<if test="userDTO.age != null ">
and age > #{userDTO.age}
</if>
and create_time > #{userDTO.beginDate}
</where>
limit #{userDTO.offset}, #{userDTO.pageSize}
</select>
看控制台打印的 SQL,获取到了 DTO 中的方法返回值
对于不懂这套写法的程序员,可能看到 DTO 中一堆没有调用的方法,且没有明确注释说明,就很有可能把这些方法作为不必要的代码清除掉。