Mybatis架构原理和机制,图文详解版,超详细!
Mybatis架构原理和机制,图文详解版,超详细!
MyBatis是Java生态中非常著名的一款ORM框架,目前在一线互联网大厂中应用广泛。熟练使用MyBatis开发已经是一项非常基本的技能,同时大厂也更希望自己的开发人员深入了解MyBatis框架的原理和核心实现。理解MyBatis原理,阅读MyBatis核心源码,这样更有利于提高职场竞争力。
Mybatis架构设计
我们把Mybatis的功能架构分为三层:
- API接口层:主要就是和数据库交互,提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库,接口层一接收到调用请求,就会调用数据处理层来完成具体的数据处理。
以使用Mapper接口为例,将配置文件中的每一个节点抽象为一个Mapper接口,这个接口中声明的方法和跟Mapper.xml中的节点项对应。id值对应方法名称,parameterType值对应方法的入参类型,而resultMap值则对应返回值类型。
配置好后,MyBatis会根据接口声明的方法信息,通过动态代理机制生成一个Mapper实例,当调用接口方法时,根据这个方法的方法名和参数类型,确定Statement Id,底层还是通过SqlSession.select/update(“statementId”, parameter)等来实现对数据库的操作。
- 数据处理层:可以说是MyBatis的核心,负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等,它主要的目的是根据调用的请求完成一次数据库操作。
从大的方面上讲,它要完成两个功能:
- 通过传入参数构建动态SQL语句
- SQL语句的执行以及封装查询结果集
通过传入参数构建动态SQL语句:动态语句生成能够说是MyBatis框架很是优雅的一个设计,MyBatis经过传入的参数值,使用Ognl来动态地构造SQL语句,使得MyBatis有很强的灵活性和扩展性。
参数映射:指的是对于java数据类型和jdbc数据类型之间的转换,这里有包括两个过程:
- 查询阶段
- 查询结果集转换阶段
查询阶段要将java类型的数据,转换成jdbc类型的数据,经过preparedStatement.setXXX()来设值;另外一个,就是对resultset查询结果集的jdbcType数据转换成java数据类型。
SQL语句的执行以及封装查询结果集:动态SQL语句生成以后,MyBatis将执行SQL语句,并将可能返回的结果集转换成List列表。
MyBatis在对结果集的处理中,支持结果集关系一对多和多对一的转换,而且有两种支持方式,一种为嵌套查询语句的查询,还有一种是嵌套结果集的查询。
- 基础支撑层:是整个MyBatis框架的地基,负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件,为上层的数据处理层提供最基础的支撑。
缓存机制
数据库是实践生成中非常核心的存储,很多业务数据都会落地到数据库,所以数据库性能的优劣直接影响了上层业务系统的优劣。我们很多线上业务都是读多写少的场景,在数据库遇到瓶颈时,缓存是最有效、最常用的手段之一,正确使用缓存可以将一部分数据库请求拦截在缓存这一层,这就能够减少一部分数据库的压力,提高系统性能。
除了使用Redis、Memcached等外置的第三方缓存以外,持久化框架一般也会自带内置的缓存,例如,MyBatis就提供了一级缓存和二级缓存,具体实现位于基础支撑层的缓存模块中。
反射工具
该模块对Java原生的反射进行了良好的封装,提供了更加简洁易用的API,方便上层使调用,并且对反射操作进行了一系列优化,例如缓存了类的元数据,提高了反射操作的性能。
类型转换
类型转换模块提供了两个主要功能,一个功能是别名机制,MyBatis为了简化配置文件提供了别名机制。
另一个功能是实现JDBC类型与Java类型之间的转换,该功能在为SQL语句绑定实参以及映射查询结果集时都会涉及。
日志
提供详细的日志输出信息,并且能够集成多种日志框架,其日志模块的一个主要功能就是集成第三方日志框架。
资源加载
资源加载模块主要是对类加载器进行封装,确定类加载器的使用顺序,并提供了加载类文件以及其他资源文件的功能。
解析器
解析器模块主要提供两个功能,一个功能是对XPath进行封装,为MyBatis初始化时解析mybatis-config.xml配置文件以及映射配置文件提供支持。
另一个功能是为处理动态SQL语句中的占位符提供支持。
事务管理
持久层框架一般都会提供一套事务管理机制实现数据库的事务控制,MyBatis对数据库中的事务进行了一层简单的抽象,提供了简单易用的事务接口和实现。
一般情况下,Java项目都会集成Spring,并由Spring框架管理事务。
Binding
在调用SqlSession相应方法执行数据库操作时,需要指定映射文件中定义的SQL节点,如果出现拼写错误,我们只能在运行时才能发现相应的异常。
为了尽早发现这种错误,MyBatis通过Binding模块将用户自定义的Mapper接口与映射配置文件关联起来,系统可以通过调用自定义Mapper接口中的方法执行相应的SQL语句完成数据库操作,从而避免上述问题。
需要注意的是,无须编写自定义Mapper接口的实现,具体的Mapper接口实现MyBatis会自动为其创建动态代理对象。
数据源
对于ORM框架而言,数据源的组织是一个非常重要的一部分,这直接影响到框架的性能问题。
数据库连接是一项有限的昂贵资源,一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的性能低下。
数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由应用程序动态地对池中的连接进行申请、使用和释放。
打开Mybatis源码找到datasource包下就可以看到连接池的实现,如下图所示:
Mybatis核心执行流程
mybatis的总体执行流程,总体如下六大步骤:
- MyBatis配置文件
config.xml:配置了全局配置文件,配置了MyBatis的运行环境等信息。
mapper,xml:sql的映射文件,配置了操作数据库的sql语句,此文件需在config.xml中加载。
- SqlSessionFactory
通过MyBatis环境等配置信息构造SqlSessionFactory(会话工厂)。
- SqlSession
通过会话工厂创建SqlSession(会话),对数据库进行增删改查操作。
- Exector执行器
MyBatis底层自定义了Exector执行器接口来具体操作数据库,Exector接口有两个实现,一个基本执行器(默认),一个是缓存执行器,SqlSession底层是通过Exector接口操作数据库。
- MappedStatement
MyBatis的一个底层封装对象,它包装了MyBatis配置信息与sql映射信息等。mapper.xml中的insert/select/update/delete标签对应一个MappedStatement对象。标签的id就是MappedStatement的id。
MappedStatement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo、Executor通过MappedStatement在执行sql前将输入的Java对象映射至sql中,输入参数映射就是JDBC编程对preparedStatement设置参数。
MappedStatement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过MappedStatement在执行sql后将输出结果映射至Java对象中,输出结果映射就是JDBC编程对结果的解析处理过程。
到此,我们就将Mybatis的架构从全局上做了一个拆解。后续,可以重点分析其核心源码,这样先全局、再局部,更有利于掌握其核心原理实现。
以上,是Mybatis架构原理和机制的详细解析。