Mybatis工作原理(含部分源码)
MyBatis的初始化
1、读取配置文件,形成InputStream
String resource = "mybatis.xml";
// 加载mybatis的配置文件(它也加载关联的映射文件)
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
2、解析XML配置文件,创建SqlSessionFacotry
sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// SqlSessionFactoryBuilder类
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse()); // 开始进行解析了 :)
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
根据Configuration对象来创建SqlSession
MyBatis的SQL查询流程
创建SqlSession
sqlSession = sessionFactory.openSession();
User user = sqlSession.selectOne("com.luo.dao.UserDao.getUserById", 1);
// DefaultSqlSession类
public <T> T selectOne(String statement, Object parameter) {
// 使用的selectList
List<T> list = this.<T>selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
// 多于1个结果时抛出异常
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null; // list.size() == 0
}
}
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 根据mapper.xml文件中的某个SQL语句创建MappedStatement
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();
}
}
执行器在query()方法中,先查询缓存判断是否命中,命中则直接返回,否则从数据库中查询。
// CachingExecutor类
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 将参数与mapper中的sql合并
BoundSql boundSql = ms.getBoundSql(parameterObject);
// 创建缓存的key
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
// BaseExecutor类,创建缓存对象
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
CacheKey cacheKey = new CacheKey();
cacheKey.update(ms.getId()); // mapper文件中的id
cacheKey.update(rowBounds.getOffset()); // 分页偏移
cacheKey.update(rowBounds.getLimit()); // 每页的大小
cacheKey.update(boundSql.getSql()); // sql语句
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
// 模仿 DefaultParameterHandler 逻辑,记录每个参数
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
cacheKey.update(value);
}
}
if (configuration.getEnvironment() != null) {
// issue #176
cacheKey.update(configuration.getEnvironment().getId()); // 每个SqlSessionFacotry的id
}
return cacheKey;
}
// CachingExecutor类
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
/**
* 获取二级缓存
*/
Cache cache = ms.getCache();
if (cache != null) {
// 是否刷新二级缓存
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, parameterObject, boundSql);
@SuppressWarnings("unchecked")
/**
* PerpetualCache是默认二级缓存实现类
* Map<Object, Object> cache = new HashMap<Object, Object>(); map的key就是CacheKey key
* CacheKey中有个hashcode = multiplier * hashcode + 每个update(Object object)object的hashCode()
* update()方法会向updateList添加元素
* CacheKey重写的equals()方法中先判断hashcode是否相等
* 然后用updateList每个对象的equals()判断
* 这两个条件都满足就说明缓存命中,cache.get(key)也就有值
*/
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
// 二级缓存中没有数据
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); // 底层调用queryFromDatabase
tcm.putObject(cache, key, list); // 将结果放入二级缓存
}
return list;
}
}
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); // 底层调用queryFromDatabase
}
// BaseExecutor类,从数据库查询
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
// 先往二级缓存中插入一个占位枚举值
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
// 往二级缓存中写入查询结果
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
一级缓存和二级缓存
一级缓存和二级缓存的命中判断依据是一样的。
一级缓存是SqlSession级别的缓存,不可关闭。同一个SqlSession对象对象执行2遍相同的SQL查询,第二遍查询直接返回缓存结果。
二级缓存是mapper级别的缓存。不同的SqlSession对象执行两次相同的SQL语句,第二次查询直接返回二级缓存中的结果。MyBatis默认是不开启二级缓存的。
未完,待续...
Mybatis工作原理(含部分源码)的更多相关文章
- ThreadLocal 工作原理、部分源码分析
1.大概去哪里看 ThreadLocal 其根本实现方法,是在Thread里面,有一个ThreadLocal.ThreadLocalMap属性 ThreadLocal.ThreadLocalMap t ...
- 面试官:你分析过mybatis工作原理吗?
Mybatis工作原理也是面试的一大考点,必须要对其非常清晰,这样才能怼回去.本文建立在Spring+SpringMVC+Mybatis整合的项目之上. 我将其工作原理分为六个部分: 读取核心配置文件 ...
- SpringMVC关于json、xml自动转换的原理研究[附带源码分析 --转
SpringMVC关于json.xml自动转换的原理研究[附带源码分析] 原文地址:http://www.cnblogs.com/fangjian0423/p/springMVC-xml-json-c ...
- 65、Spark Streaming:数据接收原理剖析与源码分析
一.数据接收原理 二.源码分析 入口包org.apache.spark.streaming.receiver下ReceiverSupervisorImpl类的onStart()方法 ### overr ...
- sobel算子原理及opencv源码实现
sobel算子原理及opencv源码实现 简要描述 sobel算子主要用于获得数字图像的一阶梯度,常见的应用和物理意义是边缘检测. 原理 算子使用两个33的矩阵(图1)算子使用两个33的矩阵(图1)去 ...
- MyBatis工作原理
Mybatis工作原理: 我们的应用程序通过mybatis提供的api,增删改查方法来访问数据库,api底层调用了jdbc ,只不过mybatis对jdbc的封装是不完全封装,里面的sql语句需要我们 ...
- MyBatis框架的使用及源码分析(十一) StatementHandler
我们回忆一下<MyBatis框架的使用及源码分析(十) CacheExecutor,SimpleExecutor,BatchExecutor ,ReuseExecutor> , 这4个Ex ...
- MyBatis框架的使用及源码分析(九) Executor
从<MyBatis框架的使用及源码分析(八) MapperMethod>文中我们知道执行Mapper的每一个接口方法,最后调用的是MapperMethod.execute方法.而当执行Ma ...
- 深入理解NIO(三)—— NIO原理及部分源码的解析
深入理解NIO(三)—— NIO原理及部分源码的解析 欢迎回到淦™的源码看爆系列 在看完前面两个系列之后,相信大家对NIO也有了一定的理解,接下来我们就来深入源码去解读它,我这里的是OpenJDK-8 ...
- MyBatis、Spring、SpringMVC 源码下载地址
MyBatis.Spring.SpringMVC 源码下载地址 github mybatis https://github.com/fengyu415/MyBatis-Learn.git spring ...
随机推荐
- jquery获取input的checked属性
1.经常需要判断某个按钮是否被选中. 2.基于jquery. <!DOCTYPE html> <html lang="en"> <head> & ...
- Zookeeper初见
这是Zookeeper学习总结 的系列文章. ZK简介 ZK部署及运行 ZK的常用API 创建会话 创建节点 删除节点 读取数据节点 更新数据 检测节点是否存在 ZK的开源封装
- 【LeetCode题解】237_删除链表中的节点
目录 237_删除链表中的节点 描述 解法 思路 Java 实现 Python 实现 237_删除链表中的节点 描述 请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除 ...
- Shell脚本编写2------有关变量
shell脚本中变量定义方式十分简单,直接将值赋值给变量较好例如 :name="tuanzhang"注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样.变量命名 ...
- C学习笔记(1)-结构体、预处理与多文件结构程序设计
一.结构体的定义与使用 #include <stdio.h> //定义结构体(类似模板) typedef struct { char name[50]; int age; char add ...
- bootstrap、qjuery、插件 、字体网页
http://www.bootcss.com/ 前端框架bootstrap http://www.fontawesome.com.cn/faicons/ 字体图标库 https://daneden.g ...
- 【读】为什么BIO效率低下
原因: 假如有10000个连接,4核CPU ,那么bio 就需要一万个线程,而nio大概就需要5个线程(一个接收请求,四个处理请求).如果这10000个连接同时请求,那么bio就有10000个线程抢四 ...
- ssm项目快速搭建(配置)
核心jar包 <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncod ...
- RabbitMQ--学习资源汇
1.RabbitMQ 实战教程 文集(alibaba) 2.RabbitMQ从入门到精通(9篇系列博文 )(anzhsoft2008 ) 3. RabbitMQ目录(nick's blog) 使用 ...
- Django基础七之Ajax
一 Ajax简介 1.简介 AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”.即使用Javascript语言与服务器进行异 ...