Springboot + Mybatis-plus事务管理

2022/7/8 6:21:31

本文主要是介绍Springboot + Mybatis-plus事务管理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录

一步到位springboot目录
gitee:https://gitee.com/chaitou/leilema.git

前言

上节完善了productInfo的CRUD功能,这节我们完善Order订单功能,比起productInfo,订单的功能需要

  1. 事务操作,保证多个数据库操作的原子性
  2. vo变复杂了,因此需要dto做转换

事务

事务就是为了保证多次数据库操作的原子性。举个简单的例子
买商品第一步要扣钱,第二步要扣库存。如果没有事务,一旦第一步与第二步之间出现了异常,那么钱是扣了,库存却没变,这显然不符合业务场景。要么都成功要嘛都失败

  1. springboot中使用事务就很简单了,首先引入依赖spring-tx,但是mybatis-plus的依赖中已经引入,因此又少了一步

  2. 开启事务,在Springboot的启动类,或者某个@Configuration的类上加上@EnableTransactionManagement开启事务。因为这是数据库相关,所以我加在了mybatis-plus的配置类上

@EnableTransactionManagement
@MapperScan("com.bugpool.leilema.*.mapper")
@Configuration
public class MybatisPlusConfiguration {
    /**
     * 分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
        // paginationInterceptor.setOverflow(false);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        // paginationInterceptor.setLimit(500);
        // 开启 count 的 join 优化,只针对部分 left join
        paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
        return paginationInterceptor;
    }
}
 
  1. 只要在需要使用事务的方法上加上@Transactional就可以开启事务了,还是很简单的

注意点

  1. @Transactional默认回滚的是RuntimeException也就是说如果抛出的不是RuntimeException的异常,数据库是不会回滚的。但是所幸的是,在spring框架下,所有的异常都被org.springframework重写为RuntimeException,因此不需要太担心
	@Transactional
	public void buy() throws Exception {
        1. 扣钱
        throw new 非RuntimeException异常("发生异常");
        2. 扣库存
    }

	// 有一种处理方法是指定回滚的异常
	@Transactional(rollbackFor = Exception.class)
		public void buy() throws Exception {
        1. 扣钱
        throw new 非RuntimeException异常("发生异常");
        2. 扣库存
    }
 
  1. 还有如果在异常发生时,程序员自己手动捕获处理了,异常也不会回滚
	@Transactional
	public void buy() throws Exception {
        try{
        1. 扣钱
        } catch (Exception e) {
        	catch了自己处理,也就是异常被自己吞了,外层并不知道,此时也不会回滚
        }
        3. 扣库存
    }
 

正因为如此,如果你回去看一下我们自己写的APIException,它就继承了RuntimeException,因此,如果你使用的是我们自己定义的异常,你只管直接抛异常就行,回滚、日志、统一异常都已经处理好了

	// 有一种处理方法是指定回滚的异常
	@Transactional(rollbackFor = Exception.class)
		public void buy() throws Exception {
        1. 扣钱
        // 如果此时有一些业务异常
        throw new APIException("钱太少拉!不够扣!");
        2. 扣库存
    }
 
@Getter
public class APIException extends RuntimeException {
    private int code;
    private String msg;

    // 手动设置异常
    public APIException(StatusCode statusCode, String message) {
        // message用于用户设置抛出错误详情,例如:当前价格-5,小于0
        super(message);
        // 状态码
        this.code = statusCode.getCode();
        // 状态码配套的msg
        this.msg = statusCode.getMsg();
    }

    // 默认异常使用APP_ERROR状态码
    public APIException(String message) {
        super(message);
        this.code = AppCode.APP_ERROR.getCode();
        this.msg = AppCode.APP_ERROR.getMsg();
    }

}
 

完善订单功能

vo

@Data
public class OrderAddVo {
    /**
     * 买家名字
     */
    @NotBlank(message = "买家姓名不能为空")
    private String buyerName;

    /**
     * 买家电话
     */
    @NotBlank(message = "买家电话不能为空")
    private String buyerPhone;


    @NotBlank(message = "订单详情不能为空")
    private String orderDetails;
}
 
@Data
public class OrderDetailQueryVo {
    private Integer detailId;

    /**
     * 订单主键
     */
    private Integer orderId;

    /**
     * 商品主键
     */
    private Integer productId;

    /**
     * 商品名称
     */
    private String productName;

    /**
     * 当前价格
     */
    private BigDecimal productPrice;

    /**
     * 数量
     */
    private Integer productNumber;
}
 
@Data
public class OrderQueryVo {

    private String buyerName;

    private String buyerPhone;

    private BigDecimal orderAmount;

    private Integer status;

    private List<OrderDetailQueryVo> orderDetails;
}
 

dto,由于vo接收过来的请求中附带了orderDetailjson串,因此我们需要转换一手,用到当下比较流行的fastjson。同时我们写了一个静态类,专门用于转换订单的vo和dto

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson-version}</version>
        </dependency>
 
@Data
@Accessors(chain = true)
public class OrderAddDto {

    private String buyerName;

    private String buyerPhone;

    private List<OrderDetail> orderDetails;
}
 
public class ConvertOrderAddVo2OrderAddDto {
    public static OrderAddDto convert(OrderAddVo orderAddVo) {
        OrderAddDto orderAddDto = new OrderAddDto();

        List<OrderDetail> orderDetails = JSON.parseArray(orderAddVo.getOrderDetails(), OrderDetail.class);

        orderAddDto.setBuyerName(orderAddVo.getBuyerName())
                .setBuyerPhone(orderAddVo.getBuyerPhone())
                .setOrderDetails(orderDetails);
        return orderAddDto;
    }
}
 

controller

@RestController
@RequestMapping("/product/product-info")
public class ProductInfoController {

    @Autowired
    ProductInfoService productInfoService;

    @PostMapping("/findById")
    public ProductInfoQueryVo findById(@Valid @NotNull Integer id) {
        return BeanConvertUtils.convertTo(productInfoService.getById(id), ProductInfoQueryVo::new);
    }

    @PostMapping("/page")
    public IPage findPage(Page page, @Validated ProductInfoQueryVo vo) {
        // 将vo => po,进行page查询
        productInfoService.page(page, new QueryWrapper<ProductInfo>(BeanConvertUtils.convertTo(vo, ProductInfo::new)));
        // page.getRecords()此时为po类型,转换为vo
        page.setRecords(BeanConvertUtils.convertListTo(page.getRecords(), ProductInfoQueryVo::new));
        return page;
    }

    @PostMapping("/findByLikeName")
    public List findByLikeName(String productName) {
        // lambda query写法
        // List<ProductInfo> list = productInfoService.lambdaQuery().like(ProductInfo::getProductName, productName).list();

        // 为了演示自定义sql
        List<ProductInfo> list = productInfoService.getByLikeName(productName);
        return list;
    }

    @PostMapping("/add")
    public boolean add(ProductInfoAddVo vo) {
        return productInfoService.save(BeanConvertUtils.convertTo(vo, ProductInfo::new));
    }

    @PostMapping("/deleteById")
    public boolean deleteById(@Valid @NotNull Integer id) {
        return productInfoService.removeById(id);
    }

    @PostMapping("/updateById")
    public boolean updateById(@Valid ProductInfoAddVo vo) {
        return productInfoService.updateById(BeanConvertUtils.convertTo(vo, ProductInfo::new));
    }
}
 

service

public interface OrderMasterService extends IService<OrderMaster> {
    // 新增订单
    public Integer createOrder(OrderAddDto orderAddDto);

    // 取消订单
    public boolean cancel(int orderId);

    // 查询订单
    public OrderQueryVo findOrderById(int id);

}
 
@Service
public class OrderMasterServiceImpl extends ServiceImpl<OrderMasterMapper, OrderMaster> implements OrderMasterService {

    @Autowired
    OrderMasterMapper orderMasterMapper;

    @Autowired
    ProductInfoMapper productInfoMapper;

    @Autowired
    OrderDetailService orderDetailService;

    @Override
    @Transactional
    public Integer createOrder(OrderAddDto orderAddDto) {

        // 订单总金额
        BigDecimal amount = BigDecimal.ZERO;
        // 订单详情PO
        List<OrderDetail> orderDetails = new ArrayList<OrderDetail>();

        // 从ids中查找所有商品信息
        for (OrderDetail orderDetail : orderAddDto.getOrderDetails()) {
            ProductInfo productInfo = productInfoMapper.selectById(orderDetail.getProductId());
            if (null == productInfo || ProductStatusEnums.DOWN.getCode() == productInfo.getProductStatus()) {
                throw new APIException(AppCode.PRODUCT_NOT_EXIST, "上架商品中无法查询到:" + orderDetail.getProductId());
            }
            // 计算订单总金额
            amount = amount.add(productInfo.getProductPrice()
                    .multiply(new BigDecimal(orderDetail.getProductNumber())));

            // 设置订单详情Po
            BeanUtils.copyProperties(productInfo, orderDetail);
            orderDetails.add(orderDetail);
        }

        // 设置主订单,状态是未支付
        OrderMaster orderMaster = BeanConvertUtils.convertTo(orderAddDto, OrderMaster::new);
        orderMaster.setOrderAmount(amount);
        orderMaster.setStatus(OrderStatusEnums.NO_PAY.getCode());
        save(orderMaster);

        // 设置detail的order主键
        orderDetails.stream().forEach(p -> p.setOrderId(orderMaster.getOrderId()));
        orderDetailService.saveBatch(orderDetails);

        return orderMaster.getOrderId();
    }

    @Override
    @Transactional
    public boolean cancel(int orderId) {
        OrderMaster orderMaster = getById(orderId);
        if (null == orderMaster) {
            throw new APIException(AppCode.ORDER_NOT_EXIST, "订单号不存在:" + orderId);
        }

        OrderMaster updateOrderMaster = new OrderMaster();
        updateOrderMaster.setOrderId(orderMaster.getOrderId())
                .setStatus(OrderStatusEnums.CANCEL.getCode());

        return updateById(updateOrderMaster);
    }


    @Override
    public OrderQueryVo findOrderById(int id) {
        OrderMaster orderMaster = orderMasterMapper.selectById(id);
        if (null == orderMaster) {
            throw new APIException(AppCode.ORDER_NOT_EXIST, "订单不存在:" + id);
        }

        List<OrderDetail> details = orderDetailService.lambdaQuery()
                .eq(OrderDetail::getOrderId, orderMaster.getOrderId()).list();
        if (null == details || 0 == details.size()) {
            throw new APIException(AppCode.ORDER_DETAILS_NOT_EXIST, "订单详情不存在:" + id);
        }

        OrderQueryVo orderQueryVo = new OrderQueryVo();
        orderQueryVo.setOrderDetails(BeanConvertUtils.convertListTo(details, OrderDetailQueryVo::new));
        BeanUtils.copyProperties(orderMaster, orderQueryVo);
        return orderQueryVo;
    }
}
  来源:https://blog.csdn.net/chaitoudaren/article/details/105622751

这篇关于Springboot + Mybatis-plus事务管理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程