自定义业务异常处理类并将其加入全局异常处理器,从而避免业务层直接处理异常造成代码污染,达到业务清晰简洁。
描述
在进行分类模块开发时,删除某个分类时当分类关联了菜品和套餐时,是不允许删除的。我们在管理端删除的时候会提示异常,我们使用自定义业务异常处理类并将其加入全局异常处理器,从而避免业务层直接处理异常造成代码污染,达到业务清晰简洁。
删除分类
删除分类需要注意的是当分类关联了菜品和套餐时,不允许删除。
1 基础删除,不检查关联的菜品
请求:
-
如果忘了加注解@JsonFormat注解,会发生js获取主键时丢失精度,js只能读17位,而雪花算法Long类型是19位。
-
@JsonFormat(shape = JsonFormat.Shape.STRING)
代码:
@DeleteMappingpublic R<String> page(Long ids){boolean success = categoryService.removeById(ids);if(success)return R.success("删除成功");else return R.error("删除失败");}
2 菜品和套餐的实体类,别忘了@JsonFormat
套餐:
/*** 套餐*/
@Data
public class Setmeal implements Serializable {private static final long serialVersionUID = 1L;@JsonFormat(shape = JsonFormat.Shape.STRING)private Long id;//分类id@JsonFormat(shape = JsonFormat.Shape.STRING)private Long categoryId;//套餐名称private String name;//套餐价格private BigDecimal price;//状态 0:停用 1:启用private Integer status;//编码private String code;//描述信息private String description;//图片private String image;@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;@TableField(fill = FieldFill.INSERT_UPDATE)private LocalDateTime updateTime;@TableField(fill = FieldFill.INSERT)@JsonFormat(shape = JsonFormat.Shape.STRING)private Long createUser;@TableField(fill = FieldFill.INSERT_UPDATE)@JsonFormat(shape = JsonFormat.Shape.STRING)private Long updateUser;}
菜品:
/**菜品*/
@Data
public class Dish implements Serializable {private static final long serialVersionUID = 1L;@JsonFormat(shape = JsonFormat.Shape.STRING)private Long id;//菜品名称private String name;//菜品分类id@JsonFormat(shape = JsonFormat.Shape.STRING)private Long categoryId;//菜品价格private BigDecimal price;//商品码private String code;//图片private String image;//描述信息private String description;//0 停售 1 起售private Integer status;//顺序private Integer sort;@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;@TableField(fill = FieldFill.INSERT_UPDATE)private LocalDateTime updateTime;@TableField(fill = FieldFill.INSERT)@JsonFormat(shape = JsonFormat.Shape.STRING)private Long createUser;@TableField(fill = FieldFill.INSERT_UPDATE)@JsonFormat(shape = JsonFormat.Shape.STRING)private Long updateUser;}
3 菜品和套餐的dao和service
建议自己写,很快,复制粘贴总会把实体类之类的写错。
@Mapper
public interface DishMapper extends BaseMapper<Dish> {
}
@Mapper
public interface SetmealMapper extends BaseMapper<Setmeal> {
}
public interface DishService extends IService<Dish> {
}
public interface SetmealService extends IService<Setmeal> {
}
@Service
@Slf4j
public class DishServiceImpl extends ServiceImpl<DishMapper,Dish> implements DishService {
}@Service
@Slf4j
public class SetmealServiceImpl extends ServiceImpl<SetmealMapper,Setmeal> implements SetmealService {
}
4 检查后删除,业务异常捕获
目标:当分类关联了菜品和套餐时,不允许删除。
跟进到IService的remove方法,通过比较器wrapper删除,参数改成Integer的话算重载,可以改:
代码实现:
我前面自己写的时候,没有用异常捕获,是在业务层根据不同情况返回不同数字,然后controller根据数字R.error("错误原因") 。用异常捕获也能实现,两种效果都一样,用异常捕获更规范。
service:
@Overridepublic Integer remove(Long id){LambdaQueryWrapper<Setmeal> wrapper1=new LambdaQueryWrapper<>();wrapper1.eq(Setmeal::getCategoryId,id);LambdaQueryWrapper<Dish> wrapper2=new LambdaQueryWrapper<>();wrapper2.eq(Dish::getCategoryId,id);log.info("套餐查询:{}",setmealService.getMap(wrapper1));log.info("菜品查询:{}",dishService.getMap(wrapper2));//查到有菜品或套餐使用这个分类if(setmealService.count(wrapper1)>0) return -1;if(dishService.count(wrapper2)>0) return -2;return categoryDao.deleteById(id)>0?1:0;}
@DeleteMappingpublic R<String> remove(Long ids){log.info("要删除的id:{}",ids);int success = -3;success=categoryService.remove(ids);log.info("删除状态:{}",success);if(success==1)return R.success("删除成功");else if(success==0) return R.error("网络原因删除失败");else if(success==-1) return R.error("有套餐正在使用此分类");else if(success==-2)return R.error("有菜品正在使用此分类");else return R.error("未知错误");}
异常捕获方案:
service
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper,Category> implements CategoryService{@Autowiredprivate DishService dishService;@Autowiredprivate SetmealService setmealService;/*** 根据id删除分类,删除之前需要进行判断* @param id*/@Overridepublic void remove(Long id) {LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>();//添加查询条件,根据分类id进行查询dishLambdaQueryWrapper.eq(Dish::getCategoryId,id);int count1 = dishService.count(dishLambdaQueryWrapper);//查询当前分类是否关联了菜品,如果已经关联,抛出一个业务异常if(count1 > 0){//已经关联菜品,抛出一个业务异常throw new CustomException("当前分类下关联了菜品,不能删除");}//查询当前分类是否关联了套餐,如果已经关联,抛出一个业务异常LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>();//添加查询条件,根据分类id进行查询setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId,id);int count2 = setmealService.count();if(count2 > 0){//已经关联套餐,抛出一个业务异常throw new CustomException("当前分类下关联了套餐,不能删除");}//正常删除分类super.removeById(id);}
}
controller
@DeleteMappingpublic R<String> delete(Long id){log.info("删除分类,id为:{}",id);//categoryService.removeById(id);if(categoryService.remove(ids)) return R.success("删除成功");//这里也可以throw业务异常,但不建议,一般都是service抛出异常,controller返回R.error();else return R.error("删除失败");}
业务异常类:
/*** 自定义业务异常类*/
public class CustomException extends RuntimeException {//带参构造方法,重新设置异常信息public CustomException(String message){super(message);}
}
全局异常处理:
@RestControllerAdvice
//@ControllerAdvice(annotations = {RestController.class, Controller.class})
//@ResponseBody
@Slf4j
public class GlobalExceptionHandler {/*** 异常处理方法* @return*/@ExceptionHandler(SQLIntegrityConstraintViolationException.class)public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){log.error(ex.getMessage());if(ex.getMessage().contains("Duplicate entry")){String[] split = ex.getMessage().split(" ");String msg = split[2] + "已存在";return R.error(msg);}return R.error("未知错误");}/*** 异常处理方法* @return*/@ExceptionHandler(CustomException.class)public R<String> exceptionHandler(CustomException ex){log.error(ex.getMessage());return R.error(ex.getMessage());}}