[苍穹外卖]-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>

功能测试

相关推荐
ProtonBase25 分钟前
如何从 0 到 1 ,打造全新一代分布式数据架构
java·网络·数据库·数据仓库·分布式·云原生·架构
乐之者v31 分钟前
leetCode43.字符串相乘
java·数据结构·算法
suweijie7684 小时前
SpringCloudAlibaba | Sentinel从基础到进阶
java·大数据·sentinel
公贵买其鹿5 小时前
List深拷贝后,数据还是被串改
java
xlsw_8 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹8 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭9 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫9 小时前
泛型(2)
java
超爱吃士力架9 小时前
邀请逻辑
java·linux·后端
南宫生9 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论