Implementing Batch Dish Deletion with Validation and Data Cleanup

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.

Tags: java Spring Boot MyBatis REST API CRUD Operations

Posted on Tue, 13 Oct 2026 16:48:03 +0000 by dflow