前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >小议mybatis plus相比传统mybatis手写SQL的好处

小议mybatis plus相比传统mybatis手写SQL的好处

作者头像
宜信技术学院
发布2020-12-07 09:51:57
1.3K0
发布2020-12-07 09:51:57
举报
文章被收录于专栏:宜信技术实践

一、 场景描述

假设有两张表:一张商品表、一张订单表,具体表的字段如下:

商品表和订单表是一对多的关系,一个商品可以有多个订单。现有如下需求:

1、 发送通知需求:某一特定类别的商品,购买成功后需要发送通知给下单的客户,下单后默认通知状态为未发送,发送成功标记已发送。

2、 查询需求:查询某一用户的订单列表,列出订单信息和订单的商品信息。供给用户购买记录页面呈现使用。

二、 发送通知需求(传统mybatis写法 和 mybatis plus 对比)

传统的mybatis做ORM映射工具并手写sql的时代,常规的写法基本上会是在dao层和servise层按此需求场景实现相应方法。

1、 发送通知需求:传统手写sql的时代dao层基本上会实现两个方法:

(1)“查询某一产品类别下尚未推送通知的订单”方法:用订单表关联产品表查询出某一产品类别下,尚未推送的订单

代码语言:javascript
复制
@Select(" SELECT o.id,o.order_code AS orderCode,o.notify_status AS notifyStatus,o.product_id AS productId 
          FROM product p  " +
        " INNER JOIN order o ON o.product_id=p.product_id " +
        " WHERE p.product_type_id=#{productType} and o.notify_status = 0 order by o.id  ")
List<Order> getNeedNotifyOrderBYProducType(@Param("productType") Integer productType);

(2)修改订单推送状态方法:

代码语言:javascript
复制
@Update("update order set notify_status=#{notifyStatus},update_time=NOW() " +
        "WHERE id = #{id}")
Integer updateStatus(@Param("id") Integer id,@Param("notifyStatus") Integer notifyStatus);

注:将sql写在xml文件里的方式同理 然后发送通知这个需求基本就能实现了,写个定时任务每间隔多久运行一次,先取出待推送的订单,然后调用逐一调推送方法,再修改订单状态。(或者是批量的:先批量推送,然后批量修改订单状态)

但是随着项目持续开展,需求的不断演进,你会发现这两个方法的代码就只适用于这个场景而已,这里称之为“单一场景方法”。而且项目里这种单一场景的方法会越来越多、越来越多。打开dao层的文件一看“一堆方法”。今天订单关联商品写个join的关联查询语句,明天订单关联别的又写一个join的关联查询。今天修改订单推送状态写个update方法,明天修改订单别的状态又写个update方法。项目越来越臃肿,新人上手的可维护难度变得很大。 也许有人会说,我们可以写一个统一的update方法,供service层调用。传进来实体类即可,根据主键ID修改相应数据。如下面:

代码语言:javascript
复制
this.orderDao.updateByPrimaryKey(order);

这样做隐患更大,因为我们上面的select语句是手写的sql ,字段都是手写的,因此如果需求改变了,订单表增加了几个字段,那么这些手写的select语句的sql就得从头到尾全都改一遍,否则这些sql查询出的实体数据,再执行updateByPrimaryKey方法就会把新增的字段修改为空。因此一个看似加个字段的简单需求,研发改动点会很多,测试的验证点也会很多。

2、 发送通知需求:mybatis plus时代,利用mybatis plus + Lambda表达式就能轻松实现,具体代码如下:

代码语言:javascript
复制
//第一步根据类别查询该类别下的商品
QueryWrapper<ProductEntity> productQueryWrapper = new QueryWrapper<>();
productQueryWrapper.eq("product_type_id", productTypeId);
List<ProductEntity> productList = productService.list(productQueryWrapper);

//第二步 用lambda表达式取出商品ID的集合
if(productList!=null && productList.size()>0){
    List<Integer> proudctIdList = productList.stream().map(p -> p.getProductId()).collect(Collectors.toList());

//第三步 查询该商品类别下的 未推送通知的订单列表
    QueryWrapper<OrderEntity> orderEntityQueryWrapper = new QueryWrapper<>();
    orderEntityQueryWrapper.in("product_id",proudctIdList);
    orderEntityQueryWrapper.eq("notify_status", StateTypeEnum.invalid.value());
    orderEntityQueryWrapper.orderByDesc("id");
    List<OrderMallEntity> orderList = orderService.list(orderEntityQueryWrapper);
}

最后处理完发送通知后,调用save方法修改订单状态

代码语言:javascript
复制
order.setNotifyStatus(StateTypeEnum.effective.value());
order.setUpdateTime(new Date());
orderService.saveOrderMall(order);

用mybatis plus QueryWrapper实现的好处就是:

(1) 全业务处理流程没有手写的SQL,dao层不会有过多的方法代码;

(2) 查询和修改方法由于都是基于实体映射的,因此即使日后扩展字段也不会有问题

三、 查询需求(传统mybatis写法 和 mybatis plus 对比)

需求:查询某一用户的订单列表,列出订单信息和订单的商品信息。 1、 查询需求:传统手写sql的时代dao层基本上会实现一个方法: 用订单表去关联产品表,查询出前端展示所需要的全部字段信息。

代码语言:javascript
复制
@Select(" SELECT o.id,o.order_code AS orderCode,o.notify_status AS notifyStatus,o.product_id AS productId " +
        "      ,p.product_name AS productName,p.product_type_id AS productType,p.product_img AS  productImg " +
        "      ,p.product_price AS  productPrice " +
        " FROM product_summary p  " +
        " INNER JOIN order_mall o ON o.product_id=p.product_id " +
        " WHERE  o.user_id=#{userId} " +
        " ORDER BY o.create_time desc ")
List<MallOrderDto> getOrdersByUserId(@Param("userId") Integer userId);

这种“单一场景”写法依然程序的健壮性很差,举个例子:某一天产品同事告诉你说咱们的商品需要增加一个“双十一”活动标签的概念。商品列表页、商品详情页、用户购买记录和订单详情页等等,只要涉及商品的地方,是“双十一”的商品都需要加上这个标签。 一听到这个需求,第一意识就是需要在商品表里加个字段,然后将所有涉及产品的实体类扩展这个属性,并且将所有dao层的查询sql都修改一遍,加上这个字段。

2、 查询需求:mybatis plus时代,利用mybatis plus + Lambda表达式就能轻松实现,具体代码如下:

代码语言:javascript
复制
//第一步根据 用户查询该用户的订单列表
QueryWrapper<OrderEntity> orderEntityQueryWrapper = new QueryWrapper<>();
orderEntityQueryWrapper.eq("user_id",mallOrder.getUserId());
orderEntityQueryWrapper.orderByDesc("create_time");
List<OrderEntity> orderEntityList = orderService.list(orderEntityQueryWrapper);

//第二步 用lambda表达式取出商品ID的集合
if(orderEntityList!=null && orderEntityList.size()>0){
    List<Integer> proudctIdList = orderEntityList.stream().map(o -> o.getProductId()).distinct().collect(Collectors.toList());

//第三步 根据商品ID的集合查询商品列表
    QueryWrapper<ProductEntity> productQueryWrapper = new QueryWrapper<>();
    productQueryWrapper.in("product_id", proudctIdList);
    List<ProductEntity> productList = productService.list(productQueryWrapper);

//第四部将两个集合数据返回给前端
    respData.put("orderList",orderEntityList);
    respData.put("productList",productList);
}

这种实现方式,对于“双十一”商品标签这个需求,只需要数据库添加完字段,直接实体类扩展属性即可。所有使用到商品实体类做查询的接口自动就完成填充了。无需到dao层一个一个去修改select语句。

四、mybatis plus特性

总结:

虽然相比join多了一次数据库的交互,会稍微浪费点性能,但整体的代码可读性、可维护性和健壮性得到了很大提升。

 文章的最后,将网上摘来的mybatis plus特性的一张图呈现给大家了解:

作者:谭文涛

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-12-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、 场景描述
  • 二、 发送通知需求(传统mybatis写法 和 mybatis plus 对比)
  • 三、 查询需求(传统mybatis写法 和 mybatis plus 对比)
  • 四、mybatis plus特性
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档