Mybaits 源码解析 (四)----- SqlSession的创建过程(看懂框架源码再也不用死记硬背面试题)
SqlSession是mybatis的核心接口之一,是myabtis接口层的主要组成部分,对外提供了mybatis常用的api。myabtis提供了两个SqlSesion接口的实现,常用的实现类是DefaultSqlSession。它相当于一个数据库连接对象,在一个SqlSession中可以执行多条SQL语句。
创建SqlSession
前面的两篇文章我们已经得到了SqlSessionFactory
,那么SqlSession
将由SqlSessionFactory
进行创建。
SqlSession sqlSession=sqlSessionFactory.openSession();
我们就来看看这个SqlSessionFactory
的 openSession
方法是如何创建SqlSession对象的。根据上面的分析,这里的SqlSessionFactory
类型对象其实是一个DefaultSqlSessionFactory
对象,因此,需要到DefaultSqlSessionFactory
类中去看openSession
方法。
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
调用了openSessionFromDataSource方法,并且第一个参数获取了默认的执行器类型,第二个参数为null,第三个参数为false,看看这个默认的执行器类型是啥
默认的执行器类型SIMPLE,我们跟进openSessionFromDataSource方法
/**
* ExecutorType 指定Executor的类型,分为三种:SIMPLE, REUSE, BATCH,默认使用的是SIMPLE
* TransactionIsolationLevel 指定事务隔离级别,使用null,则表示使用数据库默认的事务隔离界别
* autoCommit 是否自动提交,传过来的参数为false,表示不自动提交
*/
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);
// 创建Executor,即执行器
// 它是真正用来Java和数据库交互操作的类,后面会展开说。
final Executor executor = configuration.newExecutor(tx, execType);
// 创建DefaultSqlSession对象返回,其实现了SqlSession接口
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
主要包含以下几个步骤:
- 首先从configuration获取Environment对象,里面主要包含了DataSource和TransactionFactory对象
- 创建TransactionFactory
- 创建Transaction
- 从configuration获取Executor
- 构造DefaultSqlSession对象
我们先来看看常规的environment配置
//配置environment环境
<environments default="development">
<environment id="development">
/** 事务配置 type= JDBC、MANAGED
* 1.JDBC:这个配置直接简单使用了JDBC的提交和回滚设置。它依赖于从数据源得到的连接来管理事务范围。
* 2.MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接。
*/
<transactionManager type="JDBC" />
/** 数据源类型:type = UNPOOLED、POOLED、JNDI
* 1.UNPOOLED:这个数据源的实现是每次被请求时简单打开和关闭连接。
* 2.POOLED:这是JDBC连接对象的数据源连接池的实现。
* 3.JNDI:这个数据源的实现是为了使用如Spring或应用服务器这类的容器
*/
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/xhm" />
<property name="username" value="root" />
<property name="password" value="root" />
//默认连接事务隔离级别
<property name="defaultTransactionIsolationLevel" value=""/>
</dataSource>
</environment>
</environments>
还记得前面文章是怎么解析environments的吗,Mybaits 源码解析 (二)----- 根据配置文件创建SqlSessionFactory(Configuration的创建过程),我们简单的回顾一下
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
// 获取 default 属性
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
// 获取 id 属性
String id = child.getStringAttribute("id");
/*
* 检测当前 environment 节点的 id 与其父节点 environments 的属性 default
* 内容是否一致,一致则返回 true,否则返回 false
* 将其default属性值与子元素environment的id属性值相等的子元素设置为当前使用的Environment对象
*/
if (isSpecifiedEnvironment(id)) {
// 将environment中的transactionManager标签转换为TransactionFactory对象
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
// 将environment中的dataSource标签转换为DataSourceFactory对象
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
// 创建 DataSource 对象
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
// 构建 Environment 对象,并设置到 configuration 中
configuration.setEnvironment(environmentBuilder.build());
}
}
}
} private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
//通过别名获取Class,并实例化
TransactionFactory factory = (TransactionFactory)this.resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
} else {
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
} private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
//通过别名获取Class,并实例化
Properties props = context.getChildrenAsProperties();
DataSourceFactory factory = (DataSourceFactory)this.resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
} else {
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
}
获取TransactionFactory
我们的environment配置中transactionManager type="JDBC"和dataSource type="POOLED",则生成的transactionManager为JdbcTransactionFactory,DataSourceFactory为PooledDataSourceFactory
我们回到openSessionFromDataSource,接着看看getTransactionFactoryFromEnvironment方法
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
return (TransactionFactory)(environment != null && environment.getTransactionFactory() != null ? environment.getTransactionFactory() : new ManagedTransactionFactory());
}
创建Transaction
很明显 environment.getTransactionFactory() 就是JdbcTransactionFactory,看看这个工厂是如何创建Transaction的
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(ds, level, autoCommit);
}
直接通过工厂方法创建了一个JdbcTransaction对象,并传参DataSource ,事务隔离级别null,自动提交false三个参数,我们来看看JdbcTransaction
public class JdbcTransaction implements Transaction {
//数据库连接对象
protected Connection connection;
//数据库DataSource
protected DataSource dataSource;
//数据库隔离级别
protected TransactionIsolationLevel level;
//是否自动提交
protected boolean autoCommmit; public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
//设置dataSource和隔离级别,是否自动提交属性
//这里隔离级别传过来的是null,表示使用数据库默认隔离级别,自动提交为false,表示不自动提交
this.dataSource = ds;
this.level = desiredLevel;
this.autoCommmit = desiredAutoCommit;
} public Connection getConnection() throws SQLException {
if (this.connection == null) {
this.openConnection();
} return this.connection;
} //提交功能是通过Connection去完成的
public void commit() throws SQLException {
if (this.connection != null && !this.connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Committing JDBC Connection [" + this.connection + "]");
} this.connection.commit();
} } //回滚功能是通过Connection去完成的
public void rollback() throws SQLException {
if (this.connection != null && !this.connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Rolling back JDBC Connection [" + this.connection + "]");
} this.connection.rollback();
} } //关闭功能是通过Connection去完成的
public void close() throws SQLException {
if (this.connection != null) {
this.resetAutoCommit();
if (log.isDebugEnabled()) {
log.debug("Closing JDBC Connection [" + this.connection + "]");
} this.connection.close();
} } //获取连接是通过dataSource来完成的
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {
log.debug("Opening JDBC Connection");
} this.connection = this.dataSource.getConnection();
if (this.level != null) {
this.connection.setTransactionIsolation(this.level.getLevel());
} this.setDesiredAutoCommit(this.autoCommmit);
}
}
JdbcTransaction主要维护了一个默认autoCommit为false的Connection对象,对事物的提交,回滚,关闭等都是接见通过Connection完成的。
创建Executor
//创建一个执行器,默认是SIMPLE
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
//根据executorType来创建相应的执行器,Configuration默认是SIMPLE
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
//创建SimpleExecutor实例,并且包含Configuration和transaction属性
executor = new SimpleExecutor(this, transaction);
} //如果要求缓存,生成另一种CachingExecutor,装饰者模式,默认都是返回CachingExecutor
/**
* 二级缓存开关配置示例
* <settings>
* <setting name="cacheEnabled" value="true"/>
* </settings>
*/
if (cacheEnabled) {
//CachingExecutor使用装饰器模式,将executor的功能添加上了二级缓存的功能,二级缓存会单独文章来讲
executor = new CachingExecutor(executor);
}
//此处调用插件,通过插件可以改变Executor行为,此处我们后面单独文章讲
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
executor包含了Configuration和刚刚创建的Transaction,默认的执行器为SimpleExecutor,如果开启了二级缓存(默认开启),则CachingExecutor会包装SimpleExecutor,然后依次调用拦截器的plugin方法返回一个被代理过的Executor对象。
CachingExecutor 对象里面包含了刚创建的SimpleExecutor,后面文章我们会及具体讲这个类
public class CachingExecutor implements Executor {
private Executor delegate;
private TransactionalCacheManager tcm = new TransactionalCacheManager(); public CachingExecutor(Executor delegate) {
this.delegate = delegate;
delegate.setExecutorWrapper(this);
}
//略
}
构造DefaultSqlSession对象
new DefaultSqlSession(this.configuration, executor, autoCommit);
传参configuration和刚生成的executor,我们来简单看看
public class DefaultSqlSession implements SqlSession { /**
* mybatis全局配置新
*/
private final Configuration configuration;
/**
* SQL执行器
*/
private final Executor executor; /**
* 是否自动提交
*/
private final boolean autoCommit; private List<Cursor<?>> cursorList; public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
} @Override
public <T> T selectOne(String statement) {
return this.<T>selectOne(statement, null);
} @Override
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List<T> list = this.<T>selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
@Override
public <E> List<E> selectList(String statement) {
return this.selectList(statement, null);
} @Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
} @Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
} //略....update等方法
}
SqlSession的所有查询接口最后都归结位Exector的方法调用。后面文章我们来分析其调用流程
Mybaits 源码解析 (四)----- SqlSession的创建过程(看懂框架源码再也不用死记硬背面试题)的更多相关文章
- Mybatis源码解析(四) —— SqlSession是如何实现数据库操作的?
Mybatis源码解析(四) -- SqlSession是如何实现数据库操作的? 如果拿一次数据库请求操作做比喻,那么前面3篇文章就是在做请求准备,真正执行操作的是本篇文章要讲述的内容.正如标题一 ...
- Spring源码分析之Bean的创建过程详解
前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...
- Flink 源码解析 —— JobManager 处理 SubmitJob 的过程
JobManager 处理 SubmitJob https://t.zsxq.com/3JQJMzZ 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1 ...
- Flink 源码解析 —— TaskManager 处理 SubmitJob 的过程
TaskManager 处理 SubmitJob 的过程 https://t.zsxq.com/eu7mQZj 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink ...
- Sentinel源码解析四(流控策略和流控效果)
引言 在分析Sentinel的上一篇文章中,我们知道了它是基于滑动窗口做的流量统计,那么在当我们能够根据流量统计算法拿到流量的实时数据后,下一步要做的事情自然就是基于这些数据做流控.在介绍Sentin ...
- Netty 源码解析(九): connect 过程和 bind 过程分析
原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第九篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源 ...
- Spring AOP源码解析——专治你不会看源码的坏毛病!
昨天有个大牛说我啰嗦,眼光比较细碎,看不到重点.太他爷爷的有道理了!要说看人品,还是女孩子强一些. 原来记得看到一个男孩子的抱怨,说怎么两人刚刚开始在一起,女孩子在心里就已经和他过完了一辈子.哥哥们, ...
- Mybatis详解(二) sqlsession的创建过程
我们处于的位置 我们要清楚现在的情况. 现在我们已经调用了SqlSessionFactoryBuilder的build方法生成了SqlSessionFactory 对象. 但是如标题所说,要想生成sq ...
- Dubbo 源码解析四 —— 负载均衡LoadBalance
欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 Dubbo 入门之二 --- 项目结构解析 Dubbo 源码分析系列之三 -- 架构原 ...
随机推荐
- 45道CSS基础面试题(附答案)
1 .介绍一下标准的CSS的盒子模型?与低版本IE的盒子模型有什么不同的? 标准盒子模型:宽度=内容的宽度(content)+ border + padding + margin低版本IE盒子模型:宽 ...
- linux使用命令上传下载文件 -- lrzsz
之前都是用Xftp工具在本地和linux系统之间上传下载文件,最近觉得麻烦,因为平时用Xshell连接,要传输文件的时候还要额外使用别的工具,下面是lrzsz的安装和简单的使用过程: 详细的使用可以s ...
- Eclipse的egit插件冲突合并方法
Eclipse有一个git的插件叫EGit,用于实现本地代码和远程代码对比.合并以及提交.但是在本地代码和远程代码有冲突的时候,EGit的处理方案还是有点复杂.今天就彻底把这些步骤给理清楚,并公开让一 ...
- CF #579 (Div. 3) A.Circle of Students
A. Circle of Students time limit per test2 seconds memory limit per test256 megabytes inputstandard ...
- 基于 HTML5 和 WebGL 的地铁站 3D 可视化系统
前言 工业互联网,物联网,可视化等名词在我们现在信息化的大背景下已经是耳熟能详,日常生活的交通,出行,吃穿等可能都可以用信息化的方式来为我们表达,在传统的可视化监控领域,一般都是基于 Web SCAD ...
- 快学Scala 第七课 (类构造函数)
类 主构造器: class Person (var name: String){ } 主构造参数可以不带val或者var,如果没有被其他方法使用,则不保存为字段. 如果被其他方法使用,则被升格为字段, ...
- 问题TypeError: __init__() takes 1 positional argument but 2 were given解决方案
在搭建自动化框架时,遇到一个问题,如下图: 根据报错的意思,应该是__init__函数的问题, 位置应该是HomePage文件 立马去查看一下文件,如图: 原来是因为的粗心大意__init__(sel ...
- C# 8 的模式匹配
C# 7 里面的Pattern Mathing is 模式 switch 和 when C# 8 里面的Pattern Matching 使用Deconstructor 和 位置匹配模式 下面两个类T ...
- selenium+testNG自动化测试框架搭建
自动化测试框架搭建 1 Java环境的搭建 1.1访问oracle的官网下载最新版本的jdk http://www.oracle.com/technetwork/java/javase/downloa ...
- 02-23 决策树CART算法
目录 决策树CART算法 一.决策树CART算法学习目标 二.决策树CART算法详解 2.1 基尼指数和熵 2.2 CART算法对连续值特征的处理 2.3 CART算法对离散值特征的处理 2.4 CA ...