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

posted @ 2023-01-14 09:13  yub4by  阅读(18)  评论(0编辑  收藏  举报