负载均衡

MyBatis

JDBC流程

(12条消息) 操作数据库的三种选择_mybatis gpa_A minor的博客-CSDN博客

Java DataBase Connectivity — java 数据库连接

1、加载驱动Driver,通过类加载机制 执行static代码 进行 xx

2、获取连接 – (连接池)

3、创建sql

4、statement参数设置

5、statement.execute()

6、处理返回集信息

Mybatis执行流程

(10条消息) 【MyBatis】执行原理(一):创建会话工厂(SqlSessionFactory) –配置解析源码分析_A minor的博客-CSDN博客

配置类解析

在这一步,我们主要完成了config配置文件、Mapper文件、Mapper接口上的注解的解析。
我们得到了一个最重要的对象Configuration,这里面存放了全部的配置信息,它在属性里面还有各种各样的容器。
最后,返回了一个DefaultSqlSessionFactory,里面持有了Configuration的实例。

在这里插入图片描述

(10条消息) 【MyBatis】执行原理(二):创建会话(SqlSession) 源码分析_sqlsession addbatch_A minor的博客-CSDN博客

创建会话

SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
ReuseExecutor:执行 update 或 select,以 sql 作为 key查找 Statement 对象,存在就使用,不存在就创建,用完后,不关闭 Statement 对象,而是放置于 Map 内,供下一次使用。简言之,就是重复使用Statement对象。

BatchExecutor:执行 update(没有select,JDBC 批处理不支持 select),将所有 sql 都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement 对象,每个Statement 对象都是 addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。

在这里插入图片描述

创建代理对象

(12条消息) 【MyBatis】执行原理(三):获取代理对象(MapperProxy) 源码分析_A minor的博客-CSDN博客

获得Mapper对象的过程,实质上是获取了一个 MapperProxy 的代理对象。MapperProxy 中有 sqlSession、mapperInterface、methodCache。

MapperProxy -> InvocationHandler (JDK动态代理 – 通过接口)

在这里插入图片描述

执行Executor

(12条消息) 【MyBatis】执行原理(四):Executor 执行SQL源码分析_sqlsessionfactory execute sql_A minor的博客-CSDN博客

在这里插入图片描述

使用

第一种

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void testMapper() throws IOException { 
// 全局配置文件(在resource目录下)
String resource = "mybatis-config.xml";
InputStream inputStream = Resources. getResourceAsStream(resource);
// 1.SqlSessionFatoryBuilder,根据全局配置文件创建 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = newSqlSessionFactoryBuilder().build(inputStream);

// 2.SqlSessionFactory,在SQLSessionFactory中拿到一个SqlSession
SqlSession session = sqlSessionFactory.openSession();

try {
// 3.SqlSession,通过statementId(nameSpace+id)找到具体sql,通过SqlSession方法执行
// 注:1.这种硬编码方式,可以给mapper.xml的namespace任意命名
// 2.selectOne 是 SqlSession 的方法(另外还有insert,delete),实际调用的是执行器 Executor 的 doQuery()
// 3.若是增删改,最后要提交 session.commit()
Blog blog = session.selectOne("blog.findUserById",1);
System.out.println(blog);
} finally {
// 4.关闭当前会话(连接)SqlSesion
session.close();
}
}

第二种

Mapper代理模式。通过接口来代理一个 mapper.xml 的执行,这个接口全路径必须跟 Mapper.xml 里面的 namespace 对应起来,方法也要跟 StatementID一一对应。

1
2
3
4
public interface BlogMapper {
Blog selectBlogById(int id);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void testMapper() throws IOException { 
String resource = "mybatis-config.xml";
InputStream inputStream = Resources. getResourceAsStream(resource);
// 1.SqlSessionFatoryBuilder,根据全局配置文件创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = newSqlSessionFactoryBuilder().build(inputStream);

// 2.SqlSessionFatory,在SQLSessionFactory中拿到一个SqlSession
SqlSession session = sqlSessionFactory.openSession();

try {
// 3.SqlSession,通过接口类拿到相应Mapper
// 注:1.mapper.xml的namespace=接口相对路径
// 2.mapper.xml的statementId=接口方法名
BlogMapper mapper = session.getMapper(BlogMapper.class);

// 4.Mapper,通过Mapper调用mapper.xml中的sql
// 相当于上面硬编码一行代码做的事,这里需要两行
Blog blog = mapper.selectBlogById(1);
System.out.println(blog);
} finally {
// 5.关闭当前会话(连接)SqlSesion
session.close();
}
}

#{}:占位符号,好处防止sql注入 ${}:sql拼接符号

(1条消息) MyBatis源码解析(二)——执行流程_mybati执行代码_爱看雲的雲的博客-CSDN博客

架构

Mybatis架构图

MyBatis层级架构图

1
2
3
4
5
6
7
8
9
10
11
12
MyBatis层级结构如上图所示,先简单介绍一下各个组件:

SqlSession:它是 MyBatis 的核心API,主要用来执行命令,获取映射,管理事务。接收开发人员提供的StatementId和参数,并返回操作结果。
Executor:执行器,是 MyBatis 调度的核心,负责SQL语句的生成以及查询缓存的维护。
StatementHandler:用于封装JDBC中Statement的操作。即声明SQL、设置参数、执行SQL等。通常一次SQL执行完之后就要关闭它,最多它的生命周期也不会超过一次事务的范围。
ParameterHandler:负责对用户传递过来的参数转换成JDBC Statement所需要的参数。
ResultSetHandler:负责将 JDBC 返回的ResultSet结果集对象转换成List类型的集合。
TypeHandler:用于 Java类型和 JDBC类型之间的转换。
BoundSql:表示动态生成的 SQL语句以及相应的参数信息。
MappedStatement:动态SQL 的封装,存储了一个 SQL所有的信息。
SqlSource:表示从 XML文件或注解方法读取的映射语句的内容,它创建将从用户接收的输入参数传递给数据库的SQL
Configuration:MyBatis 所有的配置信息都维持在 Configuration 对象中。

img

1
2
3
4
5
6
7
8
先通过 Resource 读取 MyBatis 配置文件(mybatis-config.xml),将其转换成输入流。
将输入流传入 SqlSession工厂构建器(SqlSessionFactoryBuilder),调用 build方法传入输入流生成 XML配置文件构造器(XMLConfigBuilder)。
XMLConfigBuilder 会解析 mybatis-config.xml 文件中的所有标签,其中最重要的就是解析标签,加载的所有配置都会保存在 Configuration 对象中。
遍历 mapper 标签,根据资源标识类型进行不同的操作,对于资源标识为 package 和 class 的,使用configurationaddMappersaddMapper 方法解析;资源标识为 resourceurl 的,新建一个 mapper映射文件构造器(XMLMapperBuilder)去解析对应的 mapper 映射文件。
先看 Configuration 对象的 addMappers,调用 mapper 注册器(MapperRegistry)的 addMappers 方法。mapper 注册器解析流程为:资源标识为 package,获取配置包下的所有 mapper 映射文件,再获取文件中指向的 mapper 接口 Class 对象集合,遍历解析 Class 对象。资源标识为 class,则直接解析 Class 对象。调用 mapper 注解构造器(MapperAnnotationBuilder)的 parse 方法解析 Class 对象。
mapper注解构造器主要分为两个步骤,一是调用 XMLMapperBuilder 解析 mapper接口对应的 mapper映射文件,这里和解析资源标识为 resourceurlmapper 标签,殊途同归了;二是解析 mapper 接口方法上的SQL语句,生成对应的 MappedStatement 对象。
mapper映射文件构造器先解析 mapper 映射文件中的 resultMapsql 等标签,然后调用 mapper 映射文件声明构造器(XMLStatementBuilder)解析CRUD标签(selectinsertupdatedelete),遍历这些标签,然后封装为SqlSource,最后调用 MappedStatement 创建助手(MapperBuilderAssistant)封装成MappedStatement 对象,一个 CRUD 标签生成一个 MappedStatement 对象。
最后将 MappedStatement 添加到 Configuration 对象的 mappedStatements 属性中。

img

img

Mybatis关键类

对象 相关对象 作用
Configuration MapperRegistry TypeAliasRegistry TypeHandlerRegistry 包含了 MyBatis 的所有的配置信息
SqlSession SqlSessionFactory DefaultSqlSession 对操作数据库的增删改査的 API 进行了封装,提供给应用层使用
Executor BaseExecutor SimpleExecutor BatchExecutor ReuseExecutor MyBatis 执行器 , 是 MyBatis 调度的核心, 负责SQL语句的生成和查询缓存的维护
StatementHandler BaseStatementHandler SimpleStatementHandler PreparedStatementHandler CallableStatementHandler 封装了 JDBC Statement 操作,负责对 JDBC Statement 的操作,
如设置参数、将Statement结果转换为List集合
ParameterHandler DefaultParameterHandler 把用户传递的参数转换成 JDBC Statement 所需要的参数
MapperProxy MapperProxyFactory 代理对象,用于代理 Mapper 接口方法
MappedStatement SqlSource BoundSql MappedStatement 维护了一条 〈select 丨 update丨 delete丨 insert〉节点的封装,包括了 SQL 信息、入参信息、出参信息

Executor

Executor类图

BaseExecutor:

ReuseExecutor: 缓存sql,直接再次使用

SimpleExecutor : 普通的Executor 准备Handler,

​ – 获取连接,准备statement,调用Hanlder,

​ – 调用Handler的query()

StatementHandler

img

Statement

PrepareStatementWrapper

image-20230330175452764

ClientPraparedStatement

image-20230330175508388

插件plugin

增强Executor

在初始 Executor的时候,会调用Configuration的newExecutor() 方法,执行插件执行链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ## Configuraiton
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? this.defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Object executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}

if (this.cacheEnabled) {
executor = new CachingExecutor((Executor)executor);
}
// 初始化
Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
return executor;
}

增强Handler

在初始化Handler的时候,会进入Configuration中,调用newStatementHandler() ,其中会调用 插件执行链进行增强操作

1
2
3
4
5
6
7
8
9
10
// Executor  - doQuery() / doQueryCursor()
protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
Configuration configuration = ms.getConfiguration();
/// 初始化 Handler调用插件
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, (ResultHandler)null, boundSql);
Statement stmt = this.prepareStatement(handler, ms.getStatementLog());
Cursor<E> cursor = handler.queryCursor(stmt);
stmt.closeOnCompletion();
return cursor;
}
1
2
3
4
5
6
7
8
// Configuration
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
StatementHandler statementHandler =
// 调用插件 -- 增强Handler
(StatementHandler)this.interceptorChain.pluginAll(statementHandler);
return statementHandler;
}

img

Spring整合MyBatis

mybatis关键对象,SqlSessionFactory(创建SqlSession), SqlSession, MapperProxy(接口代理), Configuration(配置类封装)

步骤:

1、将工厂注入到容器 —- 通过实现 FactoryBean实现自定义 工厂创建逻辑 (需要加载Configuration 等)

2、SqlSession的默认实现DefalutSqlSession 生命周期为 (请求 / 连接),线程不安全,需要通过 SqlSessionTemplate,每一次请求创建一个DefalutSqlSession ,然后调用对应方法

3、Mapper接口注入为BeanDefinition,通过MapperScanner实现 扫描类路径,逻辑类似于 AnnotatedApplicationContext的 ClassPathBeanDefinitionScanner,用于类路径扫描并加载为BeanDefinition

4、Mapper是接口,需要生成对应的代理对象,Spring 中实现FactoryBean,为每个Mapper的BeanDefinition创建能够调用方法的代理对象MaperProxy

对象 生命周期
SqlSessionTemplate Spring 中 SqlSession 的替代品,是线程安全的,通过代理的方式调用 DefaultSqlSession 的方法
SqlSessionInterceptor(内部类) 代理对象,用来代理 DefaultSqlSession,在 SqlSessionTemplate 中使用
SqlSessionDaoSupport 用于获取 SqlSessionTemplate,只要继承它即可 MapperFactoryBean 注册到 IOC 容器中替换接口类,继承了 SqlSessionDaoSupport 用来获取 SqlSessionTemplate,因为注入接口的时候,就会调用它的 getObject()方法
SqlSessionHolder 控制 SqlSession 和事务
接口 方法 作用
FactoryBean getObject() 返回由 FactoryBean 创建的 Bean 实例
InitializingBean afterPropertiesSet() bean 属性初始化完成后添加操作
BeanDefinitionRegistryPostProcessor postProcessBeanDefinitionRegistry() 注入 BeanDefination 时添加操作

负载均衡
http://example.com/2023/06/01/分布式组件+常见组件/MyBatis/
作者
where
发布于
2023年6月1日
许可协议