假设有两张表:一张商品表、一张订单表,具体表的字段以下:java
现有以下需求:sql
需求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:根据支付状态和通知状态查询订单表信息mybatis
@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,根据订单状态查询订单信息。app
传统mybaits须要三步:首先须要在订单表里加个字段,而后在订单的实体类添加这个属性,而且将全部dao层设计该状态的的查询sql都修改一遍,加上这个字段。分布式
需求a:对于不一样的实体类,即便查询的目的一致,仍然须要重复构造相似的sql语句,仅仅是表字段和表信息不一样。工具
需求b:对于类似的查询条件,针对某个单一场景必须构造不一样的sql,形成sql语句的大量冗余。性能
需求c:将dao层全部涉及到新增字段的sql都须要修改一遍,这个过程比较繁琐且容易出错。测试
使用mybatis-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,所以减小了大量的重复劳动。
MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的加强工具,在 MyBatis 的基础上只作加强不作改变,为简化开发、提升效率而生。
本文针对MyBatis-plus中的核心功能:SQL自动注入功能,进行流程分析及原理探究。
先从一个简单的demo入手,感觉一下MyBatis-plus的便捷性。
源码中提供了一个基础接口,里面包含了基本的增删改查方法。
业务中根据具体实体对象,继承该抽象接口。
控制台显示:MyBatis-plus最终为咱们自动生成了SQL语句。根据上述操做分析:UserMapper继承了BaseMapper,拥有了deleteById的方法,可是MyBatis-plus是基于mybatis的加强版,关键在于最终仍然须要提供具体的SQL语句,来进行数据库操做。
下面就经过debug由上而下分析mybatis-plus是如何生成业务sql以及自动注入的。
mappedStatements:描述sql信息
以下图所示:mybatis为咱们生成了一个代理对象,里面包含了一些重要的属性。
具体以下:
userMapper——>mybatisMapperProxy——>sqlSession——>sqlSessionFactory——>configuration——>mappedStatements——>mappedStatement——>sql语句
至此咱们能够发现每个SQL语句对应一个mappedStatement,mappedstatements存储在configuration文件(configuration是mybatis的全局配置文件,包含数据源、mapper、其余配置信息)中。
基于上面的分析,想要知道SQL语句何时拿到的,就是要找到mappedStatement被添加的位置。追踪到AbstractMethod的抽象方法中。
找到了addMappedStatement()方法
而BaseMapper的全部方法(deleteById、delete、insert、select、update等)都继承了该抽象方法。
根据mapper方法(deleteById)显然是调用addDeleteMappedStatement方法。
这里咱们能够发现,源码中根据不一样的方法继承AbstractMethod实现了不一样的实现类,而且实现了injectMappedStatement方法,sqlSource也是在这个地方被添加进配置文件。
继续研究AbstractMethod抽象类,inject方法实现了自动注入sql的动做。
有上述源码可知,项目启动时,首先由默认注入器生成基础CRUD实现类对象,其次遍历实现类列表,依次注入各自的模板SQL,最后将其添加至mappedstatement。
上述方法中有两个关键的参数:SqlMethod、SqlSource;
继续研究源码发现:sqlMethod本质上是一个枚举类,存储了两个关键的元素:
BaseMapper中的方法名;
方法名对应的sql语句模板(即被<scripe>标签包裹的字符串)。
到这里咱们基本了解了mybaits-plus实现sql自动生成的本质:根据不一样的方法来提供一些通用的模板,项目启动后再加载进mappedStatement。
此时SqlSource经过解析SQL模板、以及传入的表信息和主键信息构建出了SQL语句。
分析initTableName()方法:获取表名信息源码中传入了实体类信息clazz,其实就是经过实体上的@TableName注解拿到了表名;
咱们在定义实体类的同时,指定了该实体类对应的表名。
分析initTableFields()方法:
获取主键及其余字段信息
至此tableInfo的信息已经注入完成了。
在研究完解析mapper的核心过程以后,咱们再简单看下mapper文件被添加到configuration(mybatis核心配置文件)的过程。
ISqlInjector:Sql注入器
MybatisMapperAnnotationBuilder:mapper解析器
MybatisMapperAnnotationBuilder中的parse方法获取了sqlInjector(Sql注入器)来进行SQL注入。
Mybatis添加mapper的固有流程:MybatisMapperRegistry
调用MapperAnnotionBuilder解析器进行解析
MybatisConfiguration.addMapper
MybatisXMLConfigBuilder.mapperElemnt
MybayisXMLConfigBuilder.parseConfiguration
添加mapper文件的过程分析到这里就完成了。
下面总结梳理了一下mybatis-plus解析mapper文件自动注入sql的主要流程。
1)实体类和数据库表经过自定义注解来完成一一映射。
2)对象属性和字段一样使用注解来一一对应(命名注意要相同)。
3)为了提升复用性使得具体的mapper继承通用的增删改查接口。
4)利用模板方法和对象属性值动态拼接SQL。
MyBatis-plus官方文档:https://mp.baomidou.com/
做者:vivo互联网服务器团队-Li Lei