乐趣区

关于mybatis:MybatisPlus的应用场景及注入SQL原理分析

一、背景

1.1 传统Mybatis的弊病

1.1.1 场景形容

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

现有如下需要:

  • 别离依据id查问商品表和订单表所有信息
  • 依据领取状态和告诉状态查问订单表信息
  • 对订单表减少一个订单状态,依据订单状态查问订单信息

1.1.2 需要

需要a:依据id查问商品表:

​@Select(" SELECT p.id ,p.name ,p.picture ,p.type ,p.price, p.type, p.time
          FROM product p  where id = #{id} ")
List<Product> getProductsBYId(@Param("id") Integer id);

依据id查问订单表所有信息:

@Select(" SELECT o.id ,o.pay_no ,o.user_id ,o.product_id ,o.pay_price, o.num, o.pay_time, o.order_type, o.notif_type
          FROM order o  where id = #{id} ")
List<Order> getOrderBYId(@Param("id") Integer id);

需要b:依据领取状态和告诉状态查问订单表信息

​@Select(" SELECT o.id ,o.pay_no ,o.user_id ,o.product_id ,o.pay_price, o.num, o.pay_time, o.order_type, o.notif_type
          FROM order o  where order_type= #{orderType} ")
List<Order> getOrderBYId(@Param("orderType") Integer orderType);
 
@Select(" SELECT o.id ,o.pay_no ,o.user_id ,o.product_id ,o.pay_price, o.num, o.pay_time, o.order_type, o.notif_type
          FROM order o  where notify_type= #{notifyType} ")
List<Order> getOrderBYId(@Param("notifyType") Integer notifyType);

需要c:对订单表减少一个订单状态status,依据订单状态查问订单信息。

传统mybaits须要三步:首先须要在订单表里加个字段,而后在订单的实体类增加这个属性,并且将所有dao层设计该状态的的查问sql都批改一遍,加上这个字段。

1.1.3 上述形式有什么问题呢?

需要a:对于不同的实体类,即便查问的目标统一,依然须要反复结构相似的sql语句,仅仅是表字段和表信息不同。

需要b:对于类似的查问条件,针对某个繁多场景必须结构不同的sql,造成sql语句的大量冗余。

需要c:将dao层所有波及到新增字段的sql都须要批改一遍,这个过程比拟繁琐且容易出错。

应用mybatis-plus就能够解决上述问题。

1.1.4 Myatis-plus的解决方案

首先让ProductMapper和OrderMapper继承BaseMapper类:

public interface ProductMapper extends BaseMapper<Product> {
}
 
public interface OrderMapper extends BaseMapper<Order> {
  
}

需要a:

别离依据id查问商品表和订单表:因为BaseMapper中提供了selectById的办法,能够间接依据具体业务场景,传入指定的参数例如(id=1)即可。无需书写具体的sql语句,至于sql主动生成的原理将在上面介绍;

productMapper.selectById(1);
orderMapper.selectById(1);

需要b:

此时利用BaseMapper.selectList(Wapper queryWrapper)办法间接结构查问条件,例如查问领取状态为2和告诉状态为1的订单信息

orderMapper.selectList(new QueryWrapper<Order>().eq("orderType",2));
orderMapper.selectList(new QueryWrapper<Order>().eq("notifyType",1));
orderMapper.selectList(new QueryWrapper<Order>().eq("orderType",2));orderMapper.selectList(new QueryWrapper<Order>().eq("notifyType",1));

此时咱们能够发现:应用了Mybatis-plus当前,咱们更加聚焦于业务自身,对于上述类似的利用场景,无需结构雷同的SQL,利用包装器间接传入查问条件。

需要c:

前两步与传统mybatis统一,因为MyBatis-plus无需手动创立SQL,因而缩小了大量的重复劳动。

1.2 MyBatis-Plus的定位

MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的加强工具,在 MyBatis 的根底上只做加强不做扭转,为简化开发、提高效率而生。

1.3 个性

  • 无侵入:只做加强不做扭转,引入它不会对现有工程产生影响,如丝般顺滑;
  • 损耗小:启动即会主动注入根本 CURD,性能根本无损耗,间接面向对象操作;
  • 弱小的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过大量配置即可实现单表大部分 CRUD 操作,更有弱小的条件结构器,满足各类应用需要;
  • 反对 Lambda 模式调用:通过 Lambda 表达式,不便的编写各类查问条件,无需再放心字段写错;
  • 反对主键主动生成:反对多达 4 种主键策略(内含分布式惟一 ID 生成器 – Sequence),可自在配置,完满解决主键问题;
  • 反对 ActiveRecord 模式:反对 ActiveRecord 模式调用,实体类只需继承 Model 类即可进行弱小的 CRUD 操作;
  • 反对自定义全局通用操作:反对全局通用办法注入( Write once, use anywhere );
  • 内置代码生成器:采纳代码或者 Maven 插件可疾速生成 Mapper 、 Model 、 Service 、 Controller 层代码,反对模板引擎,更有超多自定义配置等您来应用;
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关怀具体操作,配置好插件之后,写分页等同于一般 List 查问;
  • 分页插件反对多种数据库:反对 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库;
  • 内置性能剖析插件:可输入 Sql 语句以及其执行工夫,倡议开发测试时启用该性能,能疾速揪出慢查问;
  • 内置全局拦挡插件:提供全表 delete 、 update 操作智能剖析阻断,也可自定义拦挡规定,预防误操作。

1.4 原理解析

本文针对MyBatis-plus中的外围性能:SQL主动注入性能,进行流程剖析及原理探索。

二、筹备工作

先从一个简略的demo动手,感受一下MyBatis-plus的便捷性。

2.1 根底接口BaseMapper

源码中提供了一个根底接口,外面蕴含了根本的增删改查办法。

2.2 创立实体类对象

2.3 业务接口UserMapper

业务中依据具体实体对象,继承该形象接口。

2.4 测试用例

控制台显示:MyBatis-plus最终为咱们主动生成了SQL语句。根据上述操作剖析:UserMapper继承了BaseMapper,领有了deleteById的办法,然而MyBatis-plus是基于mybatis的增强版,关键在于最终依然须要提供具体的SQL语句,来进行数据库操作

上面就通过debug由上而下剖析mybatis-plus是如何生成业务sql以及主动注入的。

三、SQL语句存储在哪里?

mappedStatements:形容sql信息

如下图所示:mybatis为咱们生成了一个代理对象,外面蕴含了一些重要的属性。

具体如下:

userMapper——>mybatisMapperProxy——>sqlSession——>sqlSessionFactory——>configuration——>mappedStatements——>mappedStatement——>sql语句

至此咱们能够发现每一个SQL语句对应一个mappedStatement,mappedstatements存储在configuration文件(configuration是mybatis的全局配置文件,蕴含数据源、mapper、其余配置信息)中。

四、SQL语句是什么时候注入的?

4.1 AbstractMethod.addMappedStatement

基于下面的剖析,想要晓得SQL语句什么时候拿到的,就是要找到mappedStatement被增加的地位。追踪到AbstractMethod的形象办法中。

找到了addMappedStatement()办法

而BaseMapper的所有办法(deleteById、delete、insert、select、update等)都继承了该形象办法。

依据mapper办法(deleteById)显然是调用addDeleteMappedStatement办法。

这里咱们能够发现,源码中依据不同的办法继承AbstractMethod实现了不同的实现类,并且实现了injectMappedStatement办法,sqlSource也是在这个中央被增加进配置文件。

4.2 AbstractMethod.inject

持续钻研AbstractMethod抽象类,inject办法实现了主动注入sql的动作。

有上述源码可知,我的项目启动时,首先由默认注入器生成根底CRUD实现类对象,其次遍历实现类列表,顺次注入各自的模板SQL,最初将其增加至mappedstatement。

五、SQL语句是怎么生成的?

5.1 SQL模板

上述办法中有两个要害的参数:SqlMethod、SqlSource;

持续钻研源码发现:sqlMethod实质上是一个枚举类,存储了两个要害的元素:

BaseMapper中的办法名;

办法名对应的sql语句模板(即被<scripe>标签包裹的字符串)。

到这里咱们根本理解了mybaits-plus实现sql主动生成的实质:依据不同的办法来提供一些通用的模板,我的项目启动后再加载进mappedStatement。

5.2 SqlSource

此时SqlSource通过解析SQL模板、以及传入的表信息和主键信息构建出了SQL语句。

5.3 数据库表信息是如何获取的?

剖析initTableName()办法:获取表名信息源码中传入了实体类信息clazz,其实就是通过实体上的@TableName注解拿到了表名;

咱们在定义实体类的同时,指定了该实体类对应的表名。

剖析initTableFields()办法:

获取主键及其他字段信息

至此tableInfo的信息曾经注入实现了。

在钻研完解析mapper的外围过程之后,咱们再简略看下mapper文件被增加到configuration(mybatis外围配置文件)的过程。

六、mapper文件被增加的过程

ISqlInjector:Sql注入器

MybatisMapperAnnotationBuilder:mapper解析器

MybatisMapperAnnotationBuilder中的parse办法获取了sqlInjector(Sql注入器)来进行SQL注入。

Mybatis增加mapper的固有流程:MybatisMapperRegistry

调用MapperAnnotionBuilder解析器进行解析

MybatisConfiguration.addMapper

MybatisXMLConfigBuilder.mapperElemnt

MybayisXMLConfigBuilder.parseConfiguration

增加mapper文件的过程剖析到这里就实现了。

七、总结

7.1 流程梳理

上面总结梳理了一下mybatis-plus解析mapper文件主动注入sql的次要流程。

7.2 Mybatis-plus的ORM的核心思想

1)实体类和数据库表通过自定义注解来实现一一映射。

2)对象属性和字段同样应用注解来一一对应(命名留神要雷同)。

3)为了进步复用性使得具体的mapper继承通用的增删改查接口。

4)利用模板办法和对象属性值动静拼接SQL。

八、参考文档

MyBatis-plus官网文档:https://mp.baomidou.com/

作者:vivo互联网服务器团队-Li Lei

退出移动版