目录
MyBatis根据功能架构分为三个层:(体系结构) ★★★★★
概念:
MyBatis是一个持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
MyBatis的架构:
- SqlMapConfig.xml:此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。
- mapper.xml文件:即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
- 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
- 由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
- mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
- Mapped Statement:也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
- Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,
- Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
- Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
ORM是Object和Relation之间的映射,包括Object->Relation和Relation->Object两方面。Hibernate是个完整的ORM框架,而MyBatis完成的是Relation->Object
MyBatis根据功能架构分为三个层:(体系结构) ★★★★★
1、API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。(包括:增删改查,config)
2、数据处理层(核心):负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
3、基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
原理:
在不考虑Spring的情况下,MyBatis依靠SessionFactory和Session来实现原理操作。结构如下:
SqlSessionFactory:会话工厂,通过配置文件产生的。类加载器加载再Builder。线程安全的,一旦被创建,应该在应用执行期间都存在.
SqlSession(不能共享):是执行持久化操作的独享,类似于jdbc的connection,或Servlet中的Session对象。是应用程序与持久层之间执行交互操作的一个单线程对象,也是MyBatis执行持久化操作的关键对象.在应用层和持久层之间执行交互操作的。底层封装了JDBC连接,使用完SqlSeesion之后要关闭Session。
执行器Exceutor:
执行器(Exceutor):跟SqlSession是绑定一起的,每个会话都拥有一个新的执行器,它的作用就是负责执行相应的SQL命令,执行器可以看成是Servlet中的Request对象,就是会话中对数据库的一次请求访问.
BaseExecuto:抽象类,在创建时根据传递过来的ExecutorType来创建相应子类
实现类:
simpleExecute:每执行一次update或select,就开启一个PrepareStatement对象,用完立刻关闭PrepareStatement对象 (每次都使用一个新的ps,是要完成后释放ps。close())
ReuseExecute:执行update或select,以sql作为key查找PrepareStatement对象,存在就使用,不存在就创建,用完后,不关闭PrepareStatement对象,而是放置于Map<String, PrepareStatement>内,供下一次使用.( Map缓冲池,第一次创建ps,使用完成后,放到map缓冲池中,以供下次使用)
BatchExecute:执行update,将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个PrepareStatement对象,每个PrepareStatement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理的.( 批处理)
CachingExecute:先从缓存中获取查询结果,存在就返回,不存在,再委托给Executor delegate去数据库取,delegate可以是上面任一的SimpleExecutor、ReuseExecutor、BatchExecutor.( 只针对查询,缓存操作。)
处理器Handler:
参数处理器(ParameterHandler):形参匹配处理(相当于给?号赋值操作)
结构处理器(ResultSetHandler):返回结果匹配处理(类似于rs.getXxx效果)
sql查询处理器(StatementHandler):查询匹配处理(负责对PrepareStatement对象封装和初始化操作)
类型处理器(TypeHandler):总的类型处理器(比如String对应Varchar)
以上四种分别都是对数据库类型和Java类型进行相应的转换操作。
缓存:
一级缓存:是SqlSession级别的缓存,已查询为例,当一个会话多次执行sql操作时(一个SqlSession多个Execute),第二次默认会使用第一次创建的缓存对象
二级缓存:是Mapper级别的,也可以理解成SqlSessionFactory级别缓存,一个全局空间中,多个会话共享缓存对象。
第三方缓存(Radis等)
二级缓存的实现:
步骤1:在config.xml中开启二级缓存
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
步骤2:在相应Mapper.xml中加入Cache机制
<cache eviction="LRU" //算法策略
type="org.apache.ibatis.cache.impl.PerpetualCache" //
flushInterval="120000" //缓存时间,以毫秒为单位
size="1024" //缓存大小
readOnly="true"/> //如果为false的话,缓存对象必须是可序列化的
eviction参数提到几个算法策略:
LRU:(Least Recently Used),最近最少使用算法,即如果缓存中容量已经满了,会将缓存中最近做少被使用的缓存记录清除掉,然后添加新的记录;
FIFO:(First in first out),先进先出算法,如果缓存中的容量已经满了,那么会将最先进入缓存中的数据清除掉;
Scheduled:指定时间间隔清空算法,该算法会以指定的某一个时间间隔将Cache缓存中的数据清空.
步骤3:实现序列化
MyBatis在面试中常见的问题:
1.JDBC操作数据库有哪些不足之处,MyBatis是如何解决这些问题的?
- 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,在MyBatis中通过管理SqlSession来对数据库连接等进行维护。
- Sql语句(硬编码)写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。而MyBatis将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
- 向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。而Mybatis自动将java对象映射至sql语句,并且可以通过动态SQL实现更复杂的操作。
- 对结果集解析麻烦(原因:对结果集解析存在硬编码(查询列名)),sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo(简单的Java对象)对象解析比较方便。而Mybatis自动将sql执行结果映射至java对象。
2.MyBatis实现步骤是什么样的?
- 创建SqlSessionFactory
- 通过SqlSessionFactory创建SqlSession
- 通过sqlsession执行数据库操作
- 调用session.commit()提交事务
- 调用session.close()关闭会话
3..MyBatis的Mapper.xml有什么作用?一般要做那些配置?
- Mapper接口方法名和mapper.xml中定义的每个sql的id相同
- Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
- Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
- Mapper.xml文件中的namespace即是mapper接口的类路径。
4.#{}和${}的区别是什么?
- #{}是预编译处理,${}是字符串替换。
- Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
- Mybatis在处理${}时,就是把${}替换成变量的值。
- 使用#{}可以有效的防止SQL注入,提高系统安全性。
5.当实体类中的属性名和表中的字段名不一样,怎么办?
- 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致
- 通过<resultMap>来映射字段名和实体类属性名的一一对应的关系