SSM入门详解--reggieDay2

60 阅读4分钟

练习分类业务

对于分类业务,以同样的操作完成CRUD。只需注意分类包括菜品类和套餐类

提示:

三层架构Controller,Service,Mapper

  • Get

    • Page
    • LambdaQuerryMapper
    • page
  • Post

    • @RequestBody
    • Service.save
  • Put

    • get

    • put

      • updateById
  • Delete

    • removeById
  • 代码

    查询

    前端的分页查询请求:DET /category/page?page=1&pageSize=10

    自下而上创建 实体类 Category→ Mapper→Service → Controller

    其他没啥特别的,直接看Controller:

    //CategoryController.java
    
    @RestController
    @RequstMapping("/category")
    @Slf4j
    public class CategoryController(){
    	@Autowired
    	private CategorService categoryService;
    	
    	@GetMapping("page") // 对于get请求,直接使用同名变量即可获取参数
    	public R<Page> page(int page,int pageSize){
    		Page<Category> pageInfo = new page(page,pageSize);
    		LambdaQuerryWrapper querryWrapper = new LambdaQuerryWrapper<>();
    		querryWrapper.orderByAsc(Category::getSort);
    		categoryServive.page(pageInfo,querryWrapper);
    		return R.success(pageInfo);
    	}
    }
    

    新增

    请求POST /category body中携带json对象

        @PostMapping
        public R<String> save(@RequestBody Category category){
            log.info("category:{}",category);
            categoryService.save(category);
            return R.success("新增分类成功");
         }
    

    通过 @RequestBody 即可拿到body中的json数据

    修改

    同上

       @PutMapping
        public R<String> save(@RequestBody Category category){
            log.info("category:{}",category);
            categoryService.update(category);
            return R.success("新增分类成功");
         }
    

    删除

    简单的删除同理:

    请求:DELETE /categoty?id=xxx

    @DeleteMapping
    public R<String> delete(Long id){
    	categoryService.removeById(id);
    	return R.success("删除成功");
    }
    

    但这样粗暴的删除明显不合适。在接收一个删除请求时,应该先根据id查询该分类下有无关联。当关联了其他餐品和套餐时,不可删除,而要抛出一个异常提示。以下是对该方法的实际优化:

自定义异常

全局异常类:

common下自定义异常类

//CustomException.java

public class CustomException extends RuntimeException{
    public CustomException(String message) {
        super(message);
    }
}

和全局异常处理器

//GlobalExceptionHandler.java
public class GlobalExceptionHandler {
	  @ExceptionHandler(CustomException.class)
    public R<String> exceptionHandler(CustomException ex) {
        log.error(ex.getMessage());
        return R.error(ex.getMessage());
    }

}
  • 完整的删除功能

    使用这个异常即可实现完整的删除

    在Controller层不能直接DeleteByID,需要调用我们自定义的一个remove方法,在service实现

    //CategoryController.java
    
    delete(id){
    	service.remove(id);
    	return R.success("成功");
    }
    

    servie层实现

    //CategoryServiceImpl.java
    
    @Override
    public void remove(Long id){
    				LambdaQueryWrapper<Dish>  dishLambdaQueryWrapper = new LambdaQueryWrapper<>();
            dishLambdaQueryWrapper.eq(Dish::getCategoryId, id);//条件查询
            int count = dishService.count(dishLambdaQueryWrapper);
            System.out.println("查询关联情况");
            if (count > 0) {
                throw new CustomException("当前分类关联的菜品");
            }
            LambdaQueryWrapper<Setmeal> setMealLambdaQueryWrapper = new LambdaQueryWrapper<>();
            setMealLambdaQueryWrapper.eq(Setmeal::getCategoryId, id);
            count = setMealService.count(setMealLambdaQueryWrapper);
            if (count > 0){
                throw new CustomException("当前分类关联了套餐");
            }
            System.out.printf("无关联,执行删除%d",id);
            super.removeById(id);
    
    }
    

菜品业务的多表联查

查询—这是一个经典的多表联查案例

和上面不同的是,菜品表没有直接的“分类名称”字段,需要通过“分类id“去分类表中查到该菜品对应的分类名称。然后把我们所需的数据组装成一个新的数据转换层对象DishDto,把这些Dto记录放入Page容器中返回。

创建Dto

//DishDto.java
public class DishDto extends Dish{
	private List<DishFlavor> flavors =new ArrayList<>();
	private String categoryName;
	private Integer copies;
}

Dto分页查询

    /**
     * 菜品管理的分页查询
     */
    @GetMapping("/page")
    public R<Page> page(int page, int pageSize, String name){
		    Page<DishDto> dishDtoPage = new Page<>(); //这是最终的Dto结果对象
		  //先正常查dish表
        //构造分页构造器对象
        Page<Dish> pageInfo = new Page<>(page,pageSize);
        
        //条件构造器
        LambdaQueryWrapper<Dish> queryWrapper=new LambdaQueryWrapper<>();
        //添加过滤条件
        //当name不为空时对Dish表的name进行模糊匹配 %name%
        queryWrapper.like(name!=null,Dish::getName,name);
        //添加排序条件
        queryWrapper.orderByDesc(Dish::getUpdateTime);
        dishService.page(pageInfo,queryWrapper);
			//dish查询结束
				//把已有的dish的结果装入DtoPgae,忽略records,因为records要单独处理
        BeanUtils.copyProperties(pageInfo,dishDtoPage,"records");
      //多表联查的核心操作:取出records,用每一项记录中的id查Category表,把该项record和查出的分类名装入 dot,所有的dto作为List封装进DtoPage.
        List<Dish> records = pageInfo.getRecords();
        List<DishDto> list = records.stream().map((item)->{
            DishDto dishDto = new DishDto();
            BeanUtils.copyProperties(item,dishDto);
            Long categoryId = item.getCategoryId();
            Category category=categoryService.getById(categoryId);
            if(category!=null){
                String categoryName=category.getName();
                dishDto.setCategoryName(categoryName);
            }
            return dishDto;
        }).collect(Collectors.toList());
        dishDtoPage.setRecords(list);
        return R.success(dishDtoPage);
    }

调用dishService.page后,pageInfo已经有了一些基础信息。然后使用

BeanUtils.copyProperties(pageInfo,dishDtoPage,"records");

将pageInfo的属性值复制到dishDtoPage中,忽略records字段

  • BeanUtils.copyProperties原型
  • public static void copyProperties(Object source, Object target, String... ignoreProperties)
  • 快速复制新对象,省去大量set 和 get调用。
  • 赋值还是创建
  • 可选参数用于忽略某些字段

map映射:records.stream.map()

  • 获取原分页结果作为List

  • 创建一个新的List

  • 遍历映射

    • list.stream.map((item)→{ return }).collect(Collectors.toList());
  • copy:dish→dto

  • 从dish的categoryid字段定位到category对象,取出分类名加入到Dto的CategoryName字段

    将所有映射结果collect为一个List

文件传输

前端基于form表单的文件上传

form表单

  • method=“post”
  • enctype=“multipart/form-date/”

后端接

指定一个路径

//CommonController.java
@Value(${reggie.path})

还是一个请求路径的事,Post就完了

注意一下前端的参数名,这里是 “filename”

//CommonController.java
@RestController
@RequestMapping("/commom")
public class CommonController{
	@PostMapping("/upload")
	public Resulet<String> uploadFile(MultipartFile filename){
				String originalFilename = file.getOriginalFilename();
        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
        String filename = UUID.randomUUID() + suffix;

        File dir = new File(basePath);
        if(!dir.exists()){
            dir.mkdirs();
        }

        try{
            file.transferTo(new File(basePath+filename));
        }catch (IOException e) {
            e.printStackTrace();
        }
        return R.success(filename);
	}
}

前端要

发请求:Get /common/download?name=${Response.data}

后端给

/**
 * 文件下载,图片回显浏览器
 */
@GetMapping("/download")
public void download(String name, HttpServletResponse response){
    try {
        //输入流,通过输入流读取文件内容
        FileInputStream fileInputStream = new FileInputStream(new File(basePath+name));
        //输出流,通过输出流将文件写回浏览器,在浏览器展示图片
        ServletOutputStream outputStream = response.getOutputStream();

        response.setContentType("image/jpeg");//设置响应的文件类型
        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len = fileInputStream.read(bytes)) != -1){//用while循环一直写,写到-1证明写完了
            outputStream.write(bytes,0,len);
            outputStream.flush();
        }
        //关闭资源
        outputStream.close();
        fileInputStream.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

菜品的增删改查不在赘述,自行练习吧。