乐优商城项目总结——05品牌查询(stream流,自定义异常等)

品牌查询

品牌查询实现步骤基本跟分类差不多,只不过有的需要接收很多参数。

代码

service:

/**
     * 分页查询spu
     *
     * @param page
     * @param rows
     * @param saleable
     * @param key
     * @return
     */
    @GetMapping("/spu/page")
    public ResponseEntity<PageResult<Spu>> querySpuByPage(
            @RequestParam(value = "page", defaultValue = "1") Integer page,//页面
            @RequestParam(value = "rows", defaultValue = "5") Integer rows,//一页几条
            @RequestParam(value = "saleable", required = false) Boolean saleable,//是否下架
            @RequestParam(value = "key", required = false) String key//搜索条件,false代表不查询
    ) {
        return ResponseEntity.ok(goodsService.querySpuByPage(page, rows, saleable, key));
    }

service:

   /**
     * 分页查询spu
     *
     * @param page
     * @param rows
     * @param saleable
     * @param key
     * @return
     */
    public PageResult<Spu> querySpuByPage(Integer page, Integer rows, Boolean saleable, String key) {
        //分页
        PageHelper.startPage(page, rows);
        //过滤
        Example example = new Example(Spu.class);
        Example.Criteria criteria = example.createCriteria();
        //搜索字段过滤
        if (StringUtils.isNotBlank(key)) {
            criteria.andLike("title", "%" + key + "%");
        }
        //上下架过滤
        if (saleable != null) {
            criteria.andEqualTo("saleable", saleable);
        }
        //默认排序
        example.setOrderByClause("last_update_time DESC");
        //查询
        List<Spu> spus = spuMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(spus)) {
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        //解析分类和品牌的名称
        loadCategoryAndBrandName(spus);
        //解析分页结果
        PageInfo<Spu> info = new PageInfo<>(spus);
        return new PageResult<>(info.getTotal(), spus);
    }
   /**
     * 解析分类和品牌名称,因为查出来的是id而不是名称
     *
     * @param spus
     */
    private void loadCategoryAndBrandName(List<Spu> spus) {
        for (Spu spu : spus) {
            //处理分类名称
            List<String> names = categoryService.queryByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()))
                    .stream().map(Category::getName).collect(Collectors.toList());
            spu.setCanme(StringUtils.join(names, "/"));
            //处理品牌名称
            spu.setBanme(brandService.queryById(spu.getBrandId()).getName());
        }
    }
  1. 首先需要 PageHelper.startPage();进行分页
  2. 需要查询的时候创建一个通用mapper创建查询对象,把实体类传进去new Example(Spu.class);
  3. 创建一个查询标准example.createCriteria();
  4. 然后就是搜索过滤,排序过滤,就是criteria.什么什么。(但是这样的复杂查询我感觉还是用sql语句查询更简单一些,更加简洁,通用mapper我感觉更适合一些简单增删改查,那些场景用着会更方便。
  5. 最后通过common类中的pageresult的类返回到页面。因为要处理的数据很多,封装到一个通用类会更好管理。
        //解析分页结果
        PageInfo<Spu> info = new PageInfo<>(spus);
        return new PageResult<>(info.getTotal(), spus);

PageResult
里面需要总条数,总页数,当前页数据,这里传入了总条数,页面数据。
T 是泛型,可以传入自己定义的对象
@data是lombok的set,get方法
@NoArgsConstructor是lombok的空参构造(可以自己手写),

@Data
@NoArgsConstructor
public class PageResult<T> {

    private Long total; //总条数
    private Integer totalPage;//总页数
    private List<T> items;//当前页数据

    public PageResult(Long total, List<T> items) {
        this.total = total;
        this.items = items;
    }

    public PageResult(Long total, Integer totalPage, List<T> items) {
        this.total = total;
        this.totalPage = totalPage;
        this.items = items;
    }
}

stream流

上面的代码中有一个方法是解析分类和品牌名称,用到了stream流。

 List<String> names = categoryService.queryByIds(
 Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()))
	           .stream()
	           .map(Category::getName)
	           .collect(Collectors.toList());

我自己的理解:

  1. stream流是jdk1.8的新特性其中之一,这里写的可能看着眼花缭乱,其实还好。
  2. Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()) 可以看做一个整体,看做一个ArrayList集合。
  3. 用.stream把他传入到stream流中,这个流不是跟IO流一样的概念,最简单的就是把他当做一条流水线,把那个ArrayList传入到一个流水线,对它进行操作。
  4. 这里的.map是把他重新装入另一个新的类型,每个输入元素按照规则转换成另一个元素。
  5. 在map里用双冒号(::),这也是jdk1.8新特性,用前面传来的数据循环使用Category中的getName方法,这也是我们需要的类型和数据。
  6. 最后是collect的收集器,把符合条件的数据收集起来,装入list中。
  7. 其实源码中也有介绍几种使用方法,我用有道翻译一下,贴出来
<pre>{@code
 *     // Accumulate names into a List
 *   将名字累积到列表中
 * 
 *     List<String> list = people.stream().map(Person::getName).collect(Collectors.toList());
 *
 *     // Accumulate names into a TreeSet  
 * 将名称累积到TreeSet  
 *     Set<String> set = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new));
 *
 *     // Convert elements to strings and concatenate them, separated by commas 
 *  将元素转换为字符串,并用逗号分隔
 *     String joined = things.stream()
 *                           .map(Object::toString)
 *                           .collect(Collectors.joining(", "));
 *
 *     // Compute sum of salaries of employee
 * 计算员工的工资总和
 *     int total = employees.stream()
 *                          .collect(Collectors.summingInt(Employee::getSalary)));
 *
 *     // Group employees by department
 * 按部门划分的集团员工
 *     Map<Department, List<Employee>> byDept
 *         = employees.stream()
 *                    .collect(Collectors.groupingBy(Employee::getDepartment));
 *
 *     // Compute sum of salaries by department
 * 按部门计算工资总额
 *     Map<Department, Integer> totalByDept
 *         = employees.stream()
 *                    .collect(Collectors.groupingBy(Employee::getDepartment,
 *                                                   Collectors.summingInt(Employee::getSalary)));
 *
 *     // Partition students into passing and failing
 * 把学生分成及格和不及格
 *     Map<Boolean, List<Student>> passingFailing =
 *         students.stream()
 *                 .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));
 *
 * }</pre>

可以看到我用的也就是第一种,这是官方源码的注释,几乎不会出错,不会用可以按照这个比葫芦画瓢,稳稳的。

自定义异常

自定义异常是项目中可以全局使用的,所以要把它弄到common(一个工具类的微服务,但是没注册到注册中心)中。

为什么用自定义异常

使用自己定义的异常,可以使异常信息准确美观,有条理,最主要的一点是准确,假如输入信息报错最后springmvc最后会报500错误,可实际是400错误。
使用自定义异常可以让开发人员快速定位到错误地点,准确的报错。

步骤

在这里插入图片描述

首先是拦截异常:

@ControllerAdvice
public class CommonExceptionHandler {

    @ExceptionHandler(LyException.class)
    public ResponseEntity<ExceptionResult> myException(LyException e) {
        return ResponseEntity.status(e.getExceptionEnum().getValue()).body(new ExceptionResult(e.getExceptionEnum()));
    }

LyException继承了 RuntimeException 类,如果要拦截其他异常可以多写几个。

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class LyException extends RuntimeException{
    private ExceptionEnum exceptionEnum;
}

下面是枚举类:
因为太多了,我删了一点。

@Getter
@NoArgsConstructor
@AllArgsConstructor
public enum ExceptionEnum {
    /**
     * 枚举:一组相同类型的常量,有固定实例个数,所以要提前设定
     * 下面格式是简写的,实际是用final修饰的
     * 枚举要定义在类的最前面
     */
    BRAND_CREATE_FAILED(500, "新增品牌失败"),
    BRAND_NOT_FOUND(404, "品牌不存在,查询失败"),
    UPDATE_BRAND_FAILED(500, "品牌更新失败"),
    UPDATE_ORDER_STATUS_FAILED(500, "品牌更新失败"),
    DELETE_BRAND_EXCEPTION(500, "删除品牌失败"),
    CREATE_ORDER_ERROR(500,"创建订单失败"),
		......
		......
		......
    SPEC_GROUP_CREATE_FAILED(500, "新增规格组失败"),
    SPEC_GROUP_NOT_FOUND(204, "规格组查询失败"),
    DELETE_SPEC_GROUP_FAILED(500, "商品规格组删除失败"),
    UPDATE_SPEC_GROUP_FAILED(500, "商品规格组更新失败"),
    CART_NOT_FOUND(403, "购物车为空"),


    ;
    //    状态码
    private int value;
    //    消息
    private String message;

}

最后是ExceptionResult,这个是要返回到页面上的类

@Data
public class ExceptionResult {

    private int status;

    private String message;

    private Long timestamp;


    public ExceptionResult(ExceptionEnum em) {
        this.status = em.getValue();
        this.message = em.getMessage();
        this.timestamp = System.currentTimeMillis();
    }
}

最后要用的时候抛出去就好了。
在这里插入图片描述
差别对比:

这是没用的时候:
在这里插入图片描述
这是用自定义异常的时候:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值