菜品的增删改查操作

前言:

整体来说:菜品操作相对于我之前写的员工操作需求多了不少。

新增菜品:

1:需求分析和设计:

业务规则: 菜品名称必须是唯一的(在数据库中用unique关键字)

菜品必须属于某个分类下,不能单独存在

新增菜品时可以根据情况选择菜品的口味

每个菜品必须对应一张图片(用阿里云进行存储)

2:接口设计:

根据类型查询分类(已完成)

文件上传

新增菜品

产品原型:

2:具体的代码实现:

                      阿里云文件上传:  

                关于这个文件上传接口的实现可以直接看这篇博客。 阿里云文件上传-CSDN博客

        1:开发文件上传接口:

                1:通过第三方bean注解的方式创建接口:

                        什么是第三方bean:

第三方Bean:

如果要管理的bean对象来自于第三方(不是自定义的),是无法用 @Component 及衍生注解声明bean的,就需要用到 @Bean注解,这就衍生出了第三方Bean。比如你从pom文件中引入的对象,你不能直接在这个对象上面加@Component,让IOC容器管理这个对象。

如何配置第三方bean:

如何配置第三方Bean:
若要管理的第三方bean对象,建议对这些bean进行集中分类配置,可以通过 

@Configuration 注解声明一个配置类。
@Configuration
public class CommonConfiguration {
    //声明第三方bean
    @Bean//将当前方法的返回值对象交给IOC容器,成为bean
    public SAXReader saxReader(){
        return new SAXReader();
    }
}
映入Bean对象的原则:


项目中自定义的,使用@Component及其衍生注解
项目中引入第三方的,使用@Bean注解

 回归到项目功能:

1:创建一个AliOssProperties的属性类:

里面需要用@ConfigurationProperties将配置文件种有关阿里云的配置信息引入进去。

@Component
@ConfigurationProperties(prefix = "sky.aliyunoss")
@Data
public class AliOssProperties {

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

}

2:创建一个AliOssUtil的工具类:

里面包含了具体文件上传的操作。

package com.sky.utils;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayInputStream;

@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();
    }
}

3:创建第三方bean对象的配置项:

里面用了@Bean这个注解将这个配置类交给IOC容器

还用了@ConditionalOnMissingBean注解确保整个IOC容器中只有这一个工具类。

package com.sky.config;


import com.sky.properties.AliOssProperties;
import com.sky.utils.AliOssUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@Slf4j
public class OSSConfiguration {


    @Bean
    @ConditionalOnMissingBean
    public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties){
        return new AliOssUtil(aliOssProperties.getEndpoint()
                ,aliOssProperties.getAccessKeyId()
                ,aliOssProperties.getAccessKeySecret()
                ,aliOssProperties.getBucketName());
    }
}

4:CommonController层接口开发:

我们将AliOssUtil工具类放大IOC容器中了,一开始我们肯定需要注入一下。

        1:获取原始文件名
        2:获取原始文件名的后缀(jpg,png)
        3:用UUID工具类构造新的文件名
        4:返回文件的请求路径。

        UUID:通用唯一标识符 (UUID) 是一种特定形式的标识符,在大多数实际用途中可以安全地认为是唯一的

用UUID的原因是,如果多人同时往这个阿里云里面存储,难免会碰到重名的,UUID工具类就是为了解决这个问题的。

package com.sky.controller.admin;


import com.sky.result.Result;
import com.sky.utils.AliOssUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import reactor.core.publisher.Flux;

import java.io.IOException;
import java.util.UUID;

@Slf4j
@RestController
@RequestMapping("/admin/common")
@Api("通用接口")
public class CommonController {
    @Autowired
    AliOssUtil aliOssUtil;

    @PostMapping("/upload")
    @ApiOperation("文件上传")
    //SpringMvc封装了文件相关的API:MultipartFile
    public Result<String> upload(MultipartFile file){
        try {
            log.info("文件上传:{}",file);
            //1:原始文件名
            String originalFilename = file.getOriginalFilename();
            //2:获取原始文件名后缀(jpg,png)
            String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
            //3:构造新文件名称(UUID)
            String objectname = UUID.randomUUID().toString() + suffix;
            //4:文件的请求路径
            String filepath = aliOssUtil.upload(file.getBytes(),objectname);
            return Result.success(filepath);
        } catch (IOException e) {
            log.error("文件上传失败:{}",e);
        }
        return Result.success();
    }
}

结果测试:

 

在新增页面中进行了回显。 

2:新增菜品具体代码实现:

Controller层:

package com.sky.controller.admin;


import com.sky.dto.DishDTO;
import com.sky.result.Result;
import com.sky.service.DishService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/admin/dish")
@Api("菜品相关接口")
@Slf4j
public class DishController {

    @Autowired
    private DishService dishService;

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

}

这一块的代码就很常规,新增的请求路径方式是POST。

这里接收数据的DTO是:

DishDTO
package com.sky.dto;

import com.sky.entity.DishFlavor;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@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<>();

}
Service层:
@Service
public class DishServiceImpl implements DishService {
    @Autowired
    private DishMapper dishMapper;//菜品mapper
    @Autowired
    private DishFlavorMapper dishFlavorMapper;//菜品口味mapper

    /**
     * 新增菜品操作
     * @param dishDTO
     * @return
     */
    @Override
    @Transactional//开启事务注解
    public void insert(DishDTO dishDTO) {
        //创建一个新的dish对象用来向菜品表中插入数据
        Dish dish = new Dish();
        //数据拷贝
        BeanUtils.copyProperties(dishDTO,dish);

        //向菜品表中插入一条数据
        dishMapper.SavewithFlavor(dish);

        //获取菜品口味的ID
        Long dishId = dish.getId();
        //向口味表中插入n条数据
        List<DishFlavor> flavorlist = dishDTO.getFlavors();

        if (flavorlist!=null&&flavorlist.size()!=0) {
            flavorlist.forEach(dishFlavor ->{
                dishFlavor.setDishId(dishId);
            });
            dishFlavorMapper.insertBatch(flavorlist);
        }
    }
}
Service层做的事情是两件事:

1:向菜品表中插入数据(Dish)

2:向口味表中插入数据(DishFlavor)

分析:

1:DishDTO里面包含了一个数据是:private List<DishFlavor> flavors,这其实是对应了另一个DishFlavor,数据库中的另一张表,所以这里第一步对这个数据进行处理一下,将DishDTO改造成一个Dish对象。这里里面用了之前数据拷贝的操作。

然后就是一个常规的插入操作。

2:DishDTO中的DishFlavor封装的是一个List集合,我们需要批量插入数据库,

在插入数据库的时候设计到了一个动态SQL的知识点:foreach。

foreach:

<foreach>:用在批量操作中,看代码:
<!--    批量删除员工-->
    <!--collection: 遍历的集合
    item: 遍历出来的元素
    separator: 分隔符
    open: 遍历开始前拼接的SQL片段
    close: 遍历结束后拼接的SQL片段-->
    <delete id="deleteByIds">
        delete  from mybatis.emp where id in
        <foreach collection="ids" item="id" separator="," close=")" open="(">
            #{id}
        </foreach>
    </delete>

注意点就是:这种一般批量删除的东西,我们一般用集合来进行封装
所以下面是在test中的代码:
public void testdelete2(){
        List<Integer> list = Arrays.asList(17,18,19);
        empMapper.deleteByIds(list);
    }

 3:仔细看DishFlavor这个实体类,里面有一个dishId,菜品的ID,但是我们在页面原型中是不会知道对应菜品的id的,这个时候就涉及到了一个Mybatis中的属性:useGeneratedKeys="true" keyProperty="id"

通过这两个属性就可以获得插入数据的主键ID了

Mapper层:

DishMapper层及DishMapper注解:
/**
     * 新增菜品操作
     * @param dish
     * @return
     */
    @AutoFill(value = OperationType.INSERT)
    void SavewithFlavor(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="SavewithFlavor" useGeneratedKeys="true" keyProperty="id">
        insert into sky_take_out.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>
DishFlavor层及DishFlavor注解:
/**
     * 批量插入口味数据
     * @param flavorlist
     */
    void insertBatch(List<DishFlavor> flavorlist);
<?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 sky_take_out.dish_flavor(dish_id,name,value)
        values
        <foreach collection="flavorlist" item="flavor" separator=",">
            (#{flavor.dishId},#{flavor.name},#{flavor.value})
        </foreach>
    </insert>
</mapper>

 结果测试:

如果用接口文档的方式测试,输入的数据太多了,直接前后端联调。

查询菜品:

1:查询菜品的接口设计:

查询员工

请求路径:/admin/employee/page

请求方式:GET

数据发送格式:Query

        categoryId:菜品种类ID

        name:员工姓名

        page:页码

        pagesize:每页记录数

        status:状态

数据返回格式:json

2:具体的代码实现:

接收前端传送过来的数据:

接收前端发送回来的数据:DishPageQueryDTO 。

这个需求和之前的不同点是:我们用了另一个类型来给前端传送数据:DishVO

这里提一下VO DTO pojo的用法:

vo是进行页面展示,dto是前后端数据交互的,pojo是对应数据库表字段

 Controll层:
    /**
     * 分页查询菜品
     * @param dishPageQueryDTO
     * @return
     */
    @GetMapping("/page")
    @ApiOperation("分页查询菜品操作")
    public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO){
        log.info("分页查询菜品");
        PageResult pageresult = dishService.page(dishPageQueryDTO);
        return Result.success(pageresult);
    }

这就和常规差不多,用PageResult来封装传回给前端的数据。

Service层:
    /**
     * 分页查询菜品
     * @param dishPageQueryDTO
     * @return
     */
    @Override
    //vo是进行页面展示,dto是前后端数据交互的,pojo是对应数据库表字段
    public PageResult page(DishPageQueryDTO dishPageQueryDTO) {
        PageHelper.startPage(dishPageQueryDTO.getPage(),dishPageQueryDTO.getPageSize());
        Page<DishVO> list= dishMapper.pageQuery(dishPageQueryDTO);
        return new PageResult(list.getTotal(),list.getResult());
    }

 Mapper层和注解: 

    /**
     * 分页查询菜品
     * @param dishPageQueryDTO
     * @return
     */
    Page<DishVO> pageQuery(DishPageQueryDTO dishPageQueryDTO);

 这里和之前不同的原因是:这里涉及到了多表操作:这一个需求用了左外连接的方式。

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

这里还有一个小注意点:

DishVO中的分类名称:categoryName:

而在category表中关于分类名称的这个字段是name,这就会导致在mybatis框架中,这个属性封装不上去,所以我们需要在这个查询的sql语句中给这个categoryName取个别名。

删除菜品:

一:需求分析和设计:

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

接口设计:

 根据以上的业务分析之后,说明我们想删除员工需要对三张表进行操作。

二:具体的代码实现: 

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>

 修改菜品:

一:需求分析和设计:

这个修改操作整个页面原型和新增操作很相似,唯一不同的就是需要做一个页面回显,

所谓的页面回显就是一系列的查询操作。

根据上面的分析,我们想完成这个功能需要进行两个接口的设计

1.根据id查询菜品

2.修改菜品

二:具体的代码实现: 

1:根据id查询菜品:

 Controll层:
    /**
     * 根据id查询菜品
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    @ApiOperation("根据id查询菜品接口")
    public Result<DishVO> selectById(@PathVariable Long id){
        log.info("根据id查询菜品:{}",id);
        DishVO dishVO = dishService.selectById(id);
        return Result.success(dishVO);
    }

这里说几个注意点:

这个接口的请求方式是get

这里的菜品id是在请求路径中传过来的,所以我们用@PathVariable接收。

返回给前端的数据还是老规矩:DishVO。

Service层:
    /**
     * 根据id查询菜品
     * @param id
     * @return
     */
    @Override
    public DishVO selectById(Long id) {
        //1:根据这个id先在dish表中查找dish对象
        Dish dish = dishMapper.selectById(id);
        //2:再根据返回的id进行查询
        List<DishFlavor> dishFlavor = dishFlavorMapper.selectById(id);
        //3:最后封装到DishVO中进行返回
        DishVO dishVO = new DishVO();
        BeanUtils.copyProperties(dish,dishVO);
        dishVO.setFlavors(dishFlavor);
        return dishVO;
    }

 

这里的整体逻辑分为三步:

  1. 根据id在这个dish表中找dish,返回值是一个dish对象
  2. 根据id在dish_flavor表中找口味数据,返回值是List<DishFlavor>对象
  3. 最后将这两个数据进行封装返回。

这里说一个我自己犯的错,就是我把这个传过来的id搞混了一开始,这个传过来的id在dish表中对应的字段就是id,不过在dish_flavor中对应的是dish_id

Mapper层(dishMapper和dishFlavorMapper):
/**
     * 根据菜品id查询菜品
     * @param id
     */
    @Select("SELECT * from sky_take_out.dish where id = #{id}")
    Dish selectById(Long id);
    /**
     * 根据菜品id来查询对应的口味数据
     * @param dishId
     * @return
     */
    @Select("select * from sky_take_out.dish_flavor where dish_id = #{dishId}")
    List<DishFlavor> selectById(Long dishId);

  2:修改菜品:

Controll层:
    /**
     * 修改菜品
     * @param dishDTO
     * @return
     */
    @PutMapping
    @ApiOperation("修改菜品接口")
    public Result update(@RequestBody DishDTO dishDTO){
        log.info("修改菜品:{}",dishDTO);
        dishService.updateWithFlavor(dishDTO);
        return Result.success();
    }
Service层: 
    /**
     * 修改菜品
     * @param dishDTO
     * @return
     */
    @Override
    public void updateWithFlavor(DishDTO dishDTO) {
        //更新菜品表的基本信息
        Dish dish = new Dish();
        BeanUtils.copyProperties(dishDTO,dish);
        dishMapper.update(dish);
        //先全部删除口味数据
        dishFlavorMapper.deleteById(dishDTO.getId());
        //再重新插入新的口味数据
        List<DishFlavor> flavorlist = dishDTO.getFlavors();
        if (flavorlist != null && flavorlist.size() != 0) {
            flavorlist.forEach(dishFlavor -> {
                dishFlavor.setDishId(dishDTO.getId());
            });
            dishFlavorMapper.insertBatch(flavorlist);
        }
    }

Service层整体做三件事:

  1. 更新菜品的基本信息
  2. 先删除全部的口味数据
  3. 再插入新的口味数据

为什么要先删除再插入呢,根据页面原型,想要在原有的基础上直接进行修改比较困难。

Mapper层及其注解(DishMapper和DishFlavorMapper):
    /**
     * 修改菜品
     * @param dish
     * @return
     */
    @AutoFill(value = OperationType.UPDATE)
    void update(Dish dish);

 

<update id="update">
        update sky_take_out.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>
  • 21
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我来给你提供一份Java的GUI类实现菜品信息的展示及增删改查操作的代码,使用JDBC与数据库进行交互。 首先,我们需要创建一个菜品管理的窗口,可以使用Swing库实现: ```java import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class DishManagement extends JFrame implements ActionListener { private JLabel nameLabel, priceLabel; private JTextField nameText, priceText; private JButton addButton, deleteButton, updateButton, searchButton; private JTable table; public DishManagement() { setTitle("菜品管理"); setSize(500, 400); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); nameLabel = new JLabel("菜名"); nameText = new JTextField(10); priceLabel = new JLabel("价格"); priceText = new JTextField(10); addButton = new JButton("添加"); deleteButton = new JButton("删除"); updateButton = new JButton("更新"); searchButton = new JButton("查询"); JPanel panel = new JPanel(new FlowLayout()); panel.add(nameLabel); panel.add(nameText); panel.add(priceLabel); panel.add(priceText); panel.add(addButton); panel.add(deleteButton); panel.add(updateButton); panel.add(searchButton); table = new JTable(); JScrollPane scrollPane = new JScrollPane(table); add(panel, BorderLayout.NORTH); add(scrollPane, BorderLayout.CENTER); addButton.addActionListener(this); deleteButton.addActionListener(this); updateButton.addActionListener(this); searchButton.addActionListener(this); setVisible(true); } @Override public void actionPerformed(ActionEvent e) { // TODO: 实现增删改查操作 } } ``` 接下来,我们需要使用JDBC与数据库进行交互,实现菜品信息的增删改查操作。我们可以使用MySQL数据库,并使用JDBC连接MySQL数据库: ```java import java.sql.*; public class DishDatabase { private Connection connection; private Statement statement; public DishDatabase() { try { Class.forName("com.mysql.cj.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/dish_management?useSSL=false&serverTimezone=UTC"; String user = "root"; String password = "123456"; connection = DriverManager.getConnection(url, user, password); statement = connection.createStatement(); } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); } } public ResultSet executeQuery(String sql) { ResultSet resultSet = null; try { resultSet = statement.executeQuery(sql); } catch (SQLException e) { e.printStackTrace(); } return resultSet; } public int executeUpdate(String sql) { int result = 0; try { result = statement.executeUpdate(sql); } catch (SQLException e) { e.printStackTrace(); } return result; } public void close() { try { statement.close(); connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } ``` 现在,我们可以在`actionPerformed`方法中实现菜品信息的增删改查操作了: ```java @Override public void actionPerformed(ActionEvent e) { DishDatabase database = new DishDatabase(); String name = nameText.getText(); String price = priceText.getText(); String sql; switch (e.getActionCommand()) { case "添加": sql = "insert into dish (name, price) values ('" + name + "', " + price + ")"; database.executeUpdate(sql); break; case "删除": sql = "delete from dish where name = '" + name + "'"; database.executeUpdate(sql); break; case "更新": sql = "update dish set price = " + price + " where name = '" + name + "'"; database.executeUpdate(sql); break; case "查询": sql = "select * from dish where name like '%" + name + "%'"; ResultSet resultSet = database.executeQuery(sql); table.setModel(DbUtils.resultSetToTableModel(resultSet)); break; } database.close(); } ``` 最后,我们需要在MySQL数据库中创建一个`dish`表,用于存储菜品信息: ```sql CREATE TABLE `dish` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `price` double NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ``` 现在,我们就完成了菜品管理模块的代码实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值