您当前的位置:首页 > 电脑百科 > 数据库 > 百科

MyBatis源码解析

时间:2022-01-13 11:09:22  来源:  作者:java成神之路

1.Executor

Executor 是 MyBatis 的核心接口之一,其中定义了数据库操作的基本方法。在实际应用中经常涉及的 SqISession 接口的功能,都是基于 Executor 接口实现的。

 

MyBatis源码解析

 

BaseExecutor 是一个实现了 Executor 接口的抽象类,它实现了 Executor 接口的大部分方法。BaseExecutor 中主要提供了缓存管理和事务管理的基本功能,继承 BaseExecutor 的子类只要实现四个基本方法来完成数据库的相关操作即可,这四个方法分别是:doUpdate()方法、doQuery()方法、doQueryCursor()方法、doFlushStatement()方法。


 // 一级缓存,用于缓存该Executor对象查询结果集映射得到的结果对象
  protected PerpetualCache localCache;
  // 一级缓存,用于缓存输出类型的参数
  protected PerpetualCache localOutputParameterCache;

常见的应用系统中,数据库是比较珍贵的资源,很容易成为整个系统的瓶颈。在设计和维护系统时,会进行多方面的权衡,并且利用多种优化手段,减少对数据库的直接访问。

使用缓存是一种比较有效的优化手段,使用缓存可以减少应用系统与数据库的网络交互、减少数据库访问次数、降低数据库的负担、降低重复创建和销毁对象等一系列开销,从而提高整个系统的性能。

MyBatis 提供的缓存功能,分别为一级缓存和二级缓存。BaseExecutor 主要实现了一级缓存的相关内容。一级缓存是会话级缓存,在 MyBatis 中每创建一个 SqlSession 对象,就表示开启一次数据库会话。在一次会话中,应用程序可能会在短时间内(一个事务内),反复执行完全相同的查询语句,如果不对数据进行缓存,那么每一次查询都会执行一次数据库查询操作,而多次完全相同的、时间间隔较短的查询语句得到的结果集极有可能完全相同,这会造成数据库资源的浪费。

为了避免上述问题,MyBatis 会在 Executor 对象中建立一个简单的一级缓存,将每次查询的结果集缓存起来。在执行查询操作时,会先查询一级缓存,如果存在完全一样的查询情况,则直接从一级缓存中取出相应的结果对象并返回给用户,减少数据库访问次数,从而减小了数据库的压力。

一级缓存的生命周期与 SqlSession 相同,其实也就与 SqISession 中封装的 Executor 对象的生命周期相同。当调用 Executor 对象的 close()方法时(断开连接),该 Executor 对象对应的一级缓存就会被废弃掉。一级缓存中对象的存活时间受很多方面的影响,例如,在调用 Executor 的 update()方法时,也会先请空一级缓存。一级缓存默认是开启的,一般情况下,不需要用户进行特殊配置。

CachingExecutor 中为 Executor 对象增加了二级缓存相关功能,而 mybatis 的二级缓存在实际使用中往往利大于弊,被 redis 等产品所替代

二级缓存mApper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。

Mybatis默认没有开启二级缓存需要在setting全局参数中配置开启二级缓存。

如果缓存中有数据就不用从数据库中获取,大大提高系统性能。

2.StatmentHandler

StatementHandler 接口是 MyBatis 的核心接口之一,它完成了 MyBatis 中最核心的工作,也是 Executor 接口实现的基础。
StatementHandler 接口中的功能很多,例如创建 Statement 对象,为 SQL 语句绑定实参,执行 select、insert、update、delete 等多种类型的 SQL 语句,批量执行 SQL 语句,将结果集映射成结果对象。

public enum StatementType {
  STATEMENT, PREPARED, CALLABLE
}
MyBatis源码解析

 

RoutingStatementHandler 使用了策略模式,RoutingStatementHandler 是策略类,而 SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler 则是实现了具体算法的实现类,RoutingStatementHandler 对象会根据 MappedStatement 对象的 StatementType 属性值选择使用相应的策略去执行。

  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // RoutingStatementHandler的作用就是根据ms的配置,生成一个相对应的StatementHandler对象
    // 并设置到持有的delegate属性中,本对象的所有方法都是通过调用delegate的相应方法实现的     
    switch (ms.getStatementType()) {
            case STATEMENT:
              delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
              break;
            case PREPARED:
              delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
              break;
            case CALLABLE:
              delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
              break;
            default:
              throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
          }
  }

BaseStaementHandler看它以 Base 开头,就可以猜到 它是一个实现了 StatementHandler 接口的抽象类,这个类只提供了一些参数绑定相关的方法,并没有实现操作数据库的方法。

BaseStatementHandler 主要实现了 StatementHandler 接口中的 prepare()方法,BaseStatementHandler 依赖两个重要的组件,ParameterHandler 和 ResultSetHandler。

3.ParameterHandler

DefaultParameterHandler默认实现

我们要执行的 SQL 语句中可能包含占位符"?",而每个"?"都对应了 BoundSql 中 parameterMappings 集合中的一个元素,在该 ParameterMapping 对象中记录了对应的参数名称以及该参数的相关属性。ParameterHandler 接口定义了一个非常重要的方法 setParameters(),该方法主要负责调用 PreparedStatement 的 set*()系列方法,为 SQL 语句绑定实参。MyBatis 只为 ParameterHandler 接口提供了唯一一个实现类 DefaultParameterHandler。

4.ResultSetHandler

SqlSession

 

MyBatis源码解析

 

DefaultSqlSession 是单独使用 MyBatis 进行开发时,最常用的 SqISession 接口实现。其实现了 SqISession 接口中定义的方法,及各方法的重载。select()系列方法、selectOne()系列方法、selectList()系列方法、selectMap()系列方法之间的调用,殊途同归,它们最终都会调用 Executor 的 query()方法。

上述重载方法最终都是通过调用 Executor 的 query(MappedStatement, Object, RowBounds,ResultHandler)方法实现数据库查询操作的,但各自对结果对象进行了相应的调整,例如:selectOne()方法是从结果对象集合中获取了第一个元素返回;selectMap()方法会将 List 类型的结果集 转换成 Map 类型集合返回;select()方法是将结果集交由用户指定的 ResultHandler 对象处理,且没有返回值;selectList()方法则是直接返回结果对象集合。 DefaultSqlSession 的 insert()方法、update()方法、delete()方法也有多个重载,它们最后都是通过调用 DefaultSqlSession 的 update(String, Object)方法实现的,该重载首先会将 dirty 字段置为 true,然后再通过 Executor 的 update()方法完成数据库修改操作。 DefaultSqlSession 的 commit()方法、rollback()方法以及 close()方法都会调用 Executor 中相应的方法,其中就会涉及清空缓存的操作,之后就会将 dirty 字段设置为 false。 上述的 dirty 字段主要在
isCommitOrRollbackRequired()方法中,与 autoCommit 字段以及用户传入的 force 参数共同决定是否提交/回滚事务。该方法的返回值将作为 Executor 的 commit()方法和 rollback()方法的参数。


SqlSessionFactory 负责创建 SqlSession 对象,其中包含了多个 openSession()方法的重载,可以通过其参数指定事务的隔离级别、底层使用 Executor 的类型、以及是否自动提交事务等方面的配置。

DefaultSqlSessionFactory 是 SqlSessionFactory 接口的默认实现,主要提供了两种创建 DefaultSqlSession 对象的方式,一种方式是通过数据源获取数据库连接,并创建 Executor 对象以及 DefaultSqlSession 对象;另一种方式是用户提供数据库连接对象,DefaultSqlSessionFactory 根据该数据库连接对象获取 autoCommit 属性,创建 Executor 对象以及 DefaultSqlSession 对象。

SqlSessionManager 同时实现了 SqlSession 接口和 SqlSessionFactory 接口,所以同时提供了 SqlSessionFactory 创建 SqlSession 对象,以及 SqlSession 操纵数据库的功能。

SqlSessionManager 与 DefaultSqlSessionFactory 的主要不同点 SqlSessionManager 提供了两种模式,第一种模式与 DefaultSqlSessionFactory 的行为相同,同一线程每次通过 SqlSessionManager 对象访问数据库时,都会创建新的 SqlSession 对象完成数据库操作。第二种模式是 SqlSessionManager 通过 localSqlSession 这 ThreadLocal 变量,记录与当前线程绑定的 SqlSession 对象,供当前线程循环使用,从而避免在同一线程多次创建 SqlSession 对象带来的性能损失。

DataSource

DataSourceFactory数据工厂

/**
 * 数据源工厂
 * @author Clinton Begin
 */
public interface DataSourceFactory {

    /**
     * 设置 dataSource 属性
     * @param props
     */
    void setProperties(Properties props);

    /**
     * 获取 dataSource
     * @return {@link DataSource}
     */
    DataSource getDataSource();

}

 

MyBatis源码解析

 

DyanmicSqlSourcce动态sql

PooledDataSource 管理的数据库连接对象 是由其持有的 UnpooledDataSource 对象 创建的,并由 PoolState 管理所有连接的状态。 PooledDataSource 的 getConnection()方法 会首先调用 popConnection()方法 获取 PooledConnection 对象,然后通过 PooledConnection 的 getProxyConnection()方法 获取数据库连接的代理对象。popConnection()方法 是 PooledDataSource 的核心逻

MapperMethod 中封装了 Mapper 接口 中对应方法的信息,和对应 sql 语句 的信息,是连接 Mapper 接口 及映射配置文件中定义的 sql 语句 的桥梁。

MapperMethod 中持有两个非常重要的属性,这两个属性对应的类 都是 MapperMethod 中的静态内部类。另外,MapperMethod 在被实例化时就对这两个属性进行了初始化
MapperMethod 中的核心方法 execute() 就主要用到了这两个类

  public static class SqlCommand {

    // sql语句的id
    private final String name;
    // sql语句的类型,SqlCommandType 是枚举类型,持有常用的 增、删、改、查等操作类型
    private final SqlCommandType type;
    
  }

 

MyBatis源码解析

 

 MyBatis 和spring整合源码

spring启动时候需要是由一个bean.xml配置

ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");

bean.xml配置例如:


<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- 加载数据源 -->
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath*:mappers/*Mapper.xml"/>
</bean>
 
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!-- 指定扫描的包,如果存在多个包使用(逗号,)分割 -->
    <property name="basePackage" value="com.test.bean"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>

MapperScannerConfigurer

这个类主要的方法就是
postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法

MyBatis源码解析

 

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
  if (this.processPropertyPlaceHolders) {
    processPropertyPlaceHolders();
  }
//ClassPathMapperScanner扫描器,这个扫描器继承了spring的ClassPathBeanDefinitionScanner。
  /**
第一扫描basePackage包下面所有的class类
第二将所有的class类封装成为spring的ScannedGenericBeanDefinition sbd对象
第三过滤sbd对象,只接受接口类
第四完成sbd对象属性的设置,比如设置sqlSessionFactory、BeanClass等,这个sqlSessionFactory是本文接下来要解析的SqlSessionFactoryBean
第五将过滤出来的sbd对象通过这个BeanDefinitionRegistry registry注册器注册到DefaultListableBeanFactory中,这个registry就是方法postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)中的参数。

  */
  ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
  scanner.setAddToConfig(this.addToConfig);
  scanner.setAnnotationClass(this.annotationClass);
  scanner.setMarkerInterface(this.markerInterface);
  scanner.setSqlSessionFactory(this.sqlSessionFactory);
  scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
  scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
  scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
  scanner.setResourceLoader(this.applicationContext);
  scanner.setBeanNameGenerator(this.nameGenerator);
  scanner.registerFilters();
  scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}

以上就是实例化MapperScannerConfigurer类的主要工作,总结起来就是扫描basePackage包下所有的mapper接口类,并将mapper接口类封装成为BeanDefinition对象,注册到spring的BeanFactory容器中。以下时序图不代表实际过程。

MyBatis源码解析

 

SqlSessionFactoryBean

MyBatis源码解析

 



Tags:MyBatis   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
本篇文章主要介绍了使用MyBatis框架完成数据库的增、删、改、查操作。准备工作运行schema.sql和data.sql脚本文件中的 SQL 语句创建t_user表并添加部分测试数据。schema.sql...【详细内容】
2022-07-15  Tags: MyBatis  点击:(0)  评论:(0)  加入收藏
简介MetaObject 是 MyBatis 中的反射工具类,用于获取和设置对象的属性值。示例List<Order> orders = new ArrayList<>(2);orders.add(new Order("1", "001", "美的电压力锅")...【详细内容】
2022-07-06  Tags: MyBatis  点击:(18)  评论:(0)  加入收藏
简介SqlRunner,一个非常实用的、用于操作数据库的工具类。该类对JDBC进行了很好的封装,结合SQL工具类,能够很方便地通过Java代码执行SQL语句并检索SQL执行结果。SqlRunner提供...【详细内容】
2022-07-05  Tags: MyBatis  点击:(19)  评论:(0)  加入收藏
1. If 语句需求:根据作者名字和博客名字来查询博客!如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名字来查询<!--需求1:根据作者名字和博客名字来查询博客!如果作者名...【详细内容】
2022-06-30  Tags: MyBatis  点击:(25)  评论:(0)  加入收藏
在进行持久层数据维护(新增或修改)的时候,我们通常需要记录一些非业务字段,比如:create_time、update_time、update_by、create_by等用来维护数据记录的创建时间、修改时间、修改...【详细内容】
2022-06-20  Tags: MyBatis  点击:(23)  评论:(0)  加入收藏
Mybatis 是 Java 中一个非常好用的数据库框架,这儿记录一下在使用过程中遇到的坑。官方中文文档地址:http://www.mybatis.org/mybatis-3/zh/getting-started.html1、在Mybatis...【详细内容】
2022-06-20  Tags: MyBatis  点击:(34)  评论:(0)  加入收藏
今天介绍一个 MyBatis - Plus 官方发布的神器:mybatis-mate 为 mp 企业级模块,支持分库分表,数据审计、数据敏感词过滤(AC算法),字段加密,字典回写(数据绑定),数据权限,表结构自动生成...【详细内容】
2022-06-17  Tags: MyBatis  点击:(43)  评论:(0)  加入收藏
1. Mybatis 存在的痛点我们知道 MyBatis 是一个基于 java 的持久层框架,它内部封装了 jdbc,极大提高了我们的开发效率。但是使用 Mybatis 开发也有很多痛点: 每个 Dao 接口都需...【详细内容】
2022-06-14  Tags: MyBatis  点击:(37)  评论:(0)  加入收藏
MybatisPlus是国产的第三方插件, 它封装了许多常用的CURDapi,免去了我们写mapper.xml的重复劳动,这里介绍了基本的整合SpringBoot和基础用法。2|0引入依赖在项目中pom文件引入m...【详细内容】
2022-05-05  Tags: MyBatis  点击:(52)  评论:(0)  加入收藏
一、前言我们在日常开发中经常使用ORM框架,比如Mybatis、tk.Mybatis、Mybatis-Plus。不过最广泛的还是Mybatis-Plus,我们的一些表,都会有创建时间、更新时间、创建人、更新人。...【详细内容】
2022-04-24  Tags: MyBatis  点击:(172)  评论:(0)  加入收藏
▌简易百科推荐
俗话说,天下大势,合久必分、分久必合。数据库领域同样如此。过去五十余年,数据库经历OLTP和OLAP两种需求漫长的融合-分离-再融合的过程。究其原因,数据库的发展始终与用户场景需...【详细内容】
2022-07-14  大数据在线    Tags:HTAP数据库   点击:(4)  评论:(0)  加入收藏
导读:Apache HBase(Hadoop Database),是一个基于Google BigTable论文设计的高可靠性、高性能、可伸缩的分布式存储系统。全文将围绕以下几个方面展开: HBase是什么 HBase社区的发...【详细内容】
2022-07-08  DataFunTalk    Tags:Apache HBase   点击:(10)  评论:(0)  加入收藏
TimescaleDB 超表TimescaleDB 中使用称为 hypertables 的数据表来存储数据。hypertable(超表)是与数据交互的主要点,因为它提供了可以通过标准 SQL 查询的标准表抽象。 在 Time...【详细内容】
2022-07-07  IT职业教育    Tags:TimescaleDB   点击:(19)  评论:(0)  加入收藏
使用like、between、in进行模糊查询select * from Studentswhrere 姓名 like &#39;张%&#39;通配符:_ 一个字符,% 任意长度的字符串,[] 括号中所指定的范围内的一个字符,[^]不在...【详细内容】
2022-07-07  新米米    Tags:数据库   点击:(15)  评论:(0)  加入收藏
oracle经常需要查数据库表空间大小,使用率,加表空间等,这里总结我经常使用的语句。一、数据表空间相关:查询所有表空间的使用情况:SELECT d.tablespace_name "Name", d.status "S...【详细内容】
2022-07-07  运维Danrtsey    Tags:数据表   点击:(15)  评论:(0)  加入收藏
导读:本文将介绍Apache IoTDB,它是一个基于开放数据格式的数据库。今天的介绍会围绕下面四点展开: Apache IoTDB 简介 时序文件格式 TsFile 基于开放文件的数据库架构 开源社区...【详细内容】
2022-07-06  DataFunTalk    Tags:时序数据库   点击:(22)  评论:(0)  加入收藏
一、问题 在好大夫在线内部,S3系统负责各业务方操作日志的集中存储、查询和管理。目前,该系统日均查询量数千万次,插入量数十万次。随着日志量的不断累积,主表已经达到数十亿,单...【详细内容】
2022-07-05  dbaplus社群    Tags:MongoDB   点击:(16)  评论:(0)  加入收藏
文丨刘雨琦马云曾说,数据是当下“最贵”的资产,若阿里巴巴不搞云计算,可能就会死掉。而数据库作为整个数据行业的基础软件,正在迎来它的春天。国产数据库的接连上市,为信创风口...【详细内容】
2022-07-05  光锥智能    Tags:数据库   点击:(20)  评论:(0)  加入收藏
导读:埋点数据是数据分析、推荐、运营的基础,低延时、稳定、高效的埋点数据流对提高用户体验有着非常重要的作用。而随着流量的增大,埋点的增多,在大流量场景下,埋点数据流的建设...【详细内容】
2022-07-05  DataFunTalk    Tags:数据   点击:(22)  评论:(0)  加入收藏
作为轻量级的本地存储方式,对于构建不依赖服务器的小型项目,用LowDB存储和管理数据是十分理想的选择。在Nodejs, Electron and browser等一些小型项目中经常能看到LowDB的身影...【详细内容】
2022-07-04    效能哥  Tags:数据库   点击:(30)  评论:(0)  加入收藏
站内最新
站内热门
站内头条