关键词:
文章目录
- 1、工作原理
- 2、MyBatis中的缓存
- 3、如何扩展MyBatis中的缓存
- 4、MyBatis中的涉及的设计模式
- 5、谈谈你对SqlSessionFactory的理解
- 6、谈谈你读SqlSession的理解
- 7、谈谈你对MyBatis的理解
- 8、谈谈MyBatis中分页的理解
- 9、谈谈MyBatis中的插件原理
- 10、不同Mapper中的id是否可以相同?
- 11、谈谈对MyBatis架构设计的理解
- 12、谈谈对传统JDBC开发的不足
- 13、MyBatis中数据源模块的设计
- 14、MyBatis中事务模块的设计
- 15、谈谈你对Mapper接口的设计理解
- 16、谈谈你对Reflector模块的理解
- 17、MyBatis的类型转换模块
- 18、整合MyBatis
1、工作原理
1.1 初始化
1.1.1 系统启动的时候,加载解析全局配置文件和相应的映射文件
// 1.获取配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
全局配置文件:mybatis-config.xml
映射文件:mapper/*.xml
// 2.加载解析配置文件并获取 SqlSessionFactory 对象
// SqlSessionFactory 的实例我们没有通过 DefaulSqlSessionFactory 直接来获取
// 而是通过一个 Builder 对象来建造的
// SqlSessionFactory 生产 SqlSession 对象的 SqlSessionFactory 应该是单例
// 全局配置文件和映射文件 也只需要在 系统启动的时候完成加载操作
// 通过建造者模式来 构建复杂的对象: 1.完成配置文件的加载解析 2.完成 SqlSessionFactory 的创建
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
加载解析的相关信息存储在SqlSessionFactory对象的Configuration属性里,
1.1.2 建造者模式帮助我们解决复杂对象的创建:
- 完成配置文件的加载解析
- 完成 SqlSessionFactory 的创建
1.2 处理SQL请求的流程
通过工厂得到SqlSession对象
// 3.根据 SqlSessionFactory 对象获取 SqlSession 对象
SqlSession sqlSession = factory.openSession();
1.2.1 通过sqlSession中提供的API方法来操作数据库
// 4.通过sqlSession中提供的API方法来操作数据库
List<User> list = sqlSession.selectList("com.boge.mapper.UserMapper.selectUserList");
1.2.2 获取接口的代码对象-得到的其实是 通过JDBC代理模式获取的一个代理对象
// 获取接口的代码对象-得到的其实是 通过JDBC代理模式获取的一个代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
1.2.3 处理完请求之后,需要关闭会话SqlSession
//5.关闭会话
sqlSession.close();//关闭session 清空一级缓存
1.3 底层
全局配置文件的加载解析:Configuration
映射文件的加载解析:Configuration.mappedStatements
/**
* MappedStatement 映射
*
* KEY:`$namespace.$id`
*/
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<>("Mapped Statements collection");
生产了DefaultSqlsession实例对象,完成了Executor对象的创建,以及二级缓存CachingExecutor的装饰,同时完成了插件逻辑的植入。
selectOne():二级缓存->一级缓存->数据库插入
// 1. 读取配置文件,读成字节输入流,注意:现在还没解析
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2. 解析配置文件,封装Configuration对象 创建DefaultSqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
// 3. 生产了DefaultSqlsession实例对象 设置了事务不自动提交 完成了executor对象的创建
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4.(1)根据statementid来从Configuration中map集合中获取到了指定的MappedStatement对象
//(2)将查询任务委派了executor执行器
User user = sqlSession.selectOne("com.lagou.mapper.IUserMapper.findById",1);
System.out.println(user);
User user2 = sqlSession.selectOne("com.lagou.mapper.IUserMapper.findById",1);
System.out.println(user2);
// 5.释放资源
sqlSession.close();
1.3.1 原理图:
1.3.2 源码结构:
2、MyBatis中的缓存
2.1 缓存的作用
降低数据源的访问频率,从而提高数据源的处理能力,提高服务器的响应速度。
2.2 缓存的设计
2.2.1 架构设计
通过装饰者模式对Cache接口的工作做增强
装饰者 | 作用 |
---|---|
BlockingCache | 阻塞的 Cache 实现类 |
FifoCache | 基于先进先出的淘汰机制的 Cache 实现类 |
LoggingCache | 支持打印日志的 Cache 实现类 |
LruCache | 基于最少使用的淘汰机制的 Cache 实现类 |
ScheduledCache | 定时清空整个容器的 Cache 实现类 |
SerializedCache | 支持序列化值的 Cache 实现类 |
SoftCache | 软引用缓存装饰器 |
SynchronizedCache | 同步的 Cache 实现类 |
TransactionalCache | 支持事务的 Cache 实现类,主要用于二级缓存中 |
WeakCache | 弱引用缓存装饰器 |
2.2.2 一级缓存和二级缓存
一级缓存:session级别,默认开启
<!-- STATEMENT级别的缓存,使一级缓存,只针对当前执行的这一statement有效 -->
<setting name="localCacheScope" value="STATEMENT"/>
二级缓存:SqlSessionFactory级别(工厂/进程级别)
开启二级缓存:
-
在mybatis配置文件中配置cacheEnabled为true
<!-- 控制全局二级缓存,默认ture--> <setting name="cacheEnabled" value="true"/> <!-- 延迟加载的全局开关。开启时,所有关联对象都会延迟加载。默认false --> <setting name="lazyLoadingEnabled" value="true"/>
-
在映射文件中添加cache标签,可以在cache标签中更细致的增加配置
<!--二级缓存开启--> <cache />
-
命名空间下的所有标签放开二级缓存
-
可以通过在标签中添加 useCache=false 指定api不走二级缓存
mybatis-config.xml
2.2.3 缓存的处理顺序
- 获取mapper映射文件中cache标签里的配置MappedStatement.getCache()
- 如果cache配置不为空,从二级缓存中查找(List) TransactionalCacheManager.getObject(cache, key);
- 如果没有值,则执行查询, Executor.query()这个查询实际也是先走一级缓存查询,
- 一级缓存也没有的话,则进行DB查询
- 先将查询到的结果放入缓存TransactionalCacheManager.putObject(cache, key, list),再返回结果
2.2.3.1 先在二级缓存中查找
原因:找到概率更高,性能角度。
一级缓存 | 二级缓存 | |
---|---|---|
作用域 | SqlSession级别 | SqlSessionFactory级别 |
找到概率 | 5% | 90% |
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException
// 从 MappedStatement 中获取 Cache,注意这里的 Cache 是从MappedStatement中获取的
// 也就是我们上面解析Mapper中<cache/>标签中创建的,它保存在Configration中
// 我们在初始化解析xml时分析过每一个MappedStatement都有一个Cache对象,就是这里
Cache cache = ms.getCache();
// 如果配置文件中没有配置 <cache>,则 cache 为空
if (cache != null)
//如果需要刷新缓存的话就刷新:flushCache="true"
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null)
// 暂时忽略,存储过程相关
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
// 从二级缓存中,获取结果
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null)
// 如果没有值,则执行查询,这个查询实际也是先走一级缓存查询,一级缓存也没有的话,则进行DB查询
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
// 缓存查询结果
tcm.putObject(cache, key, list); // issue #578 and #116
// 如果存在,则直接返回结果
return list;
// 不使用缓存,则从数据库中查询(会查一级缓存)
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
2.2.3.2 Executor.query()先走一级缓存查询,一级缓存也没有的话,则进行DB查询
/**
* 记录嵌套查询的层级
*/
protected int queryStack;
/**
* 本地缓存,即一级缓存
*/
protected PerpetualCache localCache;
@SuppressWarnings("unchecked")
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
// 已经关闭,则抛出 ExecutorException 异常
if (closed)
throw new ExecutorException("Executor was closed.");
// 清空本地缓存,如果 queryStack 为零,并且要求清空本地缓存。
if (queryStack == 0 && ms.isFlushCacheRequired())
clearLocalCache();
List<E> list;
try
// queryStack + 1
queryStack++;
// 从一级缓存中,获取查询结果
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
// 获取到,则进行处理
if (list != null)
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
// 获得不到,则从数据库中查询
else
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
finally
// queryStack - 1
queryStack--;
if (queryStack == 0)
// 执行延迟加载
for (DeferredLoad deferredLoad : deferredLoads)
deferredLoad.load();
// issue #601
// 清空 deferredLoads
deferredLoads.clear();
// 如果缓存级别是 LocalCacheScope.STATEMENT ,则进行清理
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT)
// issue #482
clearLocalCache();
return list;
3、如何扩展MyBatis中的缓存
3.1 架构理解
3.2 实际开发
/**
* 永不过期的 Cache 实现类,基于 HashMap 实现类
*
* @author Clinton Begin
*/
public class PerpetualCache implements Cache
/**
* 缓存容器
*/
private Map<Object, Object> cache = new HashMap<>();
@Override
public void putObject(Object key, Object value)
cache.put(key, value);
3.2.1 自定义三级缓存
创建Cache接口的实现,重写putObject和getObject方法
在mapper映射文件中的cache标签里增加type属性,关联自定义的Cache接口的实现
<cache type="org.mybatis.caches.ehcache.EhcacheCache" />
如果未添加type,会默认读取 PERETUAL (二级缓存)
4、MyBatis中的涉及的设计模式
4.1 从整体架构设计分析
4.1.1 基础模块:
cache缓存模块:装饰器模式
Cache接口 定义了缓存的基本行为
PerpetualCache基于Cache实现,针对于缓存的功能 1.缓存数据淘汰;2.缓存数据的存放机制;3.缓存数据添加是否同步【阻塞】;4.缓存对象是否同步处理…做了增强处理–>代理模式
以及很多装饰类,灵活增强出适用于不同业务场景的Cache实现
logging日志模块:适配器模式、策略模式、代理模式
帮助我们适配不同的日志框架
Log接口针对不同日志框架,有不同的实现类,做增强处理
reflection反射模块:工程模式、装饰器模式
datasource数据源:工程模式
transaction事务模块:工厂模式
SqlSessionFactory:SqlSessionFactoryBuilder建造者模式
5、谈谈你对SqlSessionFactory的理解
- 目的:创建SqlSession对象
- 单例,在应用程序(服务)中只保存唯一的一份
- SqlSessionFactory对象的创建是通过SqlSessionFactoryBuilder,
- 同时也完成了全局配置文件Configuration和相关映射文件Mapper的加载和解析操作。
- 涉及到了工厂模式和建造者模式
/**
* 构造 SqlSessionFactory 对象
*
* @param reader Reader 对象
* @param environment 环境
* @param properties Properties 变量
* @return SqlSessionFactory 对象
*/
@SuppressWarnings("Duplicates")
public SqlSessionFactory build(Reader reader, String environment, Properties properties)
try
// 创建 XMLConfigBuilder 对象
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
// 执行 XML 解析
// 创建 DefaultSqlSessionFactory 对象
return build(parser.parse());
catch (Exception e)
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
finally
ErrorContext.instance().reset();
try
reader.close();
catch (IOException e)
// Intentionally ignore. Prefer previous error.
/**
* 1.我们最初调用的build
*/
public SqlSessionFactory build(InputStream inputStream)
//调用了重载方法
return build(inputStream, null, null);
/**
* 解析 XML
*
* 具体 MyBatis 有哪些 XML 标签,参见 《XML 映射配置文件》http://www.mybatis.org/mybatis-3/zh/configuration.html
*
* @param root 根节点
*/
private void parseConfiguration(XNode root)
try
//issue #117 read properties first
// 解析 <properties /> 标签
propertiesElement(root.evalNode("properties"));
// 解析 <settings /> 标签
Properties settings = settingsAsProperties(root.evalNode("settings"));
// 加载自定义的 VFS 实现类
loadCustomVfs(settings);
// 解析 <typeAliases /> 标签
typeAliasesElement(root.evalNode("typeAliases"));
// 解析 <plugins /> 标签
pluginElement(root.evalNode("plugins"));
// 解析 <objectFactory /> 标签
objectFactoryElement(root.evalNode("objectFactory"));
// 解析 <objectWrapperFactory /> 标签
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 解析 <reflectorFactory /> 标签
reflectorFactoryElement(root.evalNode("reflectorFactory"));
// 赋值 <settings /> 到 Configuration 属性
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
// 解析 <environments /> 标签
environmentsElement(root.evalNode("environments"));
// 解析 <databaseIdProvider /> 标签
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 解析 <typeHandlers /> 标签
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析 <mappers /> 标签
mapperElement(root.evalNode("mappers"));
catch (Exception e)
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
6、谈谈你读SqlSession的理解
6.1 SqlSession
-
作用:通过相关API来实现对应的数据的操作
-
SqlSession对象的获取需要通SqlSessionFactory来实现,
-
作用域是会话级别,当一个新的会话到来的时候,需要新建一个SqlSession对象;当一个会话结束后,需要关闭相关会话资源
-
处理请求的方式:
1.通过相关crud的API直接处理
2.通过getMapper(xx.xml)来获取相关mapper接口的代理对象来处理
6.2 SqlSession的安全问题
6.2.1 非线程安全:
6.2.2 Spring中是如何解决DefaultSqlSession的数据安全问题?
- DefaultSqlSession是非线程安全的,也就意味着我们不能把DefaultSqlSession声明在成员变量中。
- 每个线程都应该有自己的SqlSession实例。
- 最佳作用域是请求或方法作用域
- 决不能将SqlSession实例引用放在一个类的静态域,甚至一个类的实例变量也不行。
- 应该将SqlSession放在一个和HTTP请求相似的作用域中,每次请求打开一个SqlSession,返回一个响应后就关闭他,关闭操作放在finally块中。
- Spring中提供了SqlSessionTemplate来实现SqlSession的相关定义。
- 其中每一个方法都通过SqlSessionProxy来操作,这是一个动态代理对象。
- 在动态代理对象中通过方法级别的DefaultSqlSession来实现相关的数据库操作。
7、谈谈你对MyBatis的理解
- 使用频率最高的ORM框架、持久层框架
- 提供了非常方便的API实现CRUD
- 支持灵活的缓存处理方案,一级缓存、二级缓存、三级缓存
- 支持相关的延迟数据加载处理
- 还提供了非常多的灵活标签,来实现复杂的业务处理,if forech where trim set bind…
- 相比Hibernate(全自动化)会更加灵活
8、谈谈MyBatis中分页的理解
8.1 谈谈分页的理解:
-
数据库层面SQL:
MySQL:LIMIT
Oracle:rowid
8.2 分页的实现
8.2.1 逻辑分页:RowBounds
8.2.2 物理分页:拦截器实现,执行分页语句的组装
9、谈谈MyBatis中的插件原理
9.1 插件设计的目的:
方便开发人员实现对MyBatis功能的增强
设计中MyBatis允许映射语句执行过程中的某一点进行拦截调用,允许使用插件拦截的方法包括:
9.2 实现原理:
9.2.1 创建自定义Java类,通过@Interceptor注解来定义相关的方法签名
9.2.2 在对应的配置文件中通过plugin来注册自定义的拦截器
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
</plugin>
</plugins>
9.2.3 拦截器的作用
- 检查执行的SQL
- 对执行SQL的参数做处理
- 对查询的结果做装饰处理
- 对查询SQL做分表处理
10、不同Mapper中的id是否可以相同?
可以相同,每一个映射文件的namespace都会设置为对应的mapper接口的全类路径名称
保证了每个Mapper映射文件的namespace是唯一的。
11、谈谈对MyBatis架构设计的理解
11.1 接口层
面向开发者,提供相关API
11.2 核心层
核心功能的实现:增删改查操作
11.3 基础模块
支撑核心层来完成核心的功能
查看详情
mybatis插件机制源码解析(代码片段)
引言本篇源码解析基于mybatis3.5.8版本。首先需要说明的是,本篇文章不是mybatis插件开发的教程,而是从源码层面分析mybatis是如何支持用户自定义插件开发的。mybatis的插件机制,让其扩展能力大大增加。比如我们项目... 查看详情
mybatis——源码解析mybatis框架底层的执行原理(代码片段)
文章目录:1.前言2.案例项目源码3.MyBatis源码解析底层执行原理3.1读取mybatis配置文件创建出SqlSeesionFactory对象3.2通过SqlSeesionFactory对象进而创建出SqlSession对象3.3通过SqlSession的getMapper获取到接口代理对象3.4通过mapper接口的代理... 查看详情
mybatis——源码解析mybatis框架底层的执行原理(代码片段)
文章目录:1.前言2.案例项目源码3.MyBatis源码解析底层执行原理3.1读取mybatis配置文件创建出SqlSeesionFactory对象3.2通过SqlSeesionFactory对象进而创建出SqlSession对象3.3通过SqlSession的getMapper获取到接口代理对象3.4通过mapper接口的代理... 查看详情
mybatis源码:mybatis配置解析(代码片段)
Mybatis有两个核心配置,全局配置会影响Mybatis的执行;Mapper配置定义了查询的SQL,下面我们来看看Mybatis是如何加载配置文件的。 本文基于Mybatis源码(一):源码编译准备中案例进行分析,主要示例代码如下:publicstaticvoidma... 查看详情
源码解析mybatis架构(代码片段)
MySQL安装与启动安装并启动一个关系型数据是调试MyBatis源码的基础。目前很多互联网公司都将MySQL作为首选数据库,所以这里我也就选用MySQL数据库来配合调试MyBatis源码。1.下载MySQL首先,从MySQL官网下载最新版本的MySQLComm... 查看详情
#mybatis源码解析之配置加载(代码片段)
Mybatis源码解析之配置加载(二)这一篇是承接上一篇文章Mybatis源码解析之配置加载(一),上一篇原本是想把整个配置加载都分析完全,然后发现内容还是比较多,所以决定分成两篇来说好了,现在就开始剩下的配置分析... 查看详情
mybatis源码解析mybatis解析全局配置文件(代码片段)
MyBatis介绍MyBatis是一个持久层的ORM框架,使用简单,学习成本较低。可以执行自己手写的SQL语句,比较灵活。但是MyBatis的自动化程度不高,移植性也不高,有时从一个数据库迁移到另外一个数据库的时候需要... 查看详情
mybatis源码解析之配置加载(代码片段)
Mybatis源码解析之配置加载(一)用了好几年的mybatis了,但是很少来钻研mybatis原理所在,最近抽出空来,就把这一整套源码都研究了下,然后发现就是这些东西,mybatis没啥难度,于是决定把研究的这一整套写... 查看详情
mapperscannerconfigurer源码解析(代码片段)
声明:源码基于mybatis-spring1.3.2前文首先在阅读本文前需要明白整合后的使用方式以及熟悉MyBatis本身的工作原理,再者如果对于本文相关知识点不熟悉的可以参考下述文章。MyBatis与Spring整合SqlSessionTemplate源码解析Spring包扫描机制... 查看详情
mybaties源码解析(代码片段)
...分也花了几个小时的时间去琢磨但是感觉值了,我觉得对mybaties的原理更加清晰了 学了mybaties 查看详情
mybatis源码-sqlsession门面模式&selectlist源码解析(代码片段)
文章目录Pre工程概览pom.xmlmybatis-config.xmlUserMapper测试类selectList源码解析附SQLlog4j.propertiesapp.propertiesUserPre如果MyBatis的基础用法还不熟悉,31篇入门博客拿走不谢戳戳戳—>https://blog.csdn.net/yangshangwei/category_7205317 查看详情
mybatis源码-解析配置文件之配置文件configuration解析(超详细,值得收藏)(代码片段)
1.简介1.1系列内容本系列文章讲解的是mybatis解析配置文件内部的逻辑,即Readerreader=Resources.getResourceAsReader("mybatis-config.xml");SqlSessionFactorysqlSessionFactory=newSqlSessionFactoryBuilder().build(reader);其背后的逻辑。1.2适合对象了解 查看详情
mybatis源码分析四xml解析与核心对象的构建(代码片段)
四、XML解析与核心对象的构建到此为止,已经把MyBatis核心的代理以及与JDBC的交互逻辑梳理完成,下面来看看,配置文件以及mapper.xml的加载和SqlSessionFactory的创建。InputStreaminputStream=Resources.getResourceAsStream("MyBatis/MyBatis-config.xml");... 查看详情
mybatis源码解析9---执行器executor解析(代码片段)
...ate去执行的。本文就分析下sql的执行器-----ExecutorExecutor是mybatis的sql执行器,SqlSession是面向程序的,而Executor则就是面向数据库的,先看下Executor接口的方法有 查看详情
mybatis的缓存机制源码分析之一级缓存解析(代码片段)
文章目录引言正文引言本篇源码解析基于mybatis3.5.8版本。MyBatis中的缓存指的是MyBatis在执行一次SQL查询时,在满足一定的条件下,会把这个sql和对应的查询结果缓存起来。当再次执行相同SQL语句的时候,就会直接从缓... 查看详情
mybatis的缓存机制源码分析之二级缓存解析(代码片段)
引言本篇源码解析基于mybatis3.5.8版本。MyBatis中的缓存指的是MyBatis在执行一次SQL查询时,在满足一定的条件下,会把这个sql和对应的查询结果缓存起来。当再次执行相同SQL语句的时候,就会直接从缓存中进行提取,... 查看详情
mybatis——源码解析mybatis框架底层的执行原理(代码片段)
文章目录:1.前言2.案例项目源码3.MyBatis源码解析底层执行原理3.1读取mybatis配置文件创建出SqlSeesionFactory对象3.2通过SqlSeesionFactory对象进而创建出SqlSession对象3.3通过SqlSession的getMapper获取到接口代理对象3.4通过mapper接口的代理... 查看详情