前言:
整体来说:菜品操作相对于我之前写的员工操作需求多了不少。
新增菜品:
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取个别名。
删除菜品:
一:需求分析和设计:
- 业务规则: 可以一次删除一个菜品,也可以批量删除菜品
- 起售中的菜品不能删除
- 被套餐关联的菜品不能删除
- 删除菜品后,关联的口味数据也需要删除
接口设计:
根据以上的业务分析之后,说明我们想删除员工需要对三张表进行操作。
二:具体的代码实现:
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;
}
这里的整体逻辑分为三步:
- 根据id在这个dish表中找dish,返回值是一个dish对象
- 根据id在dish_flavor表中找口味数据,返回值是List<DishFlavor>对象
- 最后将这两个数据进行封装返回。
这里说一个我自己犯的错,就是我把这个传过来的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层整体做三件事:
- 更新菜品的基本信息
- 先删除全部的口味数据
- 再插入新的口味数据
为什么要先删除再插入呢,根据页面原型,想要在原有的基础上直接进行修改比较困难。
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>