开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情
1. 背景
话说我们的虾搞一通餐饮公司因为业务做越做大,马上就要上市了。这个时候,不停有第三方公司对接他们的业务平台。有一天,小黄的组长老陈交给她一个任务:“你把这六张表全部的内容,按照对方的格式推送到这个地址。要分页去推送不要扫全表,一页大概200条就行了。”
小黄接到任务后陷入了沉思,她开始和小孙讨论这个重复性的操作。
2. 每个表都要分页
小孙问小黄:“每张表都多大?全量同步行吗?”小黄无奈地说:“别那么幼稚行吗?都大概500万条,全量去同步肯定不行,只能分页同步!但是分页去扫,肯定又是很多重复代码,你能不能想想办法?”
小孙灵机一动:“有了!这不是我前两天看的接口默认实现吗?”小黄疑惑地看着他,小孙开始敲击了代码:
首先,我们定义一个统一的DAO层:
public interface PageableDao {
/**
* 统计全体数量
*
* @return
*/
long countAll();
/**
* 若对全体内容进行分页
* 使用此接口获取页数
*
* @param pageSize
* @return
*/
default int getPagesInAll(int pageSize) {
long totalNumber = countAll();
return getPages(totalNumber, pageSize);
}
default int getPages(long totalNumber, int pageSize) {
int pages = (int) (totalNumber / pageSize);
long remainder = totalNumber % pageSize;
if (remainder != 0) {
pages++;
}
return pages;
}
}
在这个DAO层中,我们延迟了对于统计全体数量的实现,这交由每个表去实现即可。
public interface OrderInfoDao extends PageableDao{
/**
* 添加一个订单信息
*
* @param orderInfo
*/
void addOne(OrderInfo orderInfo);
}
就拿设备表来说,它可以有自己的业务,通过继承来扩展这个接口即可。最后的实现类可以实现最具体的内容。
3. 更多示例
小贺听到了他们的讨论,笑着补充说道:“你们这个东西,其实在MybatisPlus里边经常用到,我就拿com.baomidou.mybatisplus.core.mapper.BaseMapper来说吧,它就有几个默认实现。”
public interface BaseMapper<T> extends Mapper<T> {
int insert(T entity);
int deleteById(Serializable id);
int deleteById(T entity);
int deleteByMap(@Param("cm") Map<String, Object> columnMap);
int delete(@Param("ew") Wrapper<T> queryWrapper);
int deleteBatchIds(@Param("coll") Collection<?> idList);
int updateById(@Param("et") T entity);
int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);
T selectById(Serializable id);
List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);
List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);
default T selectOne(@Param("ew") Wrapper<T> queryWrapper) {
List<T> ts = this.selectList(queryWrapper);
if (CollectionUtils.isNotEmpty(ts)) {
if (ts.size() != 1) {
throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records", new Object[0]);
} else {
return ts.get(0);
}
} else {
return null;
}
}
default boolean exists(Wrapper<T> queryWrapper) {
Long count = this.selectCount(queryWrapper);
return null != count && count > 0L;
}
Long selectCount(@Param("ew") Wrapper<T> queryWrapper);
List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);
List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);
List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);
<P extends IPage<T>> P selectPage(P page, @Param("ew") Wrapper<T> queryWrapper);
<P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param("ew") Wrapper<T> queryWrapper);
}
这个里面的selectOne就复用了selectList方法,减少了重复的代码。
总结
本例中,我们介绍了接口的默认实现以减少重复代码。我们在实际开发过程中,可以反复思考:某几个接口是否有同样的逻辑,可以放在子类去实现。同一级的接口,是否可以提供默认的实现来调用或引用?
关于接口的默认实现,你又有哪些好的实践呢?欢迎你在评论区和我讨论。