[苍穹外卖]-04菜品管理接口开发

效果预览

新增菜品

需求分析

查看产品原型分析需求, 包括用到哪些接口, 业务的限制规则

业务规则

  1. 菜品名称必须是唯一的
  2. 菜品必须属于某个分类下, 不能单独存在
  3. 新增菜品时可以根据情况选择菜品的口味
  4. 每个菜品必须对应一张图片

接口设计

根据类型查询分类接口

文件上传接口

新增菜品接口

数据表设计

设计dish菜品表 和 dish_flavor口味表

分类查询接口

在分类管理模块中已完成

文件上传接口

定义文件上传的Controler

复制代码
/**
 * 通用接口
 */
@RestController
@RequestMapping("/admin/common")
@Api(tags = "通用接口")
@Slf4j
public class CommonController {

    /**
     * 文件上传
     *
     * @param file
     * @return
     */
    @PostMapping("/upload")
    @ApiOperation("文件上传")
    public Result<String> update(MultipartFile file) {
        log.info("文件上传:{}", file);
        return null;
    }
}

配置阿里云OSS参数: 通常把application.yml作为主配置文件, 根据运行环境引用其他配置文件的值

复制代码
// 配置属性类
@Component
@ConfigurationProperties(prefix = "sky.alioss")
@Data
public class AliOssProperties {

    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String bucketName;

}
  1. 使用 @ConfigurationProperties(prefix = "sky.alioss")注解 定义配置属性类

  2. 配置属性类的作用就是读取配置文件中的属性, 把属性的值封装到对象中

  3. 配置文件中用横线分隔, 配置属性类使用驼峰命名法

  4. springboot框架可以自动进行驼峰命名映射

    spring:
    profiles:
    active: dev

    sky:
    alioss:
    endpoint: {sky.alioss.endpoint} access-key-id: {sky.alioss.access-key-id}
    access-key-secret: {sky.alioss.access-key-secret} bucket-name: {sky.alioss.bucket-name}

  5. 在主配置文件中配置OSS连接信息

  6. 程序运行时动态引用环境配置文件中的值

  7. active: dev 该属性指定引用开发环境的信息

  8. 使用 active 属性可以实现代码环境的快捷切换

    sky:
    alioss:
    endpoint: oss-cn-beijing.aliyuncs.com
    access-key-id: ....
    access-key-secret: ....
    bucket-name: sky-itcast-cangqiongwaimai

  9. 在开发环境配置文件中配置具体的oss连接参数

准备工具类: 已经提供了用于文件上传的工具类

复制代码
@Data
@AllArgsConstructor
@Slf4j
public class AliOssUtil {

    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String bucketName;

    /**
     * 文件上传
     *
     * @param bytes
     * @param objectName
     * @return
     */
    public String upload(byte[] bytes, String objectName) {

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        try {
            // 创建PutObject请求。
            ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(bytes));
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }

        //文件访问路径规则 https://BucketName.Endpoint/ObjectName
        StringBuilder stringBuilder = new StringBuilder("https://");
        stringBuilder
                .append(bucketName)
                .append(".")
                .append(endpoint)
                .append("/")
                .append(objectName);

        log.info("文件上传到:{}", stringBuilder.toString());

        return stringBuilder.toString();
    }
}

创建配置类, 用于创建AliOssUtil对象

复制代码
@Configuration // 声明配置类
@Slf4j
public class OssConfiguration {

    @Bean // 程序启动时创建阿里云OSS对象并交给IOC容器管理
    @ConditionalOnMissingBean //条件装配注解, 指定该对象为单例模式, 只创建一个
    public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties) {
        // 通过参数注入的形式注入aliOssProperties对象
        // 通过该对象可以拿到属性配置类封装好的的属性
        log.info("开始创建阿里云文件上传工具类对象: {}", aliOssProperties);
        return new AliOssUtil(aliOssProperties.getEndpoint(),
                aliOssProperties.getAccessKeyId(),
                aliOssProperties.getAccessKeySecret(),
                aliOssProperties.getBucketName());
    }

}

使用aliOssUtil对象, 完成文件上传功能, 为了防止文件重名覆盖, 需要构建新的文件名

复制代码
/**
 * 通用接口
 */
@RestController
@RequestMapping("/admin/common")
@Api(tags = "通用接口")
@Slf4j
public class CommonController {

    @Autowired
    private AliOssUtil aliOssUtil;

    /**
     * 文件上传
     *
     * @param file
     * @return
     */
    @PostMapping("/upload")
    @ApiOperation("文件上传")
    public Result<String> update(MultipartFile file) {
        log.info("文件上传:{}", file);

        try {
            // 原始文件名
            String originalFilename = file.getOriginalFilename();
            // 截取原始文件名的后缀(xxx.png)
            String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
            // 构建新文件名
            String objectName = UUID.randomUUID().toString() + extension;
            // 文件上传
            String filePath = aliOssUtil.upload(file.getBytes(), objectName);
            return Result.success(filePath);
        } catch (IOException e) {
            log.info("文件上传失败:{}", e);
        }

        return Result.error(MessageConstant.UPLOAD_FAILED);
    }
}

新增菜品接口

准备DishDTO用来封装前端传递的菜品数据

复制代码
@Data
public class DishDTO implements Serializable {

    private Long id;
    //菜品名称
    private String name;
    //菜品分类id
    private Long categoryId;
    //菜品价格
    private BigDecimal price;
    //图片
    private String image;
    //描述信息
    private String description;
    //0 停售 1 起售
    private Integer status;
    //口味
    private List<DishFlavor> flavors = new ArrayList<>();

}

准备DishFlavor实体类, 用来封装菜品的口味数据

复制代码
/**
 * 菜品口味
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DishFlavor implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;
    //菜品id
    private Long dishId;

    //口味名称
    private String name;

    //口味数据list
    private String value;

}

新建DishController

复制代码
/**
 * 菜品管理
 */
@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品相关接口")
@Slf4j
public class DishController {
    @Autowired
    private DishService dishService;

    /**
     * 新增菜品
     *
     * @param dishDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增菜品")
    public Result save(@RequestBody DishDTO dishDTO) {
        log.info("新增菜品: {}", dishDTO);
        dishService.saveWithFlavor(dishDTO);
        return Result.success();
    }
}

新建 DishService接口 以及 DishServiceImpl实现类

复制代码
public interface DishService {
    /**
     * 新增菜品和对应的口味
     * @param dishDTO
     */
    public void saveWithFlavor(DishDTO dishDTO);
}

@Service
@Slf4j
public class DishServiceImpl implements DishService {

    @Autowired
    private DishMapper dishMapper;
    @Autowired
    private DishFlavorMapper dishFlavorMapper;

    /**
     * 新增菜品和对应的口味
     *
     * @param dishDTO
     */
     @Transactional
    public void saveWithFlavor(DishDTO dishDTO) {

        Dish dish = new Dish();
        BeanUtils.copyProperties(dishDTO, dish);
        // 向菜品表插入1条数据
        dishMapper.insert(dish);
        // 向口味表插入n条数据
        Long dishId = dish.getId(); //拿到菜品id, SQL中已开启
        List<DishFlavor> flavors = dishDTO.getFlavors(); //拿到口味数据
        if (flavors != null && flavors.size() > 0) {
            flavors.forEach(dishFlavor -> {
                // 设置口味关联的菜品id
                dishFlavor.setDishId(dishId);
            });
            dishFlavorMapper.insertBatch(flavors);
        }
    }
}

@SpringBootApplication
@EnableTransactionManagement //开启注解方式的事务管理
@Slf4j
public class SkyApplication {
    public static void main(String[] args) {
        SpringApplication.run(SkyApplication.class, args);
        log.info("server started");
    }
}
  1. 一个方法中操作多个表, 使用@Transactional注解开启事务管理
  2. @EnableTransactionManagement注解不是严格必须的, 但是显示的声明能够确保Spring容器能够识别并管理@Transactional注解标记的方法
  3. 使用 BeanUtils.copyProperties(数据源对象, 目标对象) 方法进行对象属性拷贝, 可以轻松的进行对象属性的赋值, 前提是两个对象的属性名完全一致

新建DishMapper操作菜品表

复制代码
@Mapper
public interface DishMapper {
    /**
     * 插入菜品
     *
     * @param dish
     */
    @AutoFill(value = OperationType.INSERT) // 公共字段填充
    void insert(Dish dish);
 
}

<?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.DishMapper">

    // 新增菜品
    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        insert into dish (name, category_id, price, image, description, create_time, update_time, create_user,
                          update_user, status)
        values (#{name}, #{categoryId}, #{price}, #{image}, #{description}, #{createTime}, #{updateTime}, #{createUser},
                #{updateUser}, #{status})
    </insert>

</mapper>
  1. useGeneratedKeys="true" 开启sql执行完毕返回数据
  2. keyProperty="id" 指定返回id字段

新建DishFlavorMapper操作菜品口味表

复制代码
@Mapper
public interface DishFlavorMapper {

    /**
     * 批量插入口味数据
     * @param flavors
     */
    void insertBatch( List<DishFlavor> flavors);
}

<?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.DishFlavorMapper">

    // 插入口味数据 
    <insert id="insertBatch">
        insert into dish_flavor (dish_id,name,value) VALUES
        <foreach collection="flavors" item="df" separator=",">
            (#{df.dishId},#{df.name},#{df.value})
        </foreach>
    </insert>

</mapper>

联调测试

录入的数据项比较多, 直接采用前后端联调进行测试

菜品分页查询

分析和设计

查看产品原型, 分析业务规则

  1. 根据页码展示菜品信息
  2. 每页展示10条数据
  3. 分页查询时可以根据需要输入菜品名称, 菜品分类, 菜品状态进行查询

接口设计, 分类名称需要根据菜品id查询分类表得到

代码开发

根据前端请求参数定义对应的DTO

根据接口返回的结果定义对应的VO

Controller

复制代码
/**
 * 菜品管理
 */
@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品相关接口")
@Slf4j
public class DishController {

    @Autowired
    private DishService dishService;

    /**
     * 菜品分页查询
     *
     * @param dishPageQueryDTO
     * @return
     */
    @GetMapping("/page")
    @ApiOperation("菜品分页查询接口")
    public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO) {
        log.info("菜品分页查询:{}", dishPageQueryDTO);
        PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);
        return Result.success(pageResult);
    }
}

Service

复制代码
public interface DishService {

    /**
     * 菜品分页查询
     * @param dishPageQueryDTO
     * @return
     */
    PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO);
}

@Service
@Slf4j
public class DishServiceImpl implements DishService {

    @Autowired
    private DishMapper dishMapper;

    /**
     * 菜品分页查询
     *
     * @param dishPageQueryDTO
     * @return
     */
    public PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {
        // 开启分页查询
        PageHelper.startPage(dishPageQueryDTO.getPage(), dishPageQueryDTO.getPageSize());
        // 查询业务
        Page<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO);
        // 返回数据
        return new PageResult(page.getTotal(), page.getResult());
    }
}

Mapper

复制代码
@Mapper
public interface DishMapper {
    
    /**
     * 菜品分页查询
     *
     * @param dishPageQueryDTO
     * @return
     */
    Page<DishVO> pageQuery(DishPageQueryDTO dishPageQueryDTO);

}

<?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.DishMapper">

    <select id="pageQuery" resultType="com.sky.vo.DishVO">
        select d.*, c.name as categoryName from dish d left outer join category c on d.category_id = c.id
        <where>
            <if test="name != null">
                and d.name like concat('%',#{name},'%')
            </if>
            <if test="categoryId != null">
                and d.category_id = #{categoryId}
            </if>
            <if test="status != null">
                and d.status = #{status}
            </if>
        </where>
        order by d.create_time desc
    </select>
</mapper>
  1. 对于比较复杂的查询语句, 可以在SQL图形化工具中, 编写语句并测试, 提高测试效率

功能测试

接口测试

前后端联调

删除菜品

分析和设计

需求分析: 查看产品原型, 确定业务规则

  1. 可以一次删除一个菜品, 也可以批量删除菜品
  2. 起售中的菜品不能删除
  3. 被套餐关联的菜品不能删除
  4. 删除菜品后, 关联的口味数据也需要删除

接口设计

数据表设计: 删除菜品涉及到三张表的操作, 菜品表, 菜品口味表, 菜品和口味中间表

代码开发

Controller

复制代码
/**
 * 菜品管理
 */
@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品相关接口")
@Slf4j
public class DishController {

    @Autowired
    private DishService dishService;

    /**
     * 菜品批量删除
     *
     * @param ids
     * @return
     */
    @DeleteMapping
    @ApiOperation("菜品批量删除")
    public Result delete(@RequestParam List<Long> ids) {
        // @RequestParam注解
        // 可以自动把query参数中的参数封装到List<long> ids容器中
        // "1,2,3,4" --> [1,2,3,4]
        log.info("菜品批量删除:{}", ids);
        dishService.deleteBath(ids);
        //删除缓存数据
        cleanCache("dish_*");

        return Result.success();
    }
}

Service

复制代码
public interface DishService {

    /**
     * 批量删除菜品
     * @param ids
     */
    void deleteBath(List<Long> ids);
}

@Service
@Slf4j
public class DishServiceImpl implements DishService {

    @Autowired
    private DishMapper dishMapper;
    @Autowired
    private DishFlavorMapper dishFlavorMapper;
    @Autowired
    private SetmealDishMapper setmealDishMapper;

    /**
     * 批量删除菜品
     *
     * @param ids
     */
    @Transactional // 开启事务管理
    public void deleteBath(List<Long> ids) {
        log.info("根据菜品id删除口味数据:{}", ids);
        // 判断当前菜品是否能够删除---是否存在起售中的菜品
        for (Long id : ids) {
            Dish dish = dishMapper.getById(id);
            if (dish.getStatus() == StatusConstant.ENABLE) {
                // 当前菜品处于起售中, 不能删除
                throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);
            }
        }
        // 判断当前菜品是否能够删除---是否被套餐关联了
        List<Long> setmealIds = setmealDishMapper.getSetmealIdsByDishIds(ids);
        if (setmealIds != null && setmealIds.size() > 0) {
            // 当前菜品被套餐关联, 不能删除
            throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);
        }
        // 删除菜品表中的数据(优化前)
        for (Long id : ids) {
            // 删除菜品数据
            dishMapper.deleteById(id);
            // 删除菜品关联的口味数据
            dishFlavorMapper.deleteByDishId(id);
        }

    }
}

Mapper

复制代码
@Mapper
public interface DishMapper {

    /**
     * 根据id查询菜品
     *
     * @param id
     * @return
     */
    @Select("select * from dish where id = #{id}")
    Dish getById(Long id);

    /**
     * 根据id删除菜品数据
     *
     * @param id
     */
    @Delete("delete from dish where id = #{id}")
    void deleteById(Long id);
}

@Mapper
public interface SetmealDishMapper {

    /**
     * 根据菜品id查询套餐id
     * @param dishIds
     * @return
     */
    List<Long> getSetmealIdsByDishIds(List<Long> dishIds);

}

<?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>

@Mapper
public interface DishFlavorMapper {

    /**
     * 根据id删除对应的口味数据
     * @param dishId
     */
    @Delete("delete from dish_flavor where dish_id = #{dishId}")
    void deleteByDishId(Long dishId);
}

代码优化

优化前: sql语句的数量不固定, 删除多个菜品就调用多次sql

优化后: sql语句的数量是固定的, 删除多个菜品也是调用两条sql

复制代码
@Service
@Slf4j
public class DishServiceImpl implements DishService {

    @Autowired
    private DishMapper dishMapper;
    @Autowired
    private DishFlavorMapper dishFlavorMapper;

    
    /**
     * 批量删除菜品
     *
     * @param ids
     */
    @Transactional
    public void deleteBath(List<Long> ids) {
        ... ...
        
        // 删除菜品表中的数据(优化前)
        // for (Long id : ids) {
        // 删除菜品数据
        // dishMapper.deleteById(id);
        // 删除菜品关联的口味数据
        // dishFlavorMapper.deleteByDishId(id);
        //         }

        // 优化后
        // 批量删除菜品数据
        dishMapper.deleteByIds(ids);
        // 批量删除菜品关联的口味数据
        log.info("根据菜品id删除口味数据:{}", ids);
        dishFlavorMapper.deleteByDishIds(ids);
    }
}

@Mapper
public interface DishMapper {
    /**
     * 根据ids批量删除菜品数据
     * @param ids
     */
    void deleteByIds(List<Long> ids);
}

<?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.DishMapper">

    <delete id="deleteByIds">
        delete from dish where id in
        <foreach collection="ids" close=")" open="(" separator="," item="id">
            #{id}
        </foreach>
    </delete>

</mapper>

@Mapper
public interface DishFlavorMapper {

    /**
     * 根据菜品ids批量删除口味数据
     * @param dishIds
     */
    void deleteByDishIds(List<Long> dishIds);

}

<?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.DishFlavorMapper">

    <delete id="deleteByDishIds">
        delete from dish_flavor where dish_id in
        <foreach collection="dishIds" item="dishId" separator="," open="(" close=")">
            #{dishId}
        </foreach>
    </delete>

</mapper>

功能测试

修改菜品

分析和设计

查看原型: 完成菜品信息的回显和更新

接口设计

根据id查询菜品

Controller

复制代码
/**
 * 菜品管理
 */
@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品相关接口")
@Slf4j
public class DishController {

    @Autowired
    private DishService dishService;

    /**
     * 根据id查询菜品
     *
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    @ApiOperation("根据id查询菜品")
    public Result<DishVO> getById(@PathVariable long id) {
        log.info("根据id查询菜品:{}", id);
        DishVO dishVO = dishService.getByIdWithFlavor(id);
        return Result.success(dishVO);
    }

}

Service

复制代码
public interface DishService {

    /**
     * 根据id查询菜品
     * @param id
     * @return
     */
    DishVO getByIdWithFlavor(long id);
}


@Service
@Slf4j
public class DishServiceImpl implements DishService {

    @Autowired
    private DishMapper dishMapper;
    @Autowired
    private DishFlavorMapper dishFlavorMapper;

    /**
     * 根据id查询菜品和对应的口味数据
     *
     * @param id
     * @return
     */
    public DishVO getByIdWithFlavor(long id) {
        // 根据id查询菜品数据
        Dish dish = dishMapper.getById(id);
        // 根据菜品id查询口味数据
        List<DishFlavor> dishFlavors = dishFlavorMapper.getByDishId(id);
        // 把查询结果封装到VO
        DishVO dishVO = new DishVO();
        BeanUtils.copyProperties(dish, dishVO);
        dishVO.setFlavors(dishFlavors);

        return dishVO;
    }
}

Mapper

复制代码
@Mapper
public interface DishMapper {

    /**
     * 根据id查询菜品
     *
     * @param id
     * @return
     */
    @Select("select * from dish where id = #{id}")
    Dish getById(Long id);
}

@Mapper
public interface DishFlavorMapper {

    /**
     * 根基菜品id查询对应的口味数据
     * @param dishId
     * @return
     */
    @Select("select * from dish_flavor where dish_id = #{dishId}")
    List<DishFlavor> getByDishId(long dishId);
}

根据id修改菜品

Controller

复制代码
/**
 * 菜品管理
 */
@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品相关接口")
@Slf4j
public class DishController {

    @Autowired
    private DishService dishService;

    /**
     * 修改菜品
     *
     * @param dishDTO
     * @return
     */
    @PutMapping
    @ApiOperation("修改菜品")
    public Result update(@RequestBody DishDTO dishDTO) {
        log.info("修改菜品: {}", dishDTO);
        dishService.updateWithFlavor(dishDTO);
        //删除缓存数据
        cleanCache("dish_*");

        return Result.success();
    }
}

Service

复制代码
public interface DishService {
    /**
     * 修改菜品和对应的口味
     * @param dishDTO
     */
    void updateWithFlavor(DishDTO dishDTO);
}


@Service
@Slf4j
public class DishServiceImpl implements DishService {

    @Autowired
    private DishMapper dishMapper;
    @Autowired
    private DishFlavorMapper dishFlavorMapper;

    /**
     * 修改菜品和对应的口味
     *
     * @param dishDTO
     */
    public void updateWithFlavor(DishDTO dishDTO) {
        Dish dish = new Dish();
        BeanUtils.copyProperties(dishDTO, dish);

        // 修改菜品基本信息
        dishMapper.update(dish);

        // 删除原有的口味数据
        dishFlavorMapper.deleteByDishId(dishDTO.getId());

        // 重新插入口味数据
        List<DishFlavor> flavors = dishDTO.getFlavors();
        if (flavors != null && flavors.size() > 0) {
            for (DishFlavor flavor : flavors) {
                flavor.setDishId(dishDTO.getId());
            }
            // 向口味表插入n条数据
            dishFlavorMapper.insertBatch(flavors);
        }
    }
}

Mapper

复制代码
@Mapper
public interface DishMapper {
    
    /**
     * 根据id修改菜品数据
     * @param dish
     */
    @AutoFill(value = OperationType.UPDATE)
    void update(Dish dish);
}

<?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.DishMapper">

    <update id="update">
        update dish
        <set>
            <if test="name != null">
                name = #{name},
            </if>
            <if test="categoryId != null">
                category_id = #{categoryId},
            </if>
            <if test="price != null">
                price = #{price},
            </if>
            <if test="image != null">
                image = #{image},
            </if>
            <if test="description != null">
                description = #{description},
            </if>
            <if test="status != null">
                status = #{status},
            </if>
            <if test="updateTime != null">
                update_time = #{updateTime},
            </if>
            <if test="updateUser != null">
                update_user = #{updateUser},
            </if>
        </set>
        where id = #{id}
    </update>

</mapper>

@Mapper
public interface DishFlavorMapper {

    /**
     * 批量插入口味数据
     * @param flavors
     */
    void insertBatch( List<DishFlavor> flavors);

    /**
     * 根据id删除对应的口味数据
     * @param dishId
     */
    @Delete("delete from dish_flavor where dish_id = #{dishId}")
    void deleteByDishId(Long dishId);
}

<?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.DishFlavorMapper">

    <insert id="insertBatch">
        insert into dish_flavor (dish_id,name,value) VALUES
        <foreach collection="flavors" item="df" separator=",">
            (#{df.dishId},#{df.name},#{df.value})
        </foreach>
    </insert>
                                       
</mapper>

功能测试

相关推荐
安然~~~2 分钟前
单例模式的理解
java·单例模式
我会冲击波10 分钟前
Easy Naming for IDEA:从命名到注释,您的编码效率助推器
java·intellij idea
池以遇11 分钟前
云原生高级---TOMCAT
java·tomcat
IT毕设实战小研39 分钟前
Java毕业设计选题推荐 |基于SpringBoot的水产养殖管理系统 智能水产养殖监测系统 水产养殖小程序
java·开发语言·vue.js·spring boot·毕业设计·课程设计
小小深44 分钟前
Spring进阶(八股篇)
java·spring boot·spring
京东云开发者1 小时前
虚引用GC耗时分析优化(由 1.2 降低至 0.1 秒)
java
Java中文社群1 小时前
求职必备!常用拖Offer话术总结
java·后端·面试
Techie峰2 小时前
Redis Key过期事件监听Java实现
java·数据库·redis
JosieBook2 小时前
【SpringBoot】12 核心功能-配置文件详解:Properties与YAML配置文件
java·spring boot·后端
都叫我大帅哥2 小时前
Spring Modulith 完整实战指南:从零构建模块化订单系统
java·spring boot·spring