Business Rules
- Single or multiple dishes can be removed in one operation.
- Dishes currently avialable for sale cannot be deleted.
- Dishes linked to setmeal packages cannot be removed.
- Associated flavor records must be cleared when a dish is deleted.
A single endpoint handles both single and batch deletion. To verify if a dish belongs to a setmeal, query the setmeal_dish mapping table using the dish ID. If a corresponding setmeal ID exists, the dish is considered associated and deletion is blocked.
Controller Layer
@DeleteMapping
@ApiOperation("Batch delete dishes")
public Result removeDishes(@RequestParam List<Long> dishIds) {
// Spring MVC resolves the comma-separated string into a List<Long>
log.info("Batch deleting dishes: {}", dishIds);
dishService.batchRemove(dishIds);
return Result.success();
}
Service Interface
void batchRemove(List<Long> dishIds);
Service Implementation
The method is marked with @Transactional to ensure atomicity, as it involves multiple validation steps and deletion operations.
@Transactional
public void batchRemove(List<Long> dishIds) {
// 1. Check if any dish is currently on sale
for (Long dishId : dishIds) {
Dish dish = dishMapper.queryById(dishId);
if (dish.getStatus() == StatusConstant.ENABLE) {
throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);
}
}
// 2. Check if dishes are associated with any setmeal
List<Long> relatedSetmealIds = setMealDishMapper.findSetmealIdsByDishIds(dishIds);
if (relatedSetmealIds != null && !relatedSetmealIds.isEmpty()) {
throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);
}
// 3. Perform batch deletion
dishMapper.deleteByIds(dishIds);
dishFlavorMapper.deleteByDishIds(dishIds);
}
Mapper Interfcaes
SetMealDishMapper
@Mapper
public interface SetMealDishMapper {
// Fetch setmeal IDs based on a list of dish IDs
List<Long> findSetmealIdsByDishIds(List<Long> dishIds);
}
DishMapper
@Delete("delete from dish where id = #{id}")
void deleteById(Long id);
// Batch delete dishes
void deleteByIds(List<Long> ids);
DishFlavorMapper
@Delete("delete from dish_flavor where dish_id = #{dishId}")
void deleteByDishId(Long dishId);
// Batch delete flavors
void deleteByDishIds(List<Long> dishIds);
MyBatis XML Mappings
SetMealDishMapper.xml
<select id="findSetmealIdsByDishIds" resultType="java.lang.Long">
SELECT setmeal_id FROM setmeal_dish WHERE dish_id IN
<foreach collection="dishIds" item="dishId" open="(" close=")" separator=",">
#{dishId}
</foreach>
</select>
DishMapper.xml
<delete id="deleteByIds">
DELETE FROM dish WHERE id IN
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>
DishFlavorMapper.xml
<delete id="deleteByDishIds">
DELETE FROM dish_flavor WHERE dish_id IN
<foreach collection="dishIds" item="dishId" open="(" close=")" separator=",">
#{dishId}
</foreach>
</delete>
Optimization Note
Initial implementations might loop through IDs to delete records one by one, which ganerates excessive SQL statements and harms performance. The optimized version uses batch deletion in the persistence layer, passing the entire ID list to a single DELETE ... WHERE id IN (...) query.