2020年Mybatis源码解读

为什么需要ORM 框架Mybatis

  • JDBC 工作量大,操作数据库至少要五步
  • 业务代码和技术代码耦合
  • 连接资源手动关闭,带来隐患
  • 三大巨头
    Connection 获得一个连接
    PreparedStatement 对sql语句进行预编译
    ResultSet 返回结果

ORM框架-前身是Ibatis

Mysql映射文件三要素:

  • SQL
  • 映射规则
  • POJO

Mybatis 单独开发-快速入门

  • 1、加入mybatis依赖
  • 2、添加mybatis配置文件
  • 3、场景介绍
  • 4、编写实体类、mapper、以及mapper.xml
  • 5、编写实例代码

mybatis核心配置xml

  • mybatis-config.xml
  • 实体类 mapper接口 mapper.xml;
  • 通过namespace对应不同的接口,方法对应不同id;

原理:读取配置文件创建SqlSessionFactory,拿到Sqlsession,获取对应的mapper配置文件执行对应的sql语句。
核心类:SqlsessionFactoryBuilder:读取配置信息
建造者模式 :建造SqlsessionFactory,方法级别生命周期。

SqlsessionFactory:创建sqlsession,工厂单例模式存在于整个程序生命周期
Sqlsession:线程不安全,保证线程独享(方法级别)
SQLMapper: 由一个Java接口和XML文件组成,包含了要执行的sql语句和结果集映射,方法级别生命周期;

resultType 还是 resultMap 映射关系

  • resultType对应某一个POJO
  • resultMap 属性:
    • id 唯一标识
    • type 对应的POJO
    • autoMapping 默认为true,自动映射字段与属性对应
    • 子元素:
      id:标记出id可以提高整体检索性能,一对多用于结果集合并
      result:与JavaBean 属性类型一一对应
      association:一对一查询
      collection:一对多查询
  • resultMap可以通过继承简化配置;关联的表加上前缀是一种好习惯;
  • 通过完整的命名空间可以引用其他xml文件的resultMap;
  • 阿里开发手册中强制使用resultMap:
    原因:解耦,如果有一天修改了数据库,只需要改动配置文件,而不需要改动代码重新编译等.

怎么传递多个参数?

  • 1、实体类 --常用
  • 2、map传递 可读性差,可维护性比较差,杜绝使用
  • 3、@Param注解传递 – 简单明了,小于5个参数建议使用

怎么获取主键?

  • useGeneratedKeys:true
  • keyProperty:id

SQL元素和SQL参数

预编译 #{} 进行了预编译,将传进来的参数当成一个字符串(加上单引号),能够很大程度防止sql注入;
传值 ${} 传入的数据直接显示在sql语句中,无法防止sql注入;

动态SQL

  • if 单条件分支判断
  • choose/when/otherwise 多条件分支判断
  • trim 处理插入/where查找/set更新 处理sql拼装问题
  • foreach in语句常用,用于处理批量操作

高频面试题:通过Mybatis怎么进行批量操作

  • 通过foreach拼装sql语句
  • 会用BATCH类型的excutor(执行器)

Mybatis Generator MBG代码生成器:

  • 通过数据库表生成实体类、Mapper接口、mapper.xml文件
  • generatorConfiguration:根节点
  • context:上下文
  • 代码生成器的三种方式:Maven Plugin、Java程序启动、命令行使用

关联查询

  • 被关联的字段需要有索引,如果有三个表以上的join关联
    可以进行两次查询
  • 嵌套结果与嵌套查询

mybatis如何使用懒加载

mybatis 缓存

一级缓存默认开启,关闭一级缓存在select标签上flushCache=“true”
一级缓存的生命周期时一次会话(Sqlsession)
update/insert/delete 都会清空缓存

二级缓存默认关闭

存在于SqlsessionFactory,跨sqlsession
二级缓存容易出现脏读,建议避免使用二级缓存,在业务层使用可控制的缓存代替(redis)

Mybatis 源码深入

Mybatis整体架构

  • 基础支撑层:数据源、事务管理、缓存、Binding模块、反射、类型转换、日志模块、资源加载、解析器
  • 核心处理层:配置解析、参数映射、SQL解析、SQL执行、结果集映射、插件
  • 接口层:SqlSession

外观模式(门面模式):提供了一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,让子系统更容易使用;
对内封装具体细节,对外只暴露必要的接口;
看源码必备

  • 动态代理基础 技术基础
    设计原则 思想基础
  • 单一职责原则
  • 依赖倒转原则:面向接口编程,当实现发生变化时,只提供新的实现类,不需要修改高层模块的代码;
  • (最重要)开放-封闭原则:程序对外扩展开放,对修改关闭。即当需求发生变化时,我们可以添加新模块来满足新需求,而不是通过修改原来的代码满足新需求。
  • 迪米特法则:一个对象应该对其他对象保持最少了解,尽量降低类与类之间的耦合;
  • 里氏替换原则:
  • 接口隔离原则

基础支撑层源码分析-日志模块需求

Mybatis没有提供日志实现类,需要接入第三方日志组件,第三方日志组件都有不同的Log级别,且各不相同,Mybatis统一提供了trace、debug、warn、error四个级别;
自动扫描日志实现,并且第三方插件加载优先级为:slf4j–commonsLoging–Log4j2–Log4j–jdkLog

日志的使用优雅的嵌入到主体功能中
BaseJdbcLogger:所有日志增强的抽象基类;
ConnectionLogger:负责打印连接信息和SQL语句,并创建PreparedStatementLogger;
PreparedStatementLogger:负责打印参数信息,并创建ResultSetLogger;
ResultSetLogger:负责打印数据结果信息;

ConnectionLogger,PreparedStatementLogger,ResultSetLogger 都实现了动态代理;
适配器模式:作为两个不兼容接口之间的桥梁,将一个类的接口转换成客户希望的另一个接口,适配器模式使得原本不兼容的接口可以一起工作。
Target:目标角色,期待得到的接口;
Adaptee:适配者角色,被适配的接口;
Adapter:适配器角色,将源接口转换成目标接口;
适用场景:当调用双方不容易修改时,为了复用现有组件可以使用适配器模式;在系统中接入第三方组件时经常用到;

代理模式:给目标对象提供一个代理对象,并由代理对象控制目标对象的引用;
目的:通过引入代理对象的方式间接访问目标对象,防止直接访问目标对象带来不必要的系统复杂性;通过代理对象对原有的业务进行增强;

Mybatis哪些地方需要打印日志:

  • 在创建prepareStatement时,打印执行SQL语句;
  • 访问数据库时,打印参数的类型和值;
  • 查询出结果时,打印结果数据条数;

日志模块JDBC包类图

在这里插入图片描述

扫描二维码关注公众号,回复: 12488475 查看本文章
  • ConnectionLogger:负责打印连接信息和SQL语句,并创建PrepareStatementLogger;
  • PrepareStatementLogger:负责打印参数信息,并创建ResultSetLogger;
  • ResultSetLogger:负责打印数据结果信息;

数据源模块分析

基础支撑层源码分析 创建一个数据源的难点

  • 常见的数据源组件都实现了javax.sql.DataSource接口;
  • Mybatis不但要集成第三方数据源组件,自身也提供了数据源的实现;
  • 一般情况下,数据源的初始化参数较多,比较复杂;

工厂模式(Factory Pattern):属于创建型模式,它提供了一种创建对象的最佳方式。定义一个创建对象的接口,让其子类觉得实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行;
产品接口(Product):产品接口用于定义产品类的功能,具体工厂类产生的所有产品都必须实现这个接口。调用者与产品接口直接交互,这是调用者最关心的接口;
具体产品类(ConcreteProduct):实现产品接口的实现类,具体产品类中定义了具体的业务逻辑;
**工厂接口(Factory):**工厂接口是工厂模式的核心接口,调用者会直接和工厂接口交互用于获取具体的产品实现类;
具体工厂类(ConcreteFactory): 工厂接口的实现类,用于实例化产品对象,不同的具体工厂类根据需求实例化不同的产品实现类;
简单工厂模式

为什么使用工厂模式?

在这里插入图片描述

对象的创建和对象的使用过程解耦,方便扩展,不需要修改原有的代码,新功能新需求直接添加工厂类就可以。

数据连接池核心类

  • PooledDataSource:一个简单,同步的、线程安全的数据库连接池;
  • PooledConnection :使用动态代理封装了真正的数据库连接对象;
  • PoolState:用于管理PooledConnection对象状态的组件,通过两个list分别 管理空闲状态的连接资源和活跃状态的连接资源;
    PooledDataSource获取和归还连接过程:
    在这里插入图片描述
    在这里插入图片描述

基础支撑层源码分析-缓存模块需求

装饰器模式:是一种用于代替继承的技术,无需通过继承扩展子类就能扩展对象的新功能,使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀;
在这里插入图片描述

组件(Component):组件接口定义了全部组件类和装饰器实现的行为;
组件实现类(ConcreteComponent):实现Component接口,组件实现类就是被装饰器装饰的原始对象,新功能或者附加功能都是通过装饰器添加到该类的对象上的;
装饰器抽象类(Decorator):实现Component接口的抽象类,在其中封装了一个Component 对象,也就是被装饰的对象;
具体装饰器类(ConcreteDecorator):该实现类要向被装饰的对象添加;
优点

  • 相对于继承,装饰器模式灵活性更强,扩展性更强;
  • 灵活性:装饰器模式将功能切分成一个个独立的装饰器,在运行期可以根据需要动态的添加功能,甚至对添加的新功能进行自由的组合;
  • 扩展性:当有新功能要添加的时候,只需要添加新的装饰器实现类,然后通过组合方式添加这个新装饰器,无需修改已有代码,符合开闭原则;
    装饰器模式使用举例
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("c://a.txt")));

Mybatis缓存组件
在这里插入图片描述

  • Cache:Cache接口是缓存模块的核心接口,定义了缓存的基本操作;
  • PerpetualCache:在缓存模块中扮演ConcreteComponent角色,使用HashMap来实现cache的相关操作;
  • BlockingCache:阻塞版本的缓存装饰器,保证只有一个线程到数据库去查找指定的key对应的数据;

缓存装饰器解读

  • FifoCache:先进先出缓存淘汰策略
  • LoggingCache:日志能力的缓存
  • ScheduledCache:定时清空的缓存
  • BlokingCache:阻塞式缓存
  • SeralizedCache:序列化能力的缓存
  • SynchronizedCache:进行同步控制的缓存
    某些功能;
    CacheKey

MyBatis中涉及到动态SQL的原因,缓存项的key不能仅仅通过一个String来表示,所以通过CacheKey来封装缓存的Key值,CacheKey可以封装多个影响缓存项的因素;判断两个CacheKey是否相同关键是比较两个对象的hash值是否一致;

构成CacheKey的对象

  • mappedStatment的id
  • 指定查询结果集的范围(分页信息)
  • 查询所使用的SQL语句
  • 用户传递给SQL语句的实际参数值

反射模块分析

orm框架查询数据过程
从数据库加载数据—>找到映射匹配规则—>实例化目标对象—>对象属性赋值

反射的核心类

  • ObjectFactory:Mybatis每次创建结果的新实例时,它都会使用对象工厂(ObjectFactory)去构建POJO;
  • ReflectorFactory:创建Reflector的工厂类,Reflector是Mybatis反射模块的基础,每个Reflector对象都对应一个类,在其中缓存了反射操作所需要的类元信息;
  • ObjectWrapper:对对象的包装,抽象了对象的属性信息,他定义了一系列查询对象属性信息的方法,以及更新属性的方法;
  • ObjectWrapperFactory:ObjectWrapper的工厂类,用于创建ObjectWrapper;
  • MetaObject:封装了对象元信息,包装了MyBatis中五个核心的反射类。也是提供给外部使用的反射工具类,可以利用它可以读取或者修改对象的属性信息;
    在这里插入图片描述

Mybatis核心流程三大阶段

在这里插入图片描述

  • 初始化阶段:读取XML配置文件和注解中的配置信息,创建配置对象,并完成各个模块的初始化工作;
  • 封装iBatis的编程模型,使用mapper接口开发和初始化工作;
  • 通过SqlSession完成SQL的解析,参数的映射、SQL的执行、结果的解析过程;

配置加载阶段

Mybatis 初始化 建造者模式

  • 建造者模式(Builder Pattern):使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
    在这里插入图片描述
  • Product (产品):要创建的复杂对象;
  • Builder(建造者):给出一个抽象接口,以规范产品对象的各个组成成分的建造。这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建;
  • ConcreteBuilder(具体建造者):实现Builder接口,针对不同的商业逻辑,具体化复杂对象的各部分的创建。 在建造过程完成后,提供产品的实例;
  • Director(导演):调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建;

建造者模式使用场景

  • 需要生成的对象具有复杂的内部结构,实例化对象时要屏蔽对象内部的细节,让上层代码与对象的实例化过程解耦,可以使用建造者模式;简而言之:如果 “遇到多个构造器参数时考虑用构建器”;
  • 对象的实例化是依赖各个组件的产生和装配顺序,关注的是一步步组装出目标对象,可以使用建造者模式;
设计模式 形象比喻 对象复杂度 客户端参与程度
工厂模式 生产大众版 关心的是一个产品的整体,无需关心产品的各部分是如何创建的出来的。 客户端对产品的创建过程参与度低,对象实例化时属性值相对比较固定
建造者模式 生产定制版 建造的对象更加复杂,是一个复合产品,它由各个部件复合而成,部件不同产品对象不同,生成的产品粒度细; 客户端参与了产品的创建,觉得了产品的类型和内容,参与度高;适合实例化对象时属性变化频繁的场景;

建造者模式在MyBatis中的应用

在这里插入图片描述

  • SqlSessionFactoryBuilder创建SqlSessionFactory
  • XMLConfigBuilderXMLMapperBuilderXMLStatementBuilder创建configuration对象
  • CacheBuilder 创建二级缓存
  • ResultMapping Builder创建ResultMapping对象

MyBatis建造者类图
在这里插入图片描述

  • BaseBuilder:所有解析器的父类,包含配置文件实例,为解析文件提供的一些通用的方法;
  • XMLConfigBuilder: 主要负责解析mybatis-config.xml;
  • XMLMapperBuilder: 主要负责解析映射配置文件;
  • XMLStatementBuilder: 主要负责解析映射配置文件中的SQL节点;

Mybatis初始化

在这里插入图片描述

阿里面试题:为什么使用mapper接口就可以对数据库进行开发?
在这里插入图片描述
映射器的关键类

  • Configuration:Mybatis启动初始化的核心就是将所有的xml配置文件信息加载到Configuration对象中,Configuration是单例的,生命周期是应用级的;
  • MapperRegistry:mapper接口动态代理工厂类的注册中心。在Mybatis中,通过mapperProxy实现InvocationHandler接口,MapperProxyFactory用于生成动态代理的实例对象;
  • ResultMap:用于解析mapper.xml文件中的resultMap节点,使用ResultMapping来封装id,result等子元素;
  • MappedStatement:用于存储mapper.xml文件中的selsct、insert、update和delete节点,同时还包含了
    这些节点的很多重要属性;
  • SqlSource:用户创建BoundSql,mapper.xml中的sql语句会被解析成BoundSql对象,经过解析的BoundSql语句最后仅包含 ? 占位符,可以直接提交给数据库执行;

ResultMap图解
在这里插入图片描述

配置文件解读+动态代理增强

binding模块分析

Executor组件分析

模板模式

  • Executor是Mybatis核心接口之一,定义了数据库操作最基本的方法,SqlSession的功能都是基于它来实现的;
    编程的经验和技巧,源码的思路
    先了解清楚数据结构,然后再详细探讨算法

猜你喜欢

转载自blog.csdn.net/weixin_42292697/article/details/109788159