苍穹外卖项目日记(day11)
苍穹外卖|项目日记(day11)
前言: 今天的任务不算难, 但如何把代码接口写的像老师那样优雅才是难度.
今日收获:
1.欣赏了"雅"代码的写法(stram流, 封装查询)
2.加深了对动态sql的使用(group by, order by, limit)
一.stream流的使用
1. Stream的基本概念
Stream(流)代表着一系列元素,支持顺序和并行聚合操作。它不是数据结构,而是从数据源(如集合、数组或I/O资源)获取数据的管道。
主要特点:
- 不是数据结构:不存储数据,只是从源获取数据
- 函数式编程:支持lambda表达式和方法引用
- 惰性执行:中间操作是惰性的,只有终端操作才会触发实际计算
- 可消费性:流只能被消费一次
创建stream流的方式:
// 从集合创建
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream1 = list.stream();// 从数组创建
String[] array = {"a", "b", "c"};
Stream<String> stream2 = Arrays.stream(array);// 直接创建
Stream<String> stream3 = Stream.of("a", "b", "c");// 创建无限流
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2);
Stream<Double> randomStream = Stream.generate(Math::random);
3.Stream的操作类型
中间操作(Intermediate Operations)
返回新的Stream,可以链式调用:
.filter(Predicate<T>) // 过滤
.map(Function<T,R>) // 转换
.flatMap(Function<T,Stream<R>>) // 扁平化转换
.distinct() // 去重
.sorted() // 排序
.limit(long) // 限制元素数量
.skip(long) // 跳过前n个元素
终端操作(Terminal Operations)
触发实际计算,返回非Stream结果:
.forEach(Consumer<T>) // 遍历
.collect(Collector) // 收集为集合
.toArray() // 转为数组
.reduce(BinaryOperator<T>) // 归约
.min(Comparator<T>) // 最小值
.max(Comparator<T>) // 最大值
.count() // 计数
.anyMatch(Predicate<T>) // 任意匹配
.allMatch(Predicate<T>) // 全部匹配
.noneMatch(Predicate<T>) // 无匹配
.findFirst() // 第一个元素
.findAny() // 任意元素
项目中的使用:
示例:
LocalDateTime beginTime = LocalDateTime.of(begin, LocalTime.MIN);
LocalDateTime endTime = LocalDateTime.of(end, LocalTime.MAX);List<GoodsSalesDTO> salesTop10 = orderMapper.getSalesTop10(beginTime, endTime);List<String> names = salesTop10.stream().map(GoodsSalesDTO::getName).collect(Collectors.toList());
String nameList = StringUtils.join(names, ",");List<Integer> numbers = salesTop10.stream().map(GoodsSalesDTO::getNumber).collect(Collectors.toList());
String numberList = StringUtils.join(numbers, ",");
对查询条件用map封装:
1. 动态参数传递
使用Map
可以灵活地传递不定数量、不定类型的参数。当查询条件可能变化时(比如有时需要添加更多条件),不需要修改方法签名,只需往Map中添加新的键值对即可。
2. 与ORM框架的兼容性
大多数ORM框架(如MyBatis)都支持Map作为参数:
- MyBatis示例:可以直接在XML映射文件中使用
#{beginTime}
、#{endTime}
等表达式引用Map中的值 - 避免了为每个查询创建专门的参数对象
3. 参数命名清晰
Map的键值对形式使得参数意义明确:
map.put("beginTime", beginTime); // 比直接传beginTime更明确其用途
map.put("endTime", endTime);
map.put("status", Orders.COMPLETED);
4. 便于参数重用
同一个Map可以在多个查询中复用,或者添加/删除某些参数后用于不同的查询。
5. 简化方法签名
// 相比定义包含多个参数的方法:
Double sumByParams(LocalDateTime begin, LocalDateTime end, OrderStatus status);// 使用Map只需一个参数:
Double sumByMap(Map<String, Object> params)
项目中使用示例:
// select sum(amount) from orders where order_time > beginTime and order_time < endTime and status = 5Map map = new HashMap();map.put("beginTime", beginTime);map.put("endTime", endTime);map.put("status", Orders.COMPLETED);Double turnover = orderMapper.sumByMap(map);turnover = turnover == null ? 0.0 : turnover;turnoverList.add(turnover);
二. 销量排名统计的动态sql(Top10)
在最开始, 我不了解如何获取排名前10的菜品, 想法是获取所有订单, 然后遍历获取前10的菜品, 但看了老师的代码后, 才知道对待数据的条件查询, 都可以在sql中实现.
代码示例:
<select id="getSalesTop10" resultType="com.sky.dto.GoodsSalesDTO">select od.name, sum(od.number) as numberfrom order_detail od, orders owhere od.order_id = o.idand o.status = 5 // 指定订单的状态是已完成<if test="begin != null">and o.order_time > #{begin}</if><if test="end != null">and o.order_time < #{end}</if>group by od.name // 按订单菜品的名字分组order by number desc // 进行降序limit 10 // 获取前10的菜品</select>