承接Mybatis源码解析-MapperRegistry代理mapper接口,本文将在前文基础上讲解持久层会话的生成

SqlSessionFactory生成

在spring中,SqlSessionFactory的生成是通过SqlSessionFactoryBean生成的,如下

  protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
****
****
return this.sqlSessionFactoryBuilder.build(configuration);
}

创建的SqlSessionFactory对象类型为org.apache.ibatis.session.defaults.DefaultSqlSessionFactory

  public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}

上述创建的SqlSessionFactory接口最终会注入至前文所提及的MapperFactoryBean对象中,这里就不罗列SqlSessionFactory的接口方法列表了。主要功能就是获取建立Sql会话

MapperFactoryBean父类SqlSessionDaoSupport

在其父类有个setSqlSessionFactory()方法

  private SqlSession sqlSession;

  private boolean externalSqlSession;

  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (!this.externalSqlSession) {
//包装成SqlSessionTemplate对象
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
}
}

mybatis最终会将SqlSession工厂包装成SqlSessionTemplate适配器。

SqlSessionTemplate构造函数

基本都是由SqlSessionFactory作为入参

  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
//默认的ExecutorType为ExecutorType.SIMPLE
this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
}
  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) { notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required"); this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
//对数据库的操作也会包装成代理的形式,所有的CRUD操作则都由SqlSessionInterceptor对象来完成
this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
}

我们迫在眉睫的需要了解sqlSession代理的处理流程

SqlSessionTemplate#SqlSessionInterceptor

其是一个内部私有类

  private class SqlSessionInterceptor implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//通过SqlSessionUtils.getSqlSession()获得真实处理CRUD的持久层,默认为DefaultSqlSession
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
//执行CRUD操作
Object result = method.invoke(sqlSession, args);
//非事务处理的数据操作需要强制commit
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
//异常转化
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
//关闭sqlSession,再上一个保险
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}

创建sqlSession的步骤是通过SqlSessionUtils#getSqlSession()来完成的,我们必须需要仔细分析

SqlSessionUtils#getSqlSession()-获取真实的持久层操作对象

其实本质是从SqlSessionFactory中创建SqlSession的,这里只是作下缓存SqlSession

  public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sessionFactory, "No SqlSessionFactory specified");
notNull(executorType, "No ExecutorType specified");
//尝试从ThreadLocal中获取SqlSessionHolder,类似缓存
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); if (holder != null && holder.isSynchronizedWithTransaction()) {
//不允许再次获取的SqlSession修改executorType
if (holder.getExecutorType() != executorType) {
throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
}
//对引用同一个SqlSession的计数
holder.requested(); return holder.getSqlSession();
}
//通过SqlSessionFactory创建SqlSession
SqlSession session = sessionFactory.openSession(executorType); //判断是否是同步的
if (TransactionSynchronizationManager.isSynchronizationActive()) {
Environment environment = sessionFactory.getConfiguration().getEnvironment();
//TransactionFactory默认的为SpringManagedTransactionFactory
if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
//创建SqlSessionHolder并缓存在ThreadLocal中
holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
TransactionSynchronizationManager.bindResource(sessionFactory, holder);
TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
holder.setSynchronizedWithTransaction(true);
holder.requested();
} else {
if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) {
} else {
throw new TransientDataAccessResourceException(
"SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
}
}
} return session;
}

我们知道上述的SqlSessionFactory为DefaultSqlSessionFactory,下面看下其如何创建SqlSession

DefaultSqlSessionFactory#openSessionFromDataSource

源码奉上

  //默认情况下,参数值为ExecutorType.SIMPLE/null/false
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
//事务接口对象
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
//此处的TransactionFactory为SpringManagedTransactionFactory
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//创建事务对象SpringManagedTransaction
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//根据execType创建池,默认为SimpleExecutor并支持缓存
final Executor executor = configuration.newExecutor(tx, execType);
//返回DefaultSqlSession
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();
}
}
  • 默认的事务对象则是由SpringManagedTransactionFactory来创建的,内含dataSource数据源,且不自动提交

  • 默认的Executor线程池则为SimpleExecutor并支持缓存,另外还有BatchExecutor/ReuseExecutor线程池。根据ExecutorType判断

  • 默认创建的SqlSession对象为DefaultSqlSession,内含事务对象、池、Configuration对象

SqlSessionTemplate#CRUD操作

根据前文的分析,SqlSessionTemplate的CRUD操作是由MapperMethod#execute()方法调用的,其中传给sqlSession的参数为mappedStatementId和其中的方法参数集合

public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
if (SqlCommandType.INSERT == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
} else if (SqlCommandType.UPDATE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
} else if (SqlCommandType.DELETE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
} else if (SqlCommandType.SELECT == command.getType()) {
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
} else {
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}

简单梳理下service层调用CRUD操作的实现步骤

service层调用dao数据层方法-->读取对应接口dao的MapperFactoryBean#getObject()方法

-->SqlSessionTemplate#getMapper()方法-->得到MapperProxy代理类并调用对应接口方法的MapperMethod

-->接口类+method方法名作为mappedStatementId读取MappedStatement对象以获取SqlCommand指令-->根据SqlCommand指令调用SqlSessionTemplate对应的CRUD操作,一般为select()/delete()/update()/insert()方法

-->sqlSessionProxy代理调用真实数据层处理类DefaultSqlSession对应的CRUD操作-->选用池来引用MappedStatement对象处理数据库sql语句

-->返回结果集供MapperMethod处理返回给service层

CREATE/UPDATE/DELETE

简单从CREATE操作看,先看SqlSessionTempate#insert()方法

  public int insert(String statement) {
return this.sqlSessionProxy.insert(statement);
}

进而观察DefaultSqlSession#insert()方法

  public int insert(String statement) {
return insert(statement, null);
}
  public int insert(String statement, Object parameter) {
return update(statement, parameter);
}

进行仔细的观察我们得知,除了select语句,其实调用的都是update方法

  public int update(String statement, Object parameter) {
try {
dirty = true;
//根据statement得到相应的MappedStatement对象
MappedStatement ms = configuration.getMappedStatement(statement);
//最终调用Executor池来进行相应的操作
return executor.update(ms, wrapCollection(parameter));
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}

而其中的wrapCollection()方法则是将参数分门别类下

  //StrictMap就是普通的HashMap,只是在get方法上加了必须存在相应key的判断
private Object wrapCollection(final Object object) {
if (object instanceof List) {
StrictMap<Object> map = new StrictMap<Object>();
map.put("list", object);
return map;
} else if (object != null && object.getClass().isArray()) {
StrictMap<Object> map = new StrictMap<Object>();
map.put("array", object);
return map;
}
return object;
}

SELECT

直接转进DefaultSqlSession#select()通用方法

  public void select(String statement, Object parameter, ResultHandler handler) {
select(statement, parameter, RowBounds.DEFAULT, handler);
}
  public void select(String statement, ResultHandler handler) {
select(statement, null, RowBounds.DEFAULT, handler);
}
  public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
//调用线程池的query方法来获取,获取到的值存放到ResultHandler内部属性中
executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}

小结

  1. SqlSessionTemplate接受dao层的CRUD请求,通过代理调用DefaultSqlSession的CRUD操作

  2. DefaultSqlSession的内部操作都是通过org.apache.ibatis.executor.Executor接口处理然后返回结果的

Mybatis源码分析-SqlSessionTemplate的更多相关文章

  1. Mybatis源码分析-BaseExecutor

    根据前文Mybatis源码分析-SqlSessionTemplate的简单分析,对于SqlSession的CURD操作都需要经过Executor接口的update/query方法,本文将分析下Base ...

  2. 精尽MyBatis源码分析 - MyBatis-Spring 源码分析

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  3. 精尽MyBatis源码分析 - Spring-Boot-Starter 源码分析

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  4. springboot整合mybatis源码分析

    springboot整合mybatis源码分析 本文主要讲述mybatis在springboot中是如何被加载执行的,由于涉及的内容会比较多,所以这次只会对调用关系及关键代码点进行讲解,为了避免文章太 ...

  5. MyBatis源码分析-MyBatis初始化流程

    MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...

  6. MyBatis源码分析-SQL语句执行的完整流程

    MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...

  7. MyBatis源码分析(5)——内置DataSource实现

    @(MyBatis)[DataSource] MyBatis源码分析(5)--内置DataSource实现 MyBatis内置了两个DataSource的实现:UnpooledDataSource,该 ...

  8. MyBatis源码分析(4)—— Cache构建以及应用

    @(MyBatis)[Cache] MyBatis源码分析--Cache构建以及应用 SqlSession使用缓存流程 如果开启了二级缓存,而Executor会使用CachingExecutor来装饰 ...

  9. MyBatis源码分析(3)—— Cache接口以及实现

    @(MyBatis)[Cache] MyBatis源码分析--Cache接口以及实现 Cache接口 MyBatis中的Cache以SPI实现,给需要集成其它Cache或者自定义Cache提供了接口. ...

随机推荐

  1. 论MyBatis日志

    Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具: SLF4J Apache Commons Logging Log4j 2 Log4j JDK logging 具体选择哪个日志 ...

  2. CSS样式表之常用文本属性

    断更了两周了,因为纠结之后在学java啦,但是还是要把学过的前端知识更完 以下的一些文本属性是CSS最常用的属性: [长度单位]:px(像素) [颜色单位]: 十六进制:#ffffff 分别对应红绿蓝 ...

  3. 第一个spark+scala程序

    import org.apache.spark._import SparkContext._import java.util.{Calendar,Properties,Date,Locale}impo ...

  4. SSIM(结构相似度算法)不同实现版本的差异

    前言 最近用ssim测试图片画质损伤时,发现matlab自带ssim与之前一直使用的ssim计算得分有差异,故和同事开始确定差异所在. 不同的SSIM版本 这里提到不同的ssim版本主要基于matla ...

  5. office 2013幻灯片中插入SmartArt图形时出现错误下列一个或多个文件由于包含错误而无法运行

    office 2013幻灯片中插入SmartArt图形时出现错误下列一个或多个文件由于包含错误而无法运行 系统:win8 64位 PowerPoint2013 64位 在幻灯片中插入SmartArt图 ...

  6. FineReport填报分页设置

    1. 问题描述 进行FineReport数据填报时,如果数据量过大,由于前端浏览器的性能限制,如果将数据全部展现出来,速度会非常的慢,影响用户体验,这时候大家就会想,填报是否能像分页预览一样进行分页呢 ...

  7. Opencv探索之路(十九):读写xml和yml文件

    有时候我们处理完图像后需要保存一下数据到文件上,以供下一步的处理.一个比较广泛的需求场景就是:我们对一幅图像进行特征提取之后,需要把特征点信息保存到文件上,以供后面的机器学习分类操作.那么如果遇到这样 ...

  8. maven 打包时mapper.xml打不进去问题

    首先,来看下MAVENx项目标准的目录结构: 一般情况下,我们用到的资源文件(各种xml,properites,xsd文件等)都放在src/main/resources下面,利用maven打包时,ma ...

  9. win10 运行sqlplus报错“SP2-1503: 无法初始化 Oracle 调用界面”

    解决方法: 1.临时方案:此时可以以“管理员身份”运行cmd,然后再执行sqlplus就行了. 长久方案: 请看原文:http://blog.csdn.net/bisal/article/detail ...

  10. 封装游戏配表读取和存储(xml格式);支持行列存取,标题存取

    做服务器肯定会涉及到游戏配表,而读取游戏配表是一个必备技能; 之前一直都是采用TinyXml直接读,匹配字符串,每次加个表都是一大坨代码,看着也是心累; 因此利用了闲暇的时间封装了一个 xml配置表 ...