【SpringBoot+MP】针对复杂业务来手动封装一些涉及到多表操作的删除、分页查询方法_mp实现分页查询
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
文章目录
前言
最近也是遇到了一些比较复杂的业务MP内部提供的方法显然已经不能解决问题针对场景需要自己手动封装一些方法来用也是让自己明白了项目不单单都是简单的CRUD涉及到多表还是比较复杂。
一.扩展MP提供的方法
场景一删除
在以前学习OOP中继承的时候讲到在一组继承关系中为了提高代码复用性可扩展父类的方法。
大家都知道MP的特色就是开发者不用写SQL而这背后的原理是在一次又一次接口实现、类的继承中体现的直接调用事先封装好的方法那如果他提供的方法不能用于特定场景是不是也可以在接口中扩展一下。
既然并不能帮助我们解决万难那么在一些特殊的场景我们不能局限于使用它提供的方法针对场景要进行适当“改装”。所以当“时机成熟之时”我们可以扩展父类的方法扩展功能
当实体间存在联系也就是几张表之间相互关联。如果想要删除表中的一则信息我们肯定是要考虑他与其他实体的联系比如我删除一个菜类表中的一条数据这个菜类下可能有菜品也可能关联套餐如果直接remove掉选中的菜类那么我另两个实体中的数据就会丢失
所以我如果想要删除菜类的数据直接通过MP提供的remove方法显然是不够严谨全面的。在通过service调用remove方法之前得加上贴合实际场景的判断条件。
这就要求我们对MP接口里的方法进行扩展
怎么改进呢反正层与层之间是继承、实现那我在接口里重新定义一个remove方法不去用他给我提供的不就🆗了
类似于这样一个场景
此时前端已经向我们发来了请求
在其实现类里重写该方法并加上针对“是否关联菜类套餐”做一个逻辑判断若不关联则直接调用父类的方法删除直接删除无影响反之则进入if()判断条件抛出异常并终止删除操作
怎么才能表示他有关联菜品套餐呢
直接根据相同的id查询就可并返回查询到的行数是否大于0来作为判断条件
在实现类中通过MP实现起来也就变得十分清晰明了简简单单就像这样
/**
* 这个方法是我手动封装的改装的为了就是解决特殊情况
* 接收来自前端-接口-实现类的形参中ids参数并进行等值查询
* @param ids
*/
@Override
public void remove(Long ids) {
//条件构造器
LambdaQueryWrapper<Dish> lqw1 = new LambdaQueryWrapper<>();
//添加查询条件
lqw1.eq(Dish::getCategoryId, ids);
//返回查询行数
int count1 = dishService.count(lqw1);
//查询当前分类是否关联了菜品如果关联就抛出一个业务异常
if (count1 > 0) {
//已将关联菜品则抛出一个定义好的业务异常
throw new CustomException("菜类已关联彩菜品无法删除");
}
LambdaQueryWrapper<Setmeal> lqw2 = new LambdaQueryWrapper<>();
lqw2.eq(Setmeal::getCategoryId, ids);
int count2 = setmealService.count(lqw2);
if (count2 > 0) {
throw new CustomException("菜类已关联套餐无法删除");
}
//如果不关联 则直接用框架的方法根据id删除它
super.removeById(ids);
}
上述的方法已经被我写到了实现类中在Controller层里我们注入该类对应的接口即可使用自己扩展的remove方法了
二.多表操作与事务
场景二保存
同样地在一个保存前端信息的场景中由于前端的信息涉及到了两张表我的实体不能一次性封装所有的数据我需要扩展实体类来封装信息而这就涉及到了多表的操作也需要在Service层接口中扩展一个新的方法来处理两张表的信息。
首先定义一个新的DishDto实体类通过继承获得了Dish的属性为了能够保存DishFlavor表中的属性我在此类中做出如下扩展
@Data
public class DishDto extends Dish {
//用于数据传输 由于Dish中没有flavor属性所以需要此类来扩展此类
private List<DishFlavor> flavors=new ArrayList<DishFlavor>(); //接收页面提交的flavor属性
}
经常使用MP的都知道一般都是一张表对应一个实体类和一个Service为了少写不必要的表我直接在在形参里传入DishDto来封装前端的数据然后在方法里操作DishDto中属于各自表的信息。
那么在方法里是怎么操作多表的
前端的信息已经被封装到了形参中的实体为了将菜品的基本信息保存到dish表可以直接用Dish的Service对象来调用save方法为了保存DishFlavor表中的属性到dish_flavor表则是通过DishFlavor的service来调用saveBatch保存flavor集合
@Override
@Transactional //由于涉及到多张表的操作这里要开启事务
public void saveWithFlavor(DishDto dishDto) {
//保存菜品基本信息到dish表
this.save(dishDto);//在这个类里我直接用this
Long dishId = dishDto.getId();//菜品id
//菜品口味
//由于少封装了dishId这里要遍历集合补充一下
List<DishFlavor> flavors = dishDto.getFlavors();
flavors=flavors.stream().map((temp)->{ // stream流来遍历
temp.setDishId(dishId);
return temp;
}).collect(Collectors.toList());
//保存菜品口味到dish_flavor,保存集合用saveBatch()方法dishDto.getFlavors()得到口味集合
dishFlavorService.saveBatch(flavors);
}
有人可能会问上面保存flavor字段的操作为什么比较复杂甚至还需要遍历
由于Java的单继承的机制自定义的DTO类只继承了Dish类而这就导致该类丢失了dishId属性。因为DishFlavor表中的属性的保存要和dishId相绑定存入表中所以在方法里我们需要遍历一遍给flavor设置dishId。
有人可能会问你为什么不在DishDto里直接新定义一个属性dishId呢
因为我无法给Long型数据指定泛型而且也没有对应的继承关系所以只能在方法中通过set方法获得DishFlavor的dishId。
写到这里已经又一次地在MP的基础上扩展完了方法而使用到该方法则是在Controller层中直接调用来操作封装前端信息的实体就像这样
@PostMapping()
public R<String> save(@RequestBody DishDto dishDto) {
dishService.saveWithFlavor(dishDto);
return R.success("菜品信息保存成功");
}
最后也是成功完成了保存
场景三修改
在这样一个场景中更新菜品信息的同时更新口味信息两者不是一张表不能一次性更改
为了实现这一功能同样需要自己在接口里扩展方法
在实现类中针对前端传来的实体类对应的数据首先应更新菜品信息也就是修改dish表可以直接利用MP提供的updateById方法this.updateById(dishDto);
来修改
其次对于DishFlavor表中的属性也就是对dish_flavor表的操作就稍微复杂得先删除菜品中的口味后给菜品设置新的口味信息:
主要通过遍历来动态绑定id赋予口味信息给菜品
首先从DishDto里拿到用户选择的口味信息我们通过.getFlavor()方法拿到口味信息value和name并封装到集合里由于未与dishId绑定所以我们需要遍历一遍集合并将DishDto里的dishId赋给集合里的元素
注 两张表之间是通过dish表的id字段连接dish_flavor表中的dish_id所以为了连接到要将dish表的id动态地赋给dish_flavor表中的dish_id
@Override
@Transactional
public void updateWithFlavor(DishDto dishDto) {
//根据id选择修改
this.updateById(dishDto);
//新建查询查询口味信息
LambdaQueryWrapper<DishFlavor> dishDtoLambdaQueryWrapper = new LambdaQueryWrapper<>();
//找出当前菜品对应的口味信息
dishDtoLambdaQueryWrapper.eq(DishFlavor::getDishId, dishDto.getId());
//删除指定菜品的口味信息
dishFlavorService.remove(dishDtoLambdaQueryWrapper);
//从DishDto里拿到用户选择的口味信息
List<DishFlavor> flavors = dishDto.getFlavors();
//遍历一遍flavors赋给他新的id
flavors = flavors.stream().peek((temp) -> temp.setDishId(dishDto.getId())).collect(Collectors.toList());
//保存口味信息到dish_flavor表中
dishFlavorService.saveBatch(flavors);
}
最后在Controller层中直接调用扩展的方法实现功能