目录
一、为什么要用MyBatis
1、Mybatis简介
MyBatis是一款优秀的基于ORM的半自动轻量级持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以使用简单的XML或注解来配置和映射原生类型、接口和Java的POJO (Plain Old Java Objects,普通老式Java对象) 为数据库中的记录 。
2、持久化框架对比
方式 | 优点 | 缺点 | 场景 |
---|---|---|---|
Hibernate | 不再需要编写SQL就可以通过映射关系来操作数据库;提升开发效率 | 当多表关联超过3个时Hibermate的级联会损失很多性能;灵活性太差 | 适合性能要求不太苛刻的系统,不适合需要大量复杂查询的系统 |
SpringJDBC | 内嵌Spring框架中、支持AOP;提供了统一的异常处理,框架处理了异常;事务管理 | 只是对原生JDBC进行一层非常薄的封装,没有缓存 | 需要在代码中嵌入SQL语句,适用中小型项目 |
Mybatis | 灵活性更好,满足定制SQL和性能优化的需求 | 编写SQL和映射规则,工作量相对大 | 性能要求高、响应快、灵活的系统;sql修改、优化比较方便 |
二、Mybatis架构原理
1、架构设计
我们把Mybatis的功能架构分为三层:
(1) API接口层:提供给外部使用的接口 API,开发人员通过这些本地API来操纵数据库。接口层一接收到
调用请求就会调用数据处理层来完成具体的数据处理。 MyBatis和数据库的交互有两种方式:
a. 使用传统的MyBatis提供的API ;
b. 使用Mapper代理的方式
(2) 数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
(3) 基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是 共 用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑
2、主要构件及其相互关系
1)Mybatis主要构件描述
构件 | 描述 |
---|---|
SqlSession | 作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能 |
Executor | MyBatis执行器,是MyBatis调度的核心,负责SQL语句的生成和查询缓存的维护 |
StatementHandler | 封装了JDBC Statement操作,负责对JDBC statement的操作,如设置参数、将Statement结果集转换成List集合。 |
ParameterHandler | 负责对用户传递的参数转换成JDBC Statement所需要的参数 |
ResultSetHandler | 负责将JDBC返回的ResultSet结果集对象转换成List类型的集合; |
TypeHandler | 负责java数据类型和jdbc数据类型之间的映射和转换 |
MappedStatement | MappedStatement维护了一条<select | update | delete | insert>节点的封装 |
ResultMap | 保存<select | update | delete | insert>中配置的结果集类型 |
SqlSource | 负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封 装到BoundSql对象中,并返回 |
BoundSql | 表示动态生成的SQL语句以及相应的参数信息 |
2)Mybatis主要构件结构
三、Mybatis缓存原理
1、Mybatis缓存简介
缓存就是内存中的数据,常常来自对数据库查询结果的保存,使用缓存,我们可以避免频繁的与数据库进行交互,进而提高响应速度。
mybatis也提供了对缓存的支持,分为一级缓存与二级缓存,可以通过下图来理解
2)二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
2、一级缓存
1)简介
一级缓存是SqlSession级别的缓存,默认开启。在操作数据库时需要构造SqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据结构。不同的SqlSession之间的缓存数据区域(HashMap)是互不影响的。
2)缓存生命周期流程图
2、二级缓存
二级缓存的原理和一级缓存原理一样,第一次查询,会将数据放入缓存中,然后第二次查询则会直接去缓存中取。但是一级缓存是基于sqlSession的,而二级缓存是基于mapper文件的namespace的,也就是说多个sqlSession可以共享一个mapper中的二级缓存区域,并且如果两个mapper的namespace相同,即使是两个mapper,那么这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域中
需要注意:二级缓存存的不是对象而是数据,一级缓存存的是对象(同一个地址)而不是数据
一级缓存从BaseExecutor开始分析,而二级缓存需要从CachingExecutor分析,生命周期流程图和一级缓存基本一致。
3、缓存问题
通过上面对于缓存的介绍,我们发现Mybatis缓存都是本地缓存,在分布式的情况下会出现缓存一致性问题。例如一级缓存都是默认开启的为什么没有出现这类问题呢?
1)一级缓存
前面所提到的,一级缓存是SqlSession级别的缓存,默认开启。
SqlSessioin是什么?
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的范围是请求或方法范围。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。也绝不能将 SqlSession 实例的引用放在任何类型的管理范围中,比如 Serlvet 架构中的 HttpSession。如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的范围中。换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。
所以结论:如果多个请求同一个事务中,那么多个请求都在共用一个SqlSession,反之每个请求都会创建一个SqlSession。
2)二级缓存
二级缓存是基于mapper文件的namespace的缓存,所以在分布式的情况下会很可能会缓存一致性问题。所以官方给的方案是手动开始。
四、Mybatis中设计模式应用
1、Builder构建者模式
1)背景
Builder模式的定义是"将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表 示。”,它属于创建类模式,一般来说,如果一个对象的构建比较复杂,超出了构造函数所能包含的范 围,就可以使用工厂模式和Builder模式,相对于工厂模式会产出一个完整的产品,Builder应用于更加 复杂的对象的构建,甚至只会构建产品的一个部分,直白来说,就是使用多个简单的对象一步一步构建 成一个复杂的对象。
2)Mybatis中的应用
SqlSessionFactory 的构建过程:
Mybatis的初始化工作非常复杂,不是只用一个构造函数就能搞定的。所以使用了建造者模式,使用了大量的Builder,进行分层构造,核心对象Configuration使用了XmlConfigBuilder来进行构造
在Mybatis环境的初始化过程中,SqlSessionFactoryBuilder会调用XMLConfigBuilder读取所有的 MybatisMapConfig.xml 和所有的 *Mapper.xml 文件,构建 Mybatis 运行的核心对象 Configuration 对 象,然后将该Configuration对象作为参数构建一个SqlSessionFactory对象。
其中 XMLConfigBuilder 在构建 Configuration 对象时,也会调用 XMLMapperBuilder 用于读取 *Mapper 文件,而XMLMapperBuilder会使用XMLStatementBuilder来读取和build所有的SQL语句。
在这个过程中,有一个相似的特点,就是这些Builder会读取文件或者配置,然后做大量的XpathParser 解析、配置或语法的解析、反射生成对象、存入结果缓存等步骤,这么多的工作都不是一个构造函数所 能包括的,因此大量采用了 Builder模式来解决
SqlSessionFactoryBuilder类根据不同的输入参数来构建SqlSessionFactory这个工厂对象
2、工厂模式
1)背景
在Mybatis中比如SqlSessionFactory使用的是工厂模式,该工厂没有那么复杂的逻辑,是一个简单工厂模式。
简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于创建型模式。
在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
2)Mybatis中的应用
Mybatis中执行Sql语句、获取Mappers、管理事务的核心接口SqlSession的创建过程使用到了工厂模式。
如SqlSessionFactory用来负责SqlSession的创建
可以看到,该Factory的openSession ()方法重载了很多个,来构建核心的SqlSession对象。 在DefaultSqlSessionFactory的默认工厂实现里,有一个方法可以看出工厂怎么产出一个产品:
这是一个openSession调用的底层方法,该方法先从configuration读取对应的环境配置,然后初始化 TransactionFactory 获得一个 Transaction 对象,然后通过 Transaction 获取一个 Executor 对象,最后通过configuration、Executor、autoCommit三个参数构建了 SqlSession。
3、代理模式
1)背景
代理模式(Proxy Pattern):给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式的英文叫做Proxy,它是一种对象结构型模式。
2)Mybatis中的应用
代理模式可以认为是Mybatis的核心使用模式,正是由于这个模式,我们只需要编写Mapper.java接口,不需要实现,由Mybatis后台帮我们完成具体SQL的执行。 当我们使用Configuration的getMapper方法时,会调用mapperRegistry.getMapper方法,而该方法又会调用 mapperProxyFactory.newInstance(sqlSession)来生成一个具体的代理:
在这里,先通过T newInstance(SqlSession sqlSession)方法会得到一个MapperProxy对象,然后调用T newInstance(MapperProxy mapperProxy)生成代理对象然后返回。而查看MapperProxy的代码,可以看到如下内容:
非常典型的,该MapperProxy类实现了InvocationHandler接口,并且实现了该接口的invoke方法。通过这种方式,我们只需要编写Mapper.java接口类,当真正执行一个Mapper接口的时候,就会转发给 MapperProxy.invoke方法,而该方法则会调用后续的 sqlSession.cud>executor.execute>prepareStatement 等一系列方法,完成 SQL 的执行和返回
五、阅读框架的反思
知其然也要知其所以然,一门技术只是会用那只是入门,只有熟练了,了解了其中原理才有用。用了一门技术遇到问题后,如何去快速解决,解决的思路是什么,一般解决问题的能力能直接体现你的综合能力。因为有的棘手问题不是短时间能解决或者说不是能顺利解决的,你需要利用你的技术广度和深度再加上你以往解决问题的经验。
我们虽成为不了北乔峰,但可以学习南慕容,最后要成为杨过。
自己要会造轮子先要学会别人怎么造轮子,天龙八部中南北两大高手,北乔峰是天资很好靠名师指点最后成为江湖顶级高手,南慕容是靠学习各大帮派的武功也成为了高手,射雕英雄传中杨过从蛤蟆功开始学起,中间经过神雕指点成为江湖高手,最后自己悟出黯然销魂掌终成一代大侠。
向乔峰一样天资聪明,武功根基良好的人并不多,大部分都是资质一般,要靠后天勤学苦练,在学习中不断悟道,总有一天也能有成。
学习源码也是一样,看一遍看不懂很正常,看一遍有一遍的理解,看不懂就在看一遍,直到看懂为止,看的过程要多思考,子曰:“学而不思则罔,思而不学则殆”,这个地方为什么要这样设计,如果是你应该怎么设计,怎么才是更好的设计,学习别人的设计思想,你可以实现一个优秀的框架。在阅读优秀框架源码的过程,我们吸收其中的设计模式和架构模式,对比平日里自己的设计方式,站在巨人的肩膀上思考,一步步提升境界。
术生道,道生术,术中有道,道中有术,道术相连才能生生不息
这里的术指的是架构设计模式,道指的是架构思想,两者是相辅相成的,通过学习前人的经验,行成思考、对比,然后去实践你的架构设计,不断的在实践中理解、思考架构设计思想,在将得到的架构思想再去实践,你用架构师的思想去思考问题解决问题你就是架构师,没有更好的架构,只有合适的架构,好的代码不是写出来的,如同娃娃学步一样,先学先看,然后在改再改,一步步尝试走出来的,架构也不是一层不变,好的架构是演变而来。要想成为一个好的架构师,学习和实践是我们一直坚持要做的事情,学无止境,学海无涯。