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. (重点提示࿱…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
第三周 Day 3 🎯 今日目标 理解类(class)和对象(object)的关系学会定义类的属性、方法和构造函数(init)掌握对象的创建与使用初识封装、继承和多态的基本概念(预告) &a…...
C++实现分布式网络通信框架RPC(2)——rpc发布端
有了上篇文章的项目的基本知识的了解,现在我们就开始构建项目。 目录 一、构建工程目录 二、本地服务发布成RPC服务 2.1理解RPC发布 2.2实现 三、Mprpc框架的基础类设计 3.1框架的初始化类 MprpcApplication 代码实现 3.2读取配置文件类 MprpcConfig 代码实现…...
深入解析光敏传感技术:嵌入式仿真平台如何重塑电子工程教学
一、光敏传感技术的物理本质与系统级实现挑战 光敏电阻作为经典的光电传感器件,其工作原理根植于半导体材料的光电导效应。当入射光子能量超过材料带隙宽度时,价带电子受激发跃迁至导带,形成电子-空穴对,导致材料电导率显著提升。…...
