Mybatis源码分析(二)- SqlSessionFactory和SqlSession详解
来源:互联网 发布:网络剧营销创意 编辑:程序博客网 时间:2024/05/18 07:53
本系列以Mybatis 3.3.X分支源码作为分析源,mybatis源码Git地址:https://github.com/mybatis/mybatis-3.git。
SqlSessionFactory 作为Mybatis的应用入口,主要提供了各种获取SqlSession实例的方法。SqlSessionFactory在大部分情况下,建议在系统中生成唯一实例(读写分离、多库连接等除外)。
SqlSession主要提供了常用的数据库增删查改操作。
一、 SqlSessionFactory类
SqlSessionFactory主要定义了几个重载的openSession方法,其主要实现为org.apache.ibatis.session.defaults.DefaultSqlSessionFactory。
首先描述下SqlSessionFactory的所有方法涉及到的参数:
boolean autoCommit:主要为connection.setAutoCommit(autoCommit)的参数,表示是否自动提交
Connection connection:数据库连接
TransactionIsolationLevel level:事务隔离级别,其为enum参数,主要定义了五种事务隔离级别与Connection中五种隔离级别一一对应。这里简要描述下各个级别的含义:
TransactionIsolationLevel.NONE 表示不支持事务
TransactionIsolationLevel.READ_UNCOMMITTED 表示可能发生脏读、不可重复读和幻读, 属于限制性最弱的隔离级别,并发性较高
TransactionIsolationLevel.READ_COMMITTED 表示可能发生不可重复读和幻读。SqlServer和Oracle默认采用此隔离级别
TransactionIsolationLevel.REPEATABLE_READ 表示可能发生幻读。Mysql默认采用此隔离级别
TransactionIsolationLevel.SERIALIZABLE 最高的事务隔离级别,通过强制对事务排序来避免幻读。缺点是在大并发下容易导致大量超时和锁竞争
下面给出脏读,不可重复读和幻读的基本描述:
脏读:指事务读取了其他事务未提交的数据。比如一个事务正在访问并修改数据,此修改还未提交到数据库中,此时另外一个事务需要访问此数据,正好读取到修改后的数据并使用,此时第一个事务因为某些原因进行回滚,最终导致数据的不正确性。
不可重复读:指同一个事务内,多次读取同一数据得到不同的结果。比如当前一个事务在一开始读取了数据并执行其他逻辑操作,此时另外一个事务修改了此数据,在第一个事务内再次读取数据时,发现结果与第一次不一致。
幻读:与不可重复读类似,主要指一个事务操作一组数据时,其他事务新增或者删除了该组部分数据,导致第一个事务读取时发现与想要的结果不一致,出现幻读现象。比如一个事务获取表中标志位为0的数据有100条,然后更新所有数据的标志位为1,此时其他事务添加了一条标志位为0的数据并提交,第一个事务再读取标志位为0的数据时发现还有1条。
ExecutorType execType:定义执行类型的枚举,进入源码可以看到仅定义了SIMPLE, REUSE, BATCH三种类型。其中SIMPLE表示每个语句的执行会创建一个新的预处理器;REUSE表示复用预处理器;BATCH表示执行器批量执行更新语句
不带参数的openSession是大部分情况下的使用模式,默认采用配置文件的执行器,采用数据库默认事务隔离级别且关闭自动提交。
分析了参数后,再看看DefaultSqlSessionFactory默认不带参数的openSession是如何使用的:
@Override public SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); }
不带参数的openSession是大部分情况下的使用模式,默认采用配置文件的执行器,采用数据库默认事务隔离级别且关闭自动提交。
DefaultSessionFactory主要实现逻辑有两个方法openSessionFromDataSource和openSessionFromConnection,从方法名和参数可以看出,前者主要采用配置的dataSource获取数据库连接,后者为人工设置数据库连接。因为两者实现类同,这里描述下openSessionFromDataSource的实现。
1 获取事务对象
final Environment environment = configuration.getEnvironment(); //获取事务工厂,优先从Environment中获取,不存在则新建ManagedTransactionFactory实例 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); //获取事务对象 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
事务对象共有两种JdbcTransaction和ManagedTransaction,二者的区别详见mybatis官方文档的描述:
- JDBC – 这个配置就是直接使用了 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务范围。
- MANAGED – 这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置为 false 来阻止它默认的关闭行为。
//通过ExecutorType和事务构建执行器 final Executor executor = configuration.newExecutor(tx, execType);其主要调用了Configuration的newExecutor方法实现:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) { //如果没有传递执行器类型,则采用默认的类型,默认为SIMPLE类型 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; }执行器类型在之前已经描述过,共有SIMPLE, REUSE, BATCH三种。这里注意下如果设置为cacheEnabled,则会将执行器包装生成CachingExecutor。CachingExecutor通过代理原有执行器,实现缓存的处理逻辑,后续会针对缓存进行单独的博文描述。
3 生成SqlSession实例
主要利用Configuration,Executor和autoCommit构造DefaultSqlSession对象
return new DefaultSqlSession(configuration, executor, autoCommit);
SqlSession主要定义了常用的数据库访问方法主要分为select、update两大类,其主要实现为org.apache.ibatis.session.defaults.DefaultSqlSession。SqlSession主要利用Executor执行逻辑,其本身的实现相对比较简单,Executor将在下一篇分析说明。
首先描述下DefaultSqlSession的属性定义:
private Configuration configuration;private Executor executor;private boolean autoCommit;private boolean dirty;
其中:前三者属性已经多次了解过,configuration表示配置信息,executor表示执行器,autoCommit表示是否自动提交。dirty参数主要用来标记当前的提交或者回滚是否需要数据库执行,仅当非自动提交下,dirty=true时,在调用commit和rollback时会执行数据库的commit和rollback。
1 select方法体描述
DefaultSqlSession提供了常用的查询操作,包括selectOne、selectList、selectMap、select几个重载方法。
selectOne方法主要通过调用selectList实现数据库的查询操作,其源码如下所示:
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; } }从源码可以看出,selectOne通过调用selectList实现查询,并对返回值进行判断处理,如果返回数据为1,则认为查询成功;返回数据大于1,则抛出TooManyResultsException异常;没有查询到数据则返回NULL
selectList、selectMap和select方法的实现都大同小异,select主要多了ResultHandler参数,从名称就可以看出为结果回调处理方法。这里仅以select作为简要分析,其源码如下所示:
@Override public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { MappedStatement ms = configuration.getMappedStatement(statement); executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
第一步通过statement的id获取到相应的Statement对象,其中MapperdStatement包括了本次执行请求的许多重要信息,将在后续文章中一一描述。
第二步调用执行器执行实际的查询操作,需要注意下select操作因为传递handler,其返回值为void,而其他三类查询方法直接返回查询结果。
2 update方法体
update方法体系包括insert、delete、update方法。其中insert、delete都是通过调用update方法来完成实际的更新操作。update方法实现源码如下所示:
@Override public int update(String statement, Object parameter) { try { dirty = true; MappedStatement ms = configuration.getMappedStatement(statement); return executor.update(ms, wrapCollection(parameter)); } catch (Exception e) { throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }第一步将dirty置为true,便于后续的数据库commit和rollback
第二步获取实际的statement对象
第三步调用执行器的update方法执行实际的更新操作
SqlSession实现相对比较简单,其核心的Executor将会在下一篇文章中详细描述。
- Mybatis源码分析(二)- SqlSessionFactory和SqlSession详解
- Mybatis源码分析之SqlSessionFactory,SqlSession和连接池
- Mybatis - 获取SqlSessionFactory和SqlSession
- mybatis源码学习之执行过程分析(1)——SqlSessionFactory及SqlSession的创建
- sqlsession和sqlsessionFactory区别
- SqlSessionFactory和Sqlsession
- MyBatis常用对象SqlSessionFactory和SqlSession介绍和运用
- MyBatis--SqlSessionFactoryBuilder,SqlSessionFactory,SqlSession作用域和生命周期
- MyBatis 源码分析——SqlSession接口和Executor类
- mybatis源码解析(二)生成SqlSessionFactory
- SqlSessionFactory创建SqlSession测试mybatis的sql
- Mybatis学习笔记--SqlSessionFactory、SqlSession等
- MyBatis源码分析(三)-SqlSession理解
- mybatis的探索过程之SqlSessionFactoryBuilder,SqlSessionFactory,SqlSession作用域和生命周期
- Mybatis源码分析一(SqlsessionFactory及源码整体结构)
- Mybatis源码(二)之Spring整合mybatis创建SqlSession
- Mybatis(二)-----------------Sqlsession
- mybatis源码分析——SqlSessionFactory实例的产生过程
- jquery 两种方法设置disabled属性
- Exsi5.0在虚拟机中安装时提示Intel VT不支持解决办法
- Laravel与Repository Pattern(仓库模式)——概念篇
- ndk编译出现的问题
- CentOS6.5下安装mysql(glibc包安装)
- Mybatis源码分析(二)- SqlSessionFactory和SqlSession详解
- ImageLoader类
- JDBC学习(6)工厂模式与单例模式
- 关于Epoll的通俗理解
- Java基础学习笔记
- 通过双11暴露出的我们的哪些问题-阶段性总结
- 100多家软件公司和IT名企招聘软件工程师面试题和笔试题
- Android ConstraintLayout布局的使用
- Android Studio 的必杀技