一:需求分析和设计:
- 业务规则: 可以一次删除一个菜品,也可以批量删除菜品
- 起售中的菜品不能删除
- 被套餐关联的菜品不能删除
- 删除菜品后,关联的口味数据也需要删除
接口设计:
数据库设计 :
根据以上的业务分析之后,说明我们想删除员工需要对三张表进行操作。
二:具体的代码实现:
Controll层:
/**
* 批量删除菜品
* @param ids
* @return
*/
@DeleteMapping
@ApiOperation("批量删除菜品")
public Result delete(@RequestParam List<Long> ids){
log.info("批量删除菜品:{}",ids);
dishService.deletebatch(ids);
return Result.success();
}
在delete这个方法上加了一个@RequestParam注解,这个注解的功能是用于将指定的请求参数赋值给方法中的形参。
在这里加了这个注解之后,SpringMVC框架会自动解析前端传过来的String类型的参数,转化成我们需要的List<Long> ids 这样一个集合。
Service层:
/**
* 批量删除菜品
* @param ids
* @return
*/
@Override
@Transactional
public void deletebatch(List<Long> ids) {
//判断当前菜品是否能被删除 -- 是否存在起售中的商品
for (Long id : ids) {
Dish dish = dishMapper.selectById(id);
if(dish.getStatus() == StatusConstant.ENABLE) {//菜品在起售
throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);
}
}
//判断当前菜品是否能够删除 -- 是否被套餐关联了(在套餐表中能查到)
List<Long> setmealIdsByDishIds = setmealDishMapper.getSetmealIdsByDishIds(ids);
if(!setmealIdsByDishIds.isEmpty()){
throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);
}
//删除菜品表中的菜品数据
for (Long id : ids) {
dishMapper.deleteById(id);
//删除菜品关联的口味数据
dishFlavorMapper.deleteById(id);
}
}
这个Service层做了4件事。
1:判断这个菜品是否在起售(根据我们的要求,起售是不允许删除的)
判断这个菜品是否在起售,我们就根据这个菜品的id来查询,返回值肯定是一个Dish,所以我们整体的思路就是在dishMapper中写一个selectById的方法,然后在Service层获取这个selectById方法的返回值,并且获取状态。
dishMapper层代码:
/**
* 根据菜品id查询菜品
* @param id
*/
@Select("SELECT * from sky_take_out.dish where id = #{id}")
Dish selectById(Long id);
2:判断这个菜品是否被套餐所关联了(关联了我们也不能删除)
首先分析一下这个需求,我们如何知道菜品是否被套餐关联?
根据我们上面数据库设计那个模块,我们知道,我们需要一个中间表setmeal_dish,我们需要在这张表中查询。
所以,我们现在思路是:
创建一个SetmealDishMapper,然后里面有根据菜品id查询对应的套餐的方法。
SetmealMapper层:
@Mapper
public interface SetmealDishMapper {
/***
* 根据菜品id查询对应的套餐
* @param dishIds
* @return
*/
List<Long> getSetmealIdsByDishIds(List<Long> dishIds);
}
我们再观察这个查询语句,我们是动态的查询这个dishId,所以这里我们采用动态SQL。
并且利用foreach标签
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.SetmealDishMapper">
<select id="getSetmealIdsByDishIds" resultType="java.lang.Long">
select setmeal_id from setmeal_dish where dish_id in
<foreach collection="dishIds" item="dishid" separator="," open="(" close=")">
#{dishid}
</foreach>
</select>
</mapper>
写到这里,我们就可以对这个结果进行判断了,我们返回的是一个集合
为什么是集合呢,我们这里采用的是批量删除的方式,比如我们选了1,2,3这三个菜品,如果这三个菜品中有任何一个菜品有关联套餐,根据事务的原子性,我们这三个菜品都不能删除。
我们对这个集合进行一个判空处理就行。
处理条件不满足的情况:
根据上面两个步骤来看,这两个条件中,只要有一个不满足,我们直接就可以结束了,那这个时候,我们怎么来处理或者返回这个异常的信息:抛异常即可
3:前两步都判断之后,第三步可以删除了
到这一步之后,就和常规的删除一样了。
4:删除完这个菜品之后,对应的口味数据也要删除
Mapper层(DishMapper和DishFlavorMapper):
/**
* 根据id删除菜品
* @param id
*/
@Delete("delete from sky_take_out.dish where id = #{id}")
void deleteById(Long id);
/**
* 根据id删除口味数据
* @param dishId
*/
@Delete("delete from sky_take_out.dish_flavor where dish_id = #{dishId}")
void deleteById(Long dishId);
三:性能优化:
这个性能优化主要是对:
//删除菜品表中的菜品数据
for (Long id : ids) {
dishMapper.deleteById(id);
//删除菜品关联的口味数据
dishFlavorMapper.deleteById(id);
}
仔细看这一段代码,我们每次遍历一个id的时候,都需要发出两条sql语句,导致性能不高
所以我们的优化思路是将这个遍历的过程方法sql语句中,发送一条sql语句,让一条数据把想要删的数据全删了。
优化后的Service层:
// //删除菜品表中的菜品数据
// for (Long id : ids) {
// dishMapper.deleteById(id);
// //删除菜品关联的口味数据
// dishFlavorMapper.deleteById(id);
// }
//根据集合id批量删除表中的菜品数据
dishMapper.deleteIdBatch(ids);
//根据集合id批量删除相关联的口味数据
dishFlavorMapper.deleteIdBatch(ids);
DishMapper层及注解:
/**
* 批量删除菜品数据
* @param ids
*/
void deleteIdBatch(List<Long> ids);
<delete id="deleteIdBatch">
delete from sky_take_out.dish where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
DishFlavorMapper层:
/**
* 根据id批量删除口味数据
* @param dishids
*/
void deleteIdBatch(List<Long> dishids);
<delete id="deleteIdBatch">
delete from sky_take_out.dish_flavor where id in
<foreach collection="dishids" item="dishid" close=")" open="(" separator=",">
#{dishid}
</foreach>
</delete>