mybatis源码解析5---SqlSession解析
由之前解析可知,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解析的更多相关文章
- MyBatis 源码分析 - 映射文件解析过程
1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...
- Spring mybatis源码篇章-MybatisDAO文件解析(二)
前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-MybatisDAO文件解析(一) 默认加载mybatis主文件方式 XMLConfigBuilder ...
- Spring mybatis源码篇章-MybatisDAO文件解析(一)
前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-SqlSessionFactory 加载指定的mybatis主文件 Mybatis模板文件,其中的属性 ...
- Mybatis源码分析之SqlSession和Excutor(二)
通过上一篇文章的分析我们,我初步了解了它是如何创建sessionFactory的(地址:Mybatis源码分析之SqlSessionFactory(一)), 今天我们分析下Mybatis如何创建Sql ...
- myBatis源码学习之SqlSession
在上一篇文章中SqlSessionFactory介绍了生产SqlSession的工厂,SqlSession是一个接口其具体实现类为DefaultSqlSession,SqlSession接口主要定义了 ...
- MyBatis源码部分简单地解析
. 一.解析xml: > org.apache.ibatis.session.SqlSessionFactoryBuilder.build(java.io.InputStream, java.l ...
- Mybatis 源码之Plugin类解析
public class Plugin implements InvocationHandler { private Object target; //目标对象 private Interceptor ...
- mybatis 源码学习(二)sqlsession
mybatis 中的sqlsession是一个非常重要的类.上篇我们分析了sessionfactory初始化配置文件,我们继续分析sessionfactory拿到会话进行的操作. 看这里.getMap ...
- MyBatis 源码分析 - 配置文件解析过程
* 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...
- MyBatis 源码分析 - 插件机制
1.简介 一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展.这样的好处是显而易见的,一是增加了框架的灵活性.二是开发者可以结合实际需求,对框架进行拓展,使其能够更好的工作.以 My ...
随机推荐
- 对vue.js的template编译的理解
简而言之,就是先转化成AST树,再得到的render函数返回VNode(Vue的虚拟DOM节点) 详情步骤: 首先,通过compile编译器把template编译成AST语法树(abstract sy ...
- es中对mapping的理解
(1)往es里面直接插入数据,es会自动建立索引,同时建立type以及对应的mapping (2)mapping中就自动定义了每个field的数据类型 (3)不同的数据类型(比如说text和date) ...
- Filter & Listener
一 监听器的概述 监听器就是一个实现了特定接口的Java类,用于监听另一个Java类的方法调用或属性的改变.当被监听对象发生上述事件后,监听器某个方法将会立即被执行. 即用来监听其他对象的变化,主要应 ...
- pymysql 模块 使用目录
mysql python pymysql模块 基本使用 mysql python pymysql模块 增删改查 插入数据 介绍 commit() execute() executemany() 函数 ...
- C++中为何大量使用类指针
C++的精髓之一就是多态性,只有指针或者引用可以达到多态.对象不行类指针的优点: 第一实现多态. 第二,在函数调用,传指针参数.不管你的对象或结构参数多么庞大,你用指针,传过去的就是4个字节.如果用对 ...
- ionic 版本内更新问题汇总
1.签名不一致导致的更新失败 2.解析软件包出现问题 3.当文件下载完.在android 8.0中不能打开apk包的问题 解决方案:在config.xml中添加: <platform name= ...
- docker知识点
1 安装 http://www.runoob.com/docker/centos-docker-install.html 2 dockerhub官网找到 emqttd 执行 docker pull ...
- Python 全栈开发七 面向对象
一.编程范式 编程是程序员用特定的语法+数据结构+算法组成的代码来告诉计算机如何执行任务的过程 , 一个程序是程序员为了得到一个任务结果而编写的一组指令的集合,正所谓条条大路通罗马,实现一个任务的方式 ...
- np.Linear algebra学习
转自:https://docs.scipy.org/doc/numpy-1.13.0/reference/routines.linalg.html 1.分解 //其中我觉得可以的就是svd奇异值分解吧 ...
- 单例模式多线程安全写法(double-lock-check)
原始版本 public static Object getInstance() { if (instance != null) { return instance; } instance = new ...