@Transactional和try catch【reggie_take_out】
https://blog.csdn.net/manonggeerdan/article/details/124959685
https://gitee.com/yub4by/my-reggie-take-out
package com.irun2u.reggie.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.irun2u.reggie.common.CustomException;
import com.irun2u.reggie.dto.DishDto;
import com.irun2u.reggie.entity.Dish;
import com.irun2u.reggie.entity.DishFlavor;
import com.irun2u.reggie.mapper.DishMapper;
import com.irun2u.reggie.service.DishFlavorService;
import com.irun2u.reggie.service.DishService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Author: haifei
* @Date: 2023/1/7 13:51
*/
@Service
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {
@Autowired
private DishFlavorService dishFlavorService;
/**
* 新增菜品
* 同时插入菜品对应的口味数据
* 需要操作两张表:dish、dish_flavor
* 因为涉及两张表,这里还要加入事务进行控制,防止多表操作崩溃
*
* @Transactional和try catch的使用方法
* https://blog.csdn.net/manonggeerdan/article/details/124959685
* @param dishDto
*/
@Transactional //开启事务(启动类上+@EnableTransactionManagement)
@Override
public void saveWithFlavor(DishDto dishDto) {
try {
//保存到dish表
this.save(dishDto);
//保存到dish表就有了dishId
Long dishId = dishDto.getId();
//模拟出错测试事务回滚 //友好交互前台应该有所提示:throw new CustomException("新增菜品事务内异常")处理
// int i = 1/0;
//给每条falvor记录设置dishID(falvor从前端只封装了name和value)
List<DishFlavor> flavors = dishDto.getFlavors();
/*for (DishFlavor flavor : flavors) {
flavor.setDishId(dishId);
}*/
flavors = flavors.stream().map((item) -> {
item.setDishId(dishId);
return item;
}).collect(Collectors.toList());
//保存到dish_flavor表
dishFlavorService.saveBatch(flavors);
} catch (Exception e) {
throw new CustomException("新增菜品事务内异常");
}
}
}
package com.irun2u.reggie.common;
/**
* @Author: haifei
* @Date: 2023/1/12 17:22
*
* 自定义业务异常类
*/
public class CustomException extends RuntimeException{
public CustomException(String msg){
super(msg);
}
}
package com.irun2u.reggie.common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.sql.SQLIntegrityConstraintViolationException;
/**
* @Author: haifei
* @Date: 2023/1/5 16:50
*
* 全局异常处理
* 基于代理实现,代理controller,通过aop拦截方法
* 当出现异常时,统一来此处理
*/
@ControllerAdvice(annotations = {RestController.class, Controller.class}) //拦截加了@RestController或@Controller的类
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
/**
* 异常处理
* 插入数据字段不可重复限制
* @param ex
* @return
*/
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
log.error(ex.getMessage());
//Duplicate entry 'lisi' for key 'idx_username'
if (ex.getMessage().contains("Duplicate entry")){
String[] split = ex.getMessage().split(" ");
String msg = split[2] + "已存在";
return R.error(msg);
}
return R.error("未知错误");
}
/**
* 处理自定义异常
* 与CustomException配合
* @param ex
* @return
*/
@ExceptionHandler(CustomException.class)
public R<String> exceptionHandler(CustomException ex){
log.error(ex.getMessage());
return R.error(ex.getMessage());
}
}
package com.irun2u.reggie.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.irun2u.reggie.entity.Dish;
import org.apache.ibatis.annotations.Mapper;
/**
* @Author: haifei
* @Date: 2023/1/7 13:50
*/
@Mapper
public interface DishMapper extends BaseMapper<Dish> {
}
package com.irun2u.reggie.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.irun2u.reggie.dto.DishDto;
import com.irun2u.reggie.entity.Dish;
/**
* @Author: haifei
* @Date: 2023/1/7 13:50
*/
public interface DishService extends IService<Dish> {
void saveWithFlavor(DishDto dishDto);
}
package com.irun2u.reggie.controller;
import com.irun2u.reggie.common.R;
import com.irun2u.reggie.dto.DishDto;
import com.irun2u.reggie.service.DishFlavorService;
import com.irun2u.reggie.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.*;
/**
* @Author: haifei
* @Date: 2023/1/7 13:49
*/
@Slf4j
@RestController
@RequestMapping("/dish")
@Api(tags = "菜品接口")
public class DishController {
@Autowired
private DishService dishService;
@Autowired
private DishFlavorService dishFlavorService;
/**
* 新增菜品功能前后端交互过程:
* 1. add.html页面发送ajax请求,从服务端获取菜品分类数据并展示到下拉框中
* CategoryController/list()
* 2. 页面发送请求进行图片上传,请求服务端将图片保存到服务器
* CommonController/upload()
* 3. 页面发送请求进行图片下载,将上传的图片进行回显
* CommonController/download()
* 4、点击保存按钮,发送ajax请求,将菜品相关数据以json形式提交到服务端
* DishController/save()
*
* 此处只用Dish接收不行了,因为前端add.html中传过来的json中不仅包含了Dish还包含了Flavor
* 所以需要一个新类DishDTO(数据传输对象,用于展示层与服务层之间交互)来封装页面提交的数据
*/
@ApiOperation("新增菜品")
@PostMapping
public R<String> save(@RequestBody DishDto dishDto){ //@RequestBody接收前端传来的json数据
log.info(dishDto.toString());
dishService.saveWithFlavor(dishDto);
return R.success("新增菜品成功");
}
}
package com.irun2u.reggie.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* value–类名
* description–描述
*/
@ApiModel(value = "Dish", description = "菜品实体")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dish implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("菜品id")
private Long id;
/**
* value–字段说明
* name–重写属性名字
* dataType–重写属性类型
* required–是否必填
* example–举例说明
* hidden–隐藏
*/
@ApiModelProperty(value = "菜品名称", required = true, example = "鲁菜")
private String name;
@ApiModelProperty(value = "菜品分类id", required = false)
private Long categoryId;
@ApiModelProperty(value = "菜品价格", required = false)
private BigDecimal price;
@ApiModelProperty(value = "商品码", required = false)
private String code;
@ApiModelProperty(value = "菜品图片", required = false)
private String image;
@ApiModelProperty(value = "描述信息", required = false)
private String description;
@ApiModelProperty(value = "菜品状态(0 停售 1 起售)", required = false)
private Integer status;
@ApiModelProperty(value = "顺序", required = false)
private Integer sort;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableField(fill = FieldFill.INSERT)
private Long createUser;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;
//是否删除
// private Integer isDeleted;
}
package com.irun2u.reggie.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
菜品口味
*/
@Data
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;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableField(fill = FieldFill.INSERT)
private Long createUser;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;
//是否删除
private Integer isDeleted;
}
package com.irun2u.reggie.dto;
import com.irun2u.reggie.entity.Dish;
import com.irun2u.reggie.entity.DishFlavor;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class DishDto extends Dish {
//继承了Dish:DishDto中拥有Dish的所有属性
//相较Dish,DishDto又扩展了以下属性
private List<DishFlavor> flavors = new ArrayList<>();
//以下这俩暂时用不到
private String categoryName;
private Integer copies;
}