MyBatis核心 - SqlSession如何通过Mapper接口生成Mapper对象
书接上文 MyBatis – 执行流程
我们通过SqlSession获取到了UserMapper对象,代码如下:
// 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();// 执行查询操作
try {// 获取映射器接口UserMapper userMapper = sqlSession.getMapper(UserMapper.class);// 调用映射器接口的方法执行查询操作List<User> users = userMapper.getAllUsers();// 处理查询结果for (User user : users) {System.out.println(user.getId() + " - " + user.getName());}
} finally {// 关闭SqlSessionsqlSession.close();
}
我们看到,往 sqlSession.getMapper 传入UserMapper接口后,得到的是一个 userMapper 对象,这是怎么做到的呢?
查看SqlSession源码发现,SqlSession有两个实现类,在正常情况下使用的当然就是默认的 DefaultSqlSession

DefaultSqlSession中有以下属性,其中最重要的两个属性是 Configuration 和 Executor
private final Configuration configuration;private final Executor executor;private final boolean autoCommit;private boolean dirty;private List<Cursor<?>> cursorList;
什么是Configuration
查看SqlSessionFactory的默认实现类 DefaultSqlSessionFactory 发现,其内部只有一个属性,就是Configuration

那DefaultSqlSessionFactory的Configuration从哪里获取到的值呢?那就得追踪到SqlSessionFactoryBuilder,在上文中,我们通过如下方式创建SqlSessionFactory对象
// 创建 SqlSessionFactoryBuilder 对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();// 加载配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");// 构建 SqlSessionFactory 对象
SqlSessionFactory sqlSessionFactory = builder.build(inputStream);// 关闭配置文件流
inputStream.close();
查看SqlSessionFactoryBuilder的源码(实际上SqlSessionFactoryBuilder 的所有方法就是多个重载的build,以下仅展示我们使用到的),如下:
public SqlSessionFactory build(Reader reader) {return build(reader, null, null);}public SqlSessionFactory build(Reader reader, String environment, Properties properties) {try {XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);return build(parser.parse());} catch (Exception e) {throw ExceptionFactory.wrapException("Error building SqlSession.", e);} finally {ErrorContext.instance().reset();try {reader.close();} catch (IOException e) {// Intentionally ignore. Prefer previous error.}}}
我们调用了 build(Reader reader) 方法,而该方法内部调用了 build(Reader reader, String environment, Properties properties) 方法,其中只传入了一个reader参数,另外两个参数是null,reader参数保存的是核心配置文件 mybatis-config.xml 的信息
通过reader获取到XMLConfigBuilder对象,我们不知道这个对象到底是什么,但是我们知道它的作用仍然是保存 mybatis-config.xml 的信息
接着return 了 build(parser.parse()) 方法,该方法源码如下
public SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config);}
发现调用了DefaultSqlSessionFactory的构造方法,并传入携带 mybatis-config.xml 信息的config
该构造方法源码如下:
public DefaultSqlSessionFactory(Configuration configuration) {this.configuration = configuration;}
将config赋值给了 DefaultSqlSessionFactory对像 的 configuration 属性
然后我们调用如下代码获取到SqlSession 对象(即 DefaultSqlSession对象),其中sqlSessionFactory引用指向的就是DefaultSqlSessionFactory对像
// 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
查看 DefaultSqlSessionFactory 中的 openSession() 方法源码,如下:
@Overridepublic SqlSession openSession() {return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}
发现调用了openSessionFromDataSource方法,继续跟踪
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {final Environment environment = configuration.getEnvironment();final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);final Executor executor = configuration.newExecutor(tx, execType);return new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);} finally {ErrorContext.instance().reset();}}
我们终于看到了真正创建SqlSession的方法,该方法调用了 DefaultSqlSession 的构造方法,并直接传入DefaultSqlSessionFactory 的 configuration,到此我们就知道了,DefaultSqlSession 中的 Configuration 属性就是记录着 mybatis-config.xml 的信息,其中包含着 数据源信息 和 Mapper 映射文件地址等
executor
从上面我们知道,创建 DefaultSqlSession 的方法是 DefaultSqlSessionFactory 对象中的 openSessionFromDataSource方法,源码如下:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {final Environment environment = configuration.getEnvironment();final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);final Executor executor = configuration.newExecutor(tx, execType);return new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);} finally {ErrorContext.instance().reset();}}
我们发现在调用 DefaultSqlSession 构造方法时,不仅传入了 configuration 对象,还传入了executor对象,并且是通过 configuration 的 newExecutor 方法获得,查看 newExecutor 方法源码
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {executorType = executorType == null ? defaultExecutorType : executorType;executorType = executorType == null ? ExecutorType.SIMPLE : executorType;Executor 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 (cacheEnabled) {executor = new CachingExecutor(executor);}executor = (Executor) interceptorChain.pluginAll(executor);return executor;}
大概上可以看出,该方法是通过 executorType 参数来构造不同类型的构造器,查看ExecutorType源码,发现是个枚举类
public enum ExecutorType {SIMPLE, REUSE, BATCH
}
其中的枚举值的含义如下:
- SIMPLE:简单执行器,用于执行简单的查询操作,不支持事务的提交和回滚。
- REUSE:重用执行器,用于执行重复的查询操作,可以重用缓存的查询结果,提高性能。
- BATCH:批处理执行器,用于执行批量的数据操作,如插入、更新和删除操作。它可以一次执行多个SQL语句,并支持事务的提交和回滚。
当我们调用 DefaultSqlSessionFactory 的无参 openSession 方法时,而openSession 方法又调用openSessionFromDataSource方法,并传入的参数configuration.getDefaultExecutorType(),我们不断跟踪.getDefaultExecutorType方法,发现最后返回的是ExecutorType. SIMPLE ,而 openSessionFromDataSource 又把这个参数传给了newExecutor 方法,因此此时的executor是简单执行器对象,创建的是 SimpleExecutor 对象
我们查看 SimpleExecutor 类的源码发现,其继承了 BaseExecutor,如下 ,实际上每个Executor都继承了BaseExecutor
public class SimpleExecutor extends BaseExecutor
查看 BaseExecutor 源码(源码太多不加入文章),发现其实现了Executor接口,Executor接口中的方法几乎覆盖了对数据库的所有操作,如下,包括事务的操作和增删改查的操作
public interface Executor {ResultHandler NO_RESULT_HANDLER = null;int update(MappedStatement ms, Object parameter) throws SQLException;<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;List<BatchResult> flushStatements() throws SQLException;void commit(boolean required) throws SQLException;void rollback(boolean required) throws SQLException;CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);boolean isCached(MappedStatement ms, CacheKey key);void clearLocalCache();void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);Transaction getTransaction();void close(boolean forceRollback);boolean isClosed();void setExecutorWrapper(Executor executor);}
因此SqlSession依靠Executor属性就能完成所有的SQL操作
最后看 SqlSession 是如何生成 Mapper
查看 DefaultSqlSession 中的 getMapper方法 源码
@Overridepublic <T> T getMapper(Class<T> type) {return configuration.getMapper(type, this);}
传入了Mapper接口的类对象,以及 DefaultSqlSession 本身
追踪 getMapper 方法,最后来到了 MapperRegistry 类的 getMapper 方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);if (mapperProxyFactory == null) {throw new BindingException("Type " + type + " is not known to the MapperRegistry.");}try {return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause: " + e, e);}}
该方法的大致过程就是 通过动态代理生成了 Mapper 实例的代理对象,并且这个代理对象还“整合”进了sqlSession对象
当调用代理对象的Mapper接口方法时,代理对象将拦截这个方法调用,并获取对应的方法信息(参数返回值等),然后交由 sqlSession对象 对象中的 Executor对象,执行真正的SQL操作,而Executor对象 在执行SQL时需要用到的信息就来之DefaultSqlSession对象中的configuration属性(包括数据源信息和Mapper映射文件信息等)
相关文章:
MyBatis核心 - SqlSession如何通过Mapper接口生成Mapper对象
书接上文 MyBatis – 执行流程 我们通过SqlSession获取到了UserMapper对象,代码如下: // 获取SqlSession对象 SqlSession sqlSession sqlSessionFactory.openSession();// 执行查询操作 try {// 获取映射器接口UserMapper userMapper sqlSession.get…...
【Git】标签管理与Git Flow模型
目录 一、操作标签 二、推送标签 三、删除标签 四、Git Flow模型分支设计 一、操作标签 git tag # 查看有哪些标签 git tag [name] # 给最近一次commit打标签 git tag [name] [commitID] #给指定的commit打标签 git tag -a [name] -m desc # 打标签并添加描述 二、推送标…...
日志分析和流量分析
目录 [陇剑杯 2021]日志分析(问1) [陇剑杯 2021]日志分析(问2) [陇剑杯 2021]日志分析(问3) [陇剑杯 2021]简单日志分析(问1) [陇剑杯 2021]简单日志分析(问3&#…...
typescript基础之关键字type
TypeScript的type是一个关键字,用来定义一个类型别名https://www.typescripttutorial.net/typescript-tutorial/typescript-types/。类型别名可以给一个已有的类型起一个新的名字,或者组合多个类型成为一个新的类型。例如: // 给string类型起…...
无人机航测技术有何特点?主要应用在哪些方面?
无人机航测是航空摄影测量的一种,主要面向低空遥感领域,具有成本低、快速高效、适用范围广等特点。目前,无人机航测主要应用于地形测绘、城市数字化建设、工程建设等方面。 无人机航测技术的特点 1、作业成本低 传统的人工测量技术主要利用…...
24届近5年杭州电子科技大学自动化考研院校分析
今天给大家带来的是杭州电子科技大学控制考研分析 满满干货~还不快快点赞收藏 一、杭州电子科技大学 学校简介 杭州电子科技大学(Hangzhou Dianzi University),简称“杭电”,位于杭州市,是浙江省人民政…...
调整vscode
调整vscode 连wifi linux连接wifi...
Spring xml 方式整合mybatis 第三方框架
Spring整合MyBatis MyBatis提供了mybatis-spring.jar专门用于两大框架的整合。 ①:第一步: 导入MyBatis整合Spring的相关坐标; <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency><groupI…...
RabbitMQ(二) - RabbitMQ与消息发布确认与返回、消费确认
RabbitMQ消息确认 SpringBoot与RabbitMQ整合后,对RabbitClient的“确认”进行了封装、使用方式与RabbitMQ官网不一致; 消息发布确认 生产者给交换机发送消息后、若是不管了,则会出现消息丢失; 解决方案1: 交换机接受…...
操作指南 | 如何使用Chainlink喂价功能获取价格数据
Chainlink的去中心化预言机网络中的智能合约包含由运行商为其他智能合约(DApps)使用或截取所持续更新的实施价格数据。其中有两个主要架构:喂价和基础要求模型。此教程将会展现如何在Moonbeam、Moonriver或是Moonbase Alpha测试网上使用喂价功…...
Pandaer的iPhone手机壳
哇塞,Pandaer的设计太棒了!手机壳的花样多到让我眼花缭乱,好多系列设计都很有意思,让人有集齐的冲动。我最近入手了几个iPhone的手机壳,它有亮色和透明的款式,亮色的壳内部也是亮的,因为手机壳全…...
将自己的网站免费发布到互联网上【无需公网IP】
作者简介: 辭七七,目前大一,正在学习C/C,Java,Python等 作者主页: 七七的个人主页 文章收录专栏: 七七的闲谈 欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖…...
浅谈 Python中if __name__ == ‘__main__‘:的工作原理
为了理解if __name__ __main__:的工作原理,我们需要先了解Python中的特殊变量__name__。 每个Python模块都有一个内置的变量__name__。这个变量的值取决于如何执行模块: 如果模块是被直接运行的(例如,你使用命令python myscrip…...
【力扣】344. 反转字符串 <首尾指针>
【力扣】344. 反转字符串 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 示例 1: 输入:s …...
Kubectl 详解
目录 陈述式资源管理方法:项目的生命周期:创建-->发布-->更新-->回滚-->删除声明式管理方法: 陈述式资源管理方法: kubernetes 集群管理集群资源的唯一入口是通过相应的方法调用 apiserver 的接口kubectl 是官方的CL…...
华为OD面试记录
The experience of applying for software test engineer(Dispatcher) 记录保存 招聘岗位: 测试工程师 Base:西安 华为面试流程如下: 流程名内容机试三题,总分400分,最后一道题200分人力资源面试询问私人问题,不谈薪资一面技术面二面技术面主管问项目…...
电源控制--品质因素Q值全解
什么是品质因素Q值? 在电源控制中,品质因素 Q 值通常用于描述电源滤波器的性能。电源滤波器用于减小电源中的噪声和干扰,以提供干净稳定的电源供应给电子设备。 品质因素 Q 值在电源滤波器中表示滤波器的带宽和中心频率之比,用于…...
实际工作中通过python+go-cqhttp+selenium实现自动检测维护升级并发送QQ通知消息(程序内测)
说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 首先,今年比较忙没有多余时间去实操创作分享文章给大家,那就给大家分享下博主在实际工作中的一点点内容吧,就当交…...
EC200 CAT1 拨号PPP
**硬件支持型号 点击 查看 硬件支持 详情** DTU701 产品详情 DTU702 产品详情 DTU801 产品详情 DTU802 产品详情 DTU902 产品详情 G5501 产品详情 目前 DTU系列 产品,WIFI4G拨号 ,默认开机自启动拨号。 WIFI 只需要 根据现场 修改SSID热点和密码…...
外网通过ipv6访问家里设备
想从公司访问家里的设备,比较轻松方便的,用向日葵也可以远程。但是家里电脑比较old的了,向日葵开起来,占用内存挺大的,想尝试windows自带的“mstsc”,所以硬着头皮搞ipv6. (重点提示࿱…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
