Mybatis【2.2】-- Mybatis创建sqlSession源码分析的几点疑问?
发布时间 - 2025-07-20 00:00:00 点击率:次1.为什么我们使用SQLSessionFactoryBuilder的时候不需要自己关闭流?
我们看我们的代码:
代码语言:javascript代码运行次数:0运行复制public class StudentDaoImpl implements IStudentDao { private SqlSession sqlSession; public void insertStu(Student student) { try { InputStream inputStream; inputStream = Resources.getResourceAsStream("mybatis.xml"); SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream); sqlSession=sqlSessionFactory.openSession(); sqlSession.insert("insertStudent",student); sqlSession.commit(); } catch (IOException e) { e.printStackTrace(); }finally { if(sqlSession!=null){ sqlSession.close(); } } }}当我们使用inputStream = Resources.getResourceAsStream("mybatis.xml");的时候,我们并需要去关闭inputstream,我们可以查看源码,首先看到SqlSessionFactoryBuilder().build()这个方法:
// 将inputstream传递进去,调用了另一个分装的build()方法 public SqlSessionFactory build(InputStream inputStream) { return this.build((InputStream)inputStream, (String)null, (Properties)null); }跟进去,我们再来看另一个build方法,里面有一个finally模块,无论怎么样都会执行close方法,所以这就是为什么我们在使用的时候为什么不用关闭inputstream的原因:因为这个流是在finally代码块中被关闭了。
代码语言:javascript代码运行次数:0运行复制 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { SqlSessionFactory var5; try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); var5 = this.build(parser.parse()); } catch (Exception var14) { throw ExceptionFactory.wrapException("Error building SqlSession.", var14); } finally { ErrorContext.instance().reset(); try { // 关闭流 inputStream.close(); } catch (IOException var13) { ; } } return var5; }2. Sqlsession是如何创建的?语句里面执行代码:使用SQLSessionFactory去打开一个session,这里的session我们可以初步理解为一个sql的会话,类似我们想要发信息给别人,肯定需要打开一个和别人的会话。
sqlSession=sqlSessionFactory.openSession();
我们需要查看源码,我们发现opensession是sqlSessionFactory的一个接口方法,sqlSessionFactory是一个接口。
代码语言:javascript代码运行次数:0运行复制public interface SqlSessionFactory { // 在这里只贴出了一个方法,其他的就不贴了 SqlSession openSession(); }idea选中该方法,ctrl + alt +B,我们可以发现有DefaultSqlSessionFactory,和SqlSessionManager这两个类实现了SqlSessionFactory这个接口
那么我们需要跟进去DefaultSqlSessionFactory这个类的openSesseion方法,在里面调用了一个封装好的方法:openSessionFromDataSource()
代码语言:javascript代码运行次数:0运行复制 public SqlSession openSession() { return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false); }当然在DefaultSqlSessionFactory这个类里面还有一个方法,参数是autoCommit,也就是可以指定是否自动提交:
public SqlSession openSession(boolean autoCommit) { return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, autoCommit); }我们再跟进去源码,我们会发现有一个参数是autoCommit,也就是自动提交,我们可以看到上一步传值是false,也就是不会自动提交,通过configuration(主配置)获取environment(运行环境),然后通过environment(环境)开启和获取一个事务工厂,通过事务工厂获取事务对象Transaction,通过事务对象创建一个执行器executor,Executor是一个接口,实现类有比如SimpleExecutor,BatchExecutor,ReuseExecutor,所以我们下面代码里的execType,是指定它的类型,生成指定类型的Executor,把引用给接口对象,有了执行器之后就可以return一个DefaultSqlSession对象了。
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; DefaultSqlSession var8; try { // configuration是主配置文件 Environment environment = this.configuration.getEnvironment(); // 获取事务工厂,事务管理器可以使jdbc之类的 TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment); // 获取事务对象Transaction tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); // 通过事务对象创建一个执行器executor Executor executor = this.configuration.newExecutor(tx, execType); // DefaultSqlSession是SqlSession实现类,创建一个DefaultSqlSession并返回 var8 = new DefaultSqlSession(this.configuration, executor, autoCommit); } catch (Exception var12) { this.closeTransaction(tx); throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12); } finally { ErrorContext.instance().reset(); } return var8; }我们跟var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);这句代码,我们这是初始化函数赋值于各个成员变量,我们发现里面有一个dirty成员,这是干什么用的呢?从名字上来讲我们理解是脏的,这里既然设置为false,那就是不脏的意思。那到底什么是脏呢?脏是指内存里面的数据与数据库里面的数据存在不一致的问题,如果一致就是不脏的后面会解释这个dirty的作用之处,到这里一个SqlSession就创建完成了。
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) { this.configuration = configuration; this.executor = executor; this.dirty = false; this.autoCommit = autoCommit; }3.增删改是怎么执行的我们使用到这句代码:
代码语言:javascript代码运行次数:0运行复制sqlSession.insert("insertStudent",student);我们发现同样是接口方法,上面我们知道SqlSession其实是DefaultSqlSession所实现的接口,那么我们跟进去DefaultSqlSession的insert()方法,我们发现其实inset方法底层也是实现了update这个方法,同样的delete方法在底层也是调用了update这个方法,增,删,改本质上都是改。
代码语言:javascript代码运行次数:0运行复制public int insert(String statement, Object parameter) { return this.update(statement, parameter);}public int update(String statement) { return this.update(statement, (Object)null);}那么我们现在跟进去update方法中,dirty变成ture,表明即将改数据,所以数据库数据与内存中数据不一致了,statement是我们穿过来的id,这样就可以通过id拿到statement的对象,然后就通过执行器执行修改的操作:
代码语言:javascript代码运行次数:0运行复制 public int update(String statement, Object parameter) { int var4; try { // dirty变成ture,表明数据和数据库数据不一致,需要更新 this.dirty = true; // 通过statement的id把statement从配置中拿到映射关系 MappedStatement ms = this.configuration.getMappedStatement(statement); // 执行器执行修改的操作 var4 = this.executor.update(ms, this.wrapCollection(parameter)); } catch (Exception var8) { throw ExceptionFactory.wrapException("Error updating database. Cause: " + var8, var8); } finally { ErrorContext.instance().reset(); } return var4; }4.SqlSession.commit()为什么可以提交事务(transaction)?首先,我们使用到的源码,同样选择DefaultSqlSession这个接口的方法,我们发现commit里面调用了另一个commit方法,传进去一个false的值:
代码语言:javascript代码运行次数:0运行复制 public void commit() { this.commit(false); }我们跟进去,发现上面传进去的false是变量force,里面调用了一个isCommitOrRollbackRequired(force)方法,执行的结果返回给commit方法当参数。
public void commit(boolean force) { try { this.executor.commit(this.isCommitOrRollbackRequired(force)); // 提交之后dirty置为false,因为数据库与内存的数据一致了。 this.dirty = false; } catch (Exception var6) { throw ExceptionFactory.wrapException("Error committing transaction. Cause: " + var6, var6); } finally { ErrorContext.instance().reset(); }}我们跟进去isCommitOrRollbackRequired(force)这个方法,这个方法从命名上是需要提交还是回滚的意思。在前面我们知道autoCommit是false,那么取反之后就是true,关于dirty我们知道前面我们执行过insert()方法,insert的底层调用了update方法,将dirty置为true,表示即将修改数据,那我们知道!this.autoCommit && this.dirty的值就是true,那么就短路了,所以整个表达式的值就是true。
private boolean isCommitOrRollbackRequired(boolean force) { return !this.autoCommit && this.dirty || force;}返回上一层的,我们知道this.isCommitOrRollbackRequired(force)的返回值是true。
this.executor.commit(this.isCommitOrRollbackRequired(force));
跟进去commit方法,这个commit方法是一个接口方法,实现接口的有BaseExecutor,还有CachingExecutor,我们选择BaseExecutor这个接口实现类:
代码语言:javascript代码运行次数:0运行复制// required是truepublic void commit(boolean required) throws SQLException { // 如果已经 关闭,那么就没有办法提交,抛出异常 if (th
is.closed) { throw new ExecutorException("Cannot commit, transaction is already closed"); } else { this.clearLocalCache(); this.flushStatements(); // 如果required是true,那么就提交事务 if (required) { this.transaction.commit(); } }}5.为什么sqlsession关闭就不需要回滚了?假如我们在上面已经提交过了,那么dirty的值就为false。我们使用的是sqlSession.close();,跟进去源码,同样是接口,我们跟DefaoultSqlsession的方法,同样调用了isCommitOrRollbackRequired()这个方法:
public void close() { try { this.executor.close(this.isCommitOrRollbackRequired(false)); this.dirty = false; } finally { ErrorContext.instance().reset(); } }我们跟进去isCommitOrRollbackRequired(false)这个方法,我们知道force传进来的值是false,autoCommit是false(只要我们使用无参的sqlSessionFactory.openSession();),取反之后**!autoCommit**是true,但是dirty已经是false,所以!this.autoCommit && this.dirty的值是false,那么force也是false,所以整一个表达式就是false:
private boolean isCommitOrRollbackRequired(boolean force) { return !this.autoCommit && this.dirty || force; }我们返回上一层,executor.close()方法,参数是false:
代码语言:javascript代码运行次数:0运行复制this.executor.close(this.isCommitOrRollbackRequired(false));
跟进去close()方法,forceRollback的值是false,我们发现有一个this.rollback(forceRollback):
public void close(boolean forceRollback) { try { try { this.rollback(forceRollback); } finally { // 最后如果事务不为空,那么我们就关闭事务 if (this.transaction != null) { this.transaction.close(); } } } catch (SQLException var11) { log.warn("Unexpected exception on closing transaction. Cause: " + var11); } finally { this.transaction = null; this.deferredLoads = null; this.localCache = null; this.localOutputParameterCache = null; this.closed = true; } }我们跟进去rollback()这个方法,我们可以发现required是fasle,所以this.transaction.rollback();是不会执行的,这个因为我们在前面做了提交了,所以是不用回滚的:
public void rollback(boolean required) throws SQLException { if (!this.closed) { try { this.clearLocalCache(); this.flushStatements(true); } finally { if (required) { this.transaction.rollback(); } } } }假如我们现在执行完insert()方法,但是没有使用commit(),那么现在的dirty就是true,也就是数据库数据与内存的数据不一致。我们再执行close()方法的时候,dirty是true,!this.autoCommit是true,那么整个表达式就是true。
代码语言:javascript代码运行次数:0运行复制 private boolean isCommitOrRollbackRequired(boolean force) { return !this.autoCommit && this.dirty || force; }返回上一层,close的参数就会变成true
代码语言:javascript代码运行次数:0运行复制this.executor.close(this.isCommitOrRollbackRequired(false));
close()方法里面调用了this.rollback(forceRollback);,参数为true,我们跟进去,可以看到确实执行了回滚:
public void rollback(boolean required) throws SQLException { if (!this.closed) { try { this.clearLocalCache(); this.flushStatements(true); } finally { if (required) { this.transaction.rollback(); } } } }所以只要我们执行了提交(commit),那么关闭的时候就不会执行回滚,只要没有提交事务,就会发生回滚,所以里面的dirty是很重要的。
【作者简介】:秦怀,公众号【秦怀杂货店】作者,技术之路不在一时,山高水长,纵使缓慢,驰而不息。这个世界希望一切都很快,更快,但是我希望自己能走好每一步,写好每一篇文章,期待和你们一起交流。
此文章仅代表自己(本菜鸟)学习积累记录,或者学习笔记,如有侵权,请联系作者核实删除。人无完人,文章也一样,文笔稚嫩,在下不才,勿喷,如果有错误之处,还望指出,感激不尽~
- END -
# 为什么
# red
# JavaScript
# sql
# mybatis
# 封装
# 成员变量
# Session
# xml
# 接口
# finally
# delete
# 对象
# this
# idea
# 数据库
# 我们可以
# 是一个
# 执行器
# 这是
# 就会
# 创建一个
# 上一层
# 之处
# 可以看到
# 发现有
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
PHP的CURL方法curl_setopt()函数案例介绍(抓取网页,POST数据)
深圳网站制作培训,深圳哪些招聘网站比较好?
北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?
google浏览器怎么清理缓存_谷歌浏览器清除缓存加速详细步骤
Laravel如何实现用户注册和登录?(Auth脚手架指南)
Laravel如何处理和验证JSON类型的数据库字段
ChatGPT 4.0官网入口地址 ChatGPT在线体验官网
Laravel的.env文件有什么用_Laravel环境变量配置与管理详解
公司网站制作价格怎么算,公司办个官网需要多少钱?
电商网站制作价格怎么算,网上拍卖流程以及规则?
Laravel如何使用API Resources格式化JSON响应_Laravel数据资源封装与格式化输出
Laravel怎么配置S3云存储驱动_Laravel集成阿里云OSS或AWS S3存储桶【教程】
焦点电影公司作品,电影焦点结局是什么?
Laravel如何生成和使用数据填充?(Seeder和Factory示例)
如何在云主机快速搭建网站站点?
网站制作软件免费下载安装,有哪些免费下载的软件网站?
Laravel如何处理文件下载请求?(Response示例)
如何快速搭建安全的FTP站点?
如何自己制作一个网站链接,如何制作一个企业网站,建设网站的基本步骤有哪些?
HTML5空格在Angular项目里怎么处理_Angular中空格的渲染问题【详解】
Android中AutoCompleteTextView自动提示
JavaScript如何实现音频处理_Web Audio API如何工作?
Laravel如何使用Scope本地作用域_Laravel模型常用查询逻辑封装技巧【手册】
西安专业网站制作公司有哪些,陕西省建行官方网站?
为什么php本地部署后css不生效_静态资源加载失败修复技巧【技巧】
Laravel事件监听器怎么写_Laravel Event和Listener使用教程
HTML5空格和nbsp有啥关系_nbsp的作用及使用场景【说明】
如何在云虚拟主机上快速搭建个人网站?
如何在宝塔面板中修改默认建站目录?
php 三元运算符实例详细介绍
Laravel如何实现API速率限制?(Rate Limiting教程)
智能起名网站制作软件有哪些,制作logo的软件?
手机怎么制作网站教程步骤,手机怎么做自己的网页链接?
制作旅游网站html,怎样注册旅游网站?
Laravel请求验证怎么写_Laravel Validator自定义表单验证规则教程
如何在IIS管理器中快速创建并配置网站?
Laravel如何实现数据导出到PDF_Laravel使用snappy生成网页快照PDF【方案】
移动端脚本框架Hammer.js
如何为不同团队 ID 动态生成多个非值班状态按钮
Laravel定时任务怎么设置_Laravel Crontab调度器配置
Laravel如何使用Service Provider服务提供者_Laravel依赖注入与容器绑定【深度】
Laravel怎么返回JSON格式数据_Laravel API资源Response响应格式化【技巧】
Laravel Sail是什么_基于Docker的Laravel本地开发环境Sail入门
javascript中的try catch异常捕获机制用法分析
Laravel怎么集成Vue.js_Laravel Mix配置Vue开发环境
JavaScript常见的五种数组去重的方式
Laravel集合Collection怎么用_Laravel集合常用函数详解
Laravel如何连接多个数据库_Laravel多数据库连接配置与切换教程
详解jQuery中基本的动画方法
php静态变量怎么调试_php静态变量作用域调试技巧【解答】


is.closed) { throw new ExecutorException("Cannot commit, transaction is already closed"); } else { this.clearLocalCache(); this.flushStatements(); // 如果required是true,那么就提交事务 if (required) { this.transaction.commit(); } }}