品牌查询
品牌查询实现步骤基本跟分类差不多,只不过有的需要接收很多参数。
代码
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());
}
}
- 首先需要 PageHelper.startPage();进行分页
- 需要查询的时候创建一个通用mapper创建查询对象,把实体类传进去new Example(Spu.class);
- 创建一个查询标准example.createCriteria();
- 然后就是搜索过滤,排序过滤,就是criteria.什么什么。(但是这样的复杂查询我感觉还是用sql语句查询更简单一些,更加简洁,通用mapper我感觉更适合一些简单增删改查,那些场景用着会更方便。)
- 最后通过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());
我自己的理解:
- stream流是jdk1.8的新特性其中之一,这里写的可能看着眼花缭乱,其实还好。
- Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()) 可以看做一个整体,看做一个ArrayList集合。
- 用.stream把他传入到stream流中,这个流不是跟IO流一样的概念,最简单的就是把他当做一条流水线,把那个ArrayList传入到一个流水线,对它进行操作。
- 这里的.map是把他重新装入另一个新的类型,每个输入元素按照规则转换成另一个元素。
- 在map里用双冒号(::),这也是jdk1.8新特性,用前面传来的数据循环使用Category中的getName方法,这也是我们需要的类型和数据。
- 最后是collect的收集器,把符合条件的数据收集起来,装入list中。
- 其实源码中也有介绍几种使用方法,我用有道翻译一下,贴出来
<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();
}
}
最后要用的时候抛出去就好了。
差别对比:
这是没用的时候:
这是用自定义异常的时候: