由之前解析可知,mybatis启动的时候会加载XML配置文件解析生成全局配置对象Configuration对象,SqlSessionFactoryBuilder类会根据Configuration对象创建一个DefaultSqlSessionFactory对象,而DefaultSqlSessionFactory对象实现了SqlSessionFactory中的创建SqlSession的方法,最终新建了一个SqlSession接口的默认实现类DefaultSqlSession,现在先来了解下SqlSession以及它的实现类

SqlSession解析

SqlSession位于mybatis包的org.apache.ibatis.session目录下,字面意思就是sql的会话,用于程序和数据库直接的sql会话,程序执行一次数据库操作就需要创建一个sqlSession,操作结束即关闭sqlSession;

既然是程序和数据库之间的会话,那么sqlSession接口的方法应该是程序和数据库都容易理解的,sqlSession中定义的方法都是关于数据库操作的方法,源码如下:

 package org.apache.ibatis.session;

 import java.io.Closeable;
import java.sql.Connection;
import java.util.List;
import java.util.Map; import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.BatchResult; public interface SqlSession extends Closeable { //根据Sql语句查询单条记录
<T> T selectOne(String statement);
<T> T selectOne(String statement, Object parameter);//根据Sql语句和参数查询单条记录 //根据Sql语句查询多条记录
<E> List<E> selectList(String statement);
<E> List<E> selectList(String statement, Object parameter);//根据Sql语句和参数查询多条记录
<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);//根据Sql语句和参数以及分页参数查询多条记录 //selectMap和selectList原理一样,只是将结果集映射成Map对象返回
<K, V> Map<K, V> selectMap(String statement, String mapKey);
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds); //返回游标对象
<T> Cursor<T> selectCursor(String statement);
<T> Cursor<T> selectCursor(String statement, Object parameter);
<T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds); //查询的结果对象由指定的ResultHandler处理
void select(String statement, Object parameter, ResultHandler handler);
void select(String statement, ResultHandler handler);
void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler); //执行insert语句
int insert(String statement);
int insert(String statement, Object parameter); //执行update语句
int update(String statement);
int update(String statement, Object parameter); //执行delete语句
int delete(String statement);
int delete(String statement, Object parameter); //提交事务
void commit();
void commit(boolean force); //事务回滚
void rollback();
void rollback(boolean force); //将请求刷新到数据库
List<BatchResult> flushStatements(); //关闭sqlSession
@Override
void close(); //清除缓存
void clearCache(); //获取Configuration对象
Configuration getConfiguration(); //获取Type对象的Mapper对象
<T> T getMapper(Class<T> type); //获取sqlSession对象的数据库连接
Connection getConnection();
}

SqlSession中的方法全是和数据库相关的增删改查以及事务的提交方法。

SqlSession有三个实现类,除了默认的DefaultSqlSession之外,还有SqlSessionManager和SqlSessionTemplate

接下来先看下SqlSession默认的实现类DefaultSqlSession是如何实现的。

DefaultSqlSession类解析

DefaultSqlSession有5个属性和2个构造方法如下:

   private Configuration configuration;//全局配置
private Executor executor;//执行器 private boolean autoCommit;//自动提交标识
private boolean dirty;
private List<Cursor<?>> cursorList;//游标列表 public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
} public DefaultSqlSession(Configuration configuration, Executor executor) {
this(configuration, executor, false);
}

下面以select方法为例

 @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;
}
}

很明显selectOne的方法最终都是调用了selectList方法,然后取唯一的一条数据返回。那在看看selectList相关的代码

 @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();
}
}

共有三个selectList方法,最终都是调用了最后一个selectList方法,这里有三个参数:statement是sql语句;parameter是传入的参数;RowBounds是和分页相关的参数

RowBounds源码如下:

 package org.apache.ibatis.session;

 /**
* @author Clinton Begin
*/
public class RowBounds { public static final int NO_ROW_OFFSET = 0;
public static final int NO_ROW_LIMIT = Integer.MAX_VALUE; //int的最大值
public static final RowBounds DEFAULT = new RowBounds(); private int offset;
private int limit; public RowBounds() {
this.offset = NO_ROW_OFFSET;
this.limit = NO_ROW_LIMIT;
} public RowBounds(int offset, int limit) {
this.offset = offset;
this.limit = limit;
} public int getOffset() {
return offset;
} public int getLimit() {
return limit;
} }

RowBounds的两个属性:offSet是指查询数据时从多少位置开始查询,limit是指返回数据的调试,默认是从0位置开始查询到Integer的最大值,相关于默认是不做分页处理;

回到正题,selectList最后执行的方法中执行了两行代码

 MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

调用configuration的getMapped的Statement方法获取MappedStatement对象,然后调用执行器executor的query方法获取查询结果。

这是select方法,再来看看其他的insert、update、delete方法

 @Override
public int insert(String statement) {
return insert(statement, null);
} @Override
public int insert(String statement, Object parameter) {
return update(statement, parameter);
} @Override
public int update(String statement) {
return update(statement, null);
} @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();
}
} @Override
public int delete(String statement) {
return update(statement, null);
} @Override
public int delete(String statement, Object parameter) {
return update(statement, parameter);
}

可以看出SqlSession的insert和delete的方法最终都是调用了update方法,而update方法最终也是调用了Executor的update

由此可得出结论sqlSession虽然叫程序和数据库之间的SQL会话,但是它并没有具体去执行sql语句,最终的sql语句的执行是由执行器Executor执行的,而SqlSession的作用只是创建了MappedStatement对象以及调用执行器去执行SQL

其他的commit、rollback方法同样最终都是调用的执行器Executor的对应的方法,那么接下来就去了解下执行器Executor是干嘛的,以及SqlSession创建的MappedStatement又是什么?

DefaultSqlSession完整源码如下:

 /**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.session.defaults; import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import org.apache.ibatis.binding.BindingException;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.exceptions.TooManyResultsException;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.result.DefaultMapResultHandler;
import org.apache.ibatis.executor.result.DefaultResultContext;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession; /**
*
* The default implementation for {@link SqlSession}.
* Note that this class is not Thread-Safe.
*
* @author Clinton Begin
*/
public class DefaultSqlSession implements SqlSession { private Configuration configuration;
private Executor executor; private boolean autoCommit;
private boolean dirty;
private List<Cursor<?>> cursorList; public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
} public DefaultSqlSession(Configuration configuration, Executor executor) {
this(configuration, executor, false);
} @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 <K, V> Map<K, V> selectMap(String statement, String mapKey) {
return this.selectMap(statement, null, mapKey, RowBounds.DEFAULT);
} @Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
return this.selectMap(statement, parameter, mapKey, RowBounds.DEFAULT);
} @Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
final List<? extends V> list = selectList(statement, parameter, rowBounds);
final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<K, V>(mapKey,
configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory());
final DefaultResultContext<V> context = new DefaultResultContext<V>();
for (V o : list) {
context.nextResultObject(o);
mapResultHandler.handleResult(context);
}
return mapResultHandler.getMappedResults();
} @Override
public <T> Cursor<T> selectCursor(String statement) {
return selectCursor(statement, null);
} @Override
public <T> Cursor<T> selectCursor(String statement, Object parameter) {
return selectCursor(statement, parameter, RowBounds.DEFAULT);
} @Override
public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
Cursor<T> cursor = executor.queryCursor(ms, wrapCollection(parameter), rowBounds);
registerCursor(cursor);
return cursor;
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
} @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();
}
} @Override
public void select(String statement, Object parameter, ResultHandler handler) {
select(statement, parameter, RowBounds.DEFAULT, handler);
} @Override
public void select(String statement, ResultHandler handler) {
select(statement, null, RowBounds.DEFAULT, handler);
} @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();
}
} @Override
public int insert(String statement) {
return insert(statement, null);
} @Override
public int insert(String statement, Object parameter) {
return update(statement, parameter);
} @Override
public int update(String statement) {
return update(statement, null);
} @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();
}
} @Override
public int delete(String statement) {
return update(statement, null);
} @Override
public int delete(String statement, Object parameter) {
return update(statement, parameter);
} @Override
public void commit() {
commit(false);
} @Override
public void commit(boolean force) {
try {
executor.commit(isCommitOrRollbackRequired(force));
dirty = false;
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error committing transaction. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
} @Override
public void rollback() {
rollback(false);
} @Override
public void rollback(boolean force) {
try {
executor.rollback(isCommitOrRollbackRequired(force));
dirty = false;
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error rolling back transaction. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
} @Override
public List<BatchResult> flushStatements() {
try {
return executor.flushStatements();
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error flushing statements. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
} @Override
public void close() {
try {
executor.close(isCommitOrRollbackRequired(false));
closeCursors();
dirty = false;
} finally {
ErrorContext.instance().reset();
}
} private void closeCursors() {
if (cursorList != null && cursorList.size() != 0) {
for (Cursor<?> cursor : cursorList) {
try {
cursor.close();
} catch (IOException e) {
throw ExceptionFactory.wrapException("Error closing cursor. Cause: " + e, e);
}
}
cursorList.clear();
}
} @Override
public Configuration getConfiguration() {
return configuration;
} @Override
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
} @Override
public Connection getConnection() {
try {
return executor.getTransaction().getConnection();
} catch (SQLException e) {
throw ExceptionFactory.wrapException("Error getting a new connection. Cause: " + e, e);
}
} @Override
public void clearCache() {
executor.clearLocalCache();
} private <T> void registerCursor(Cursor<T> cursor) {
if (cursorList == null) {
cursorList = new ArrayList<Cursor<?>>();
}
cursorList.add(cursor);
} private boolean isCommitOrRollbackRequired(boolean force) {
return (!autoCommit && dirty) || force;
} private Object wrapCollection(final Object object) {
if (object instanceof Collection) {
StrictMap<Object> map = new StrictMap<Object>();
map.put("collection", object);
if (object instanceof List) {
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;
} public static class StrictMap<V> extends HashMap<String, V> { private static final long serialVersionUID = -5741767162221585340L; @Override
public V get(Object key) {
if (!super.containsKey(key)) {
throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + this.keySet());
}
return super.get(key);
} } }

mybatis源码解析5---SqlSession解析的更多相关文章

  1. MyBatis 源码分析 - 映射文件解析过程

    1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...

  2. Spring mybatis源码篇章-MybatisDAO文件解析(二)

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-MybatisDAO文件解析(一) 默认加载mybatis主文件方式 XMLConfigBuilder ...

  3. Spring mybatis源码篇章-MybatisDAO文件解析(一)

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-SqlSessionFactory 加载指定的mybatis主文件 Mybatis模板文件,其中的属性 ...

  4. Mybatis源码分析之SqlSession和Excutor(二)

    通过上一篇文章的分析我们,我初步了解了它是如何创建sessionFactory的(地址:Mybatis源码分析之SqlSessionFactory(一)), 今天我们分析下Mybatis如何创建Sql ...

  5. myBatis源码学习之SqlSession

    在上一篇文章中SqlSessionFactory介绍了生产SqlSession的工厂,SqlSession是一个接口其具体实现类为DefaultSqlSession,SqlSession接口主要定义了 ...

  6. MyBatis源码部分简单地解析

    . 一.解析xml: > org.apache.ibatis.session.SqlSessionFactoryBuilder.build(java.io.InputStream, java.l ...

  7. Mybatis 源码之Plugin类解析

    public class Plugin implements InvocationHandler { private Object target; //目标对象 private Interceptor ...

  8. mybatis 源码学习(二)sqlsession

    mybatis 中的sqlsession是一个非常重要的类.上篇我们分析了sessionfactory初始化配置文件,我们继续分析sessionfactory拿到会话进行的操作. 看这里.getMap ...

  9. MyBatis 源码分析 - 配置文件解析过程

    * 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...

  10. MyBatis 源码分析 - 插件机制

    1.简介 一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展.这样的好处是显而易见的,一是增加了框架的灵活性.二是开发者可以结合实际需求,对框架进行拓展,使其能够更好的工作.以 My ...

随机推荐

  1. xargs与管道的区别

    一.直观感受 echo '--help' | cat echo的输出通过管道定向到cat的输入, 然后cat从其标准输入中读取待处理的文本内容, 输出结果: --help echo '--help' ...

  2. MySQL的并行复制多线程复制MTS(Multi-Threaded Slaves)

    MySQL的并行复制多线程复制MTS(Multi-Threaded Slaves) http://www.tuicool.com/articles/m2Unmeq 姜承饶 简称MTS:基于binlog ...

  3. adb移动端测试

    1. Android介绍 Android 是google公司主导的一个开放的手机操作系统,不过目前已经超过了手机的局限,而定位于移动设备的操作系统. Android一词的本义指“机器人”,取名原因很简 ...

  4. 利用 SPL 快速实现 Observer 设计模式

    目录: 1.什么是 SPL 2.SplSubject 和 SplObserver 接口 3.为什么使用 SplObjectStorage 类 4.模拟案例 5.结束语 6.下载资源 什么是 SPL S ...

  5. Scala数据类型的继承结构

    Scala中,所有的值都是类对象,而所有的类,包括值类型,都最终继承自一个统一的根类型Any.统一类型,是Scala的又一大特点.更特别的是,Scala中还定义了几个底层类(Bottom Class) ...

  6. 112A

    #include <iostream> #include <string> #include <cctype> using namespace std; int m ...

  7. 使用spring的aop对Struts2的Action拦截后出现依赖注入为空问题

    两种解决方案: 1.action类继承ActionSupport了后,出现依赖注入为空,要在applicationContext.xml配置中加入:<aop:aspectj-autoproxy ...

  8. 2019.03.20 mvt,Django分页

    MVT模式   MVT各部分的功能:   M全拼为Model,与MVC中的M功能相同,负责和数据库交互,进行数据处理.       V全拼为View,与MVC中的C功能相同,接收请求,进行业务处理,返 ...

  9. vbox 相关

    1.虚拟机vbox 安装mac os 10.12 图文教程: https://www.cnblogs.com/liming2017/p/7566953.html

  10. JMeter登录总是提示用户名不能为空的解决

    已传入参数了呀,还是提示用户名不能为空 解决: 将url拼接上参数 --