Mybatis是一个优秀的半ORM(对象关系映射)框架,在java的企业开发中常见的选择还有spring data jpa、hibernate,还有mybatis plus ,mybatis plus 对于设计思想和使用和jpa 类似,今天的摘录核心主要是关于Mybatis的核心原理和基础使用技巧。
一、基础定义
1.什么是Mybatis?
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。
1、Mybatis是一个半ORM(对象关系映射)框架,底层封装了JDBC,是程序员在开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。使得程序员可以花更多的精力放到业务开发中。另外,程序员直接编写原生态sql,严格控制sql执行性能,灵活度高。
2、MyBatis 可以使用简单的 XML文件 或注解方式来配置和映射原生信息,将 POJO映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
3、通过xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过java对象和 statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。(从执行sql到返回result的过程)。
Mybaits的优点:
1、基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。
2、与比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接;JDBC相
3、很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)。
4、能够与Spring很好的集成;
5、提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。
3 .MyBatis框架的缺点:
1、SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。
2、SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
4. MyBatis框架适用场合:
1、MyBatis专注于SQL本身,是一个足够灵活的DAO层解决方案。
2、对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis将是不错的选择。
MyBatis的框架架构设计是怎么样的
这张图从上往下看。MyBatis的初始化,会从mybatis-config.xml配置文件,解析构造成Configuration这个类,就是图中的红框。
(1)加载配置:配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
(2)SQL解析:当API接口层接收到调用请求时,会接收到传入SQL的ID和传入对象(可以是Map、JavaBean或者基本数据类型),Mybatis会根据SQL的ID找到对应的MappedStatement,然后根据传入参数对象对MappedStatement进行解析,解析后可以得到最终要执行的SQL语句和参数。
(3)SQL执行:将最终得到的SQL和参数拿到数据库进行执行,得到操作数据库的结果。
(4)结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean或者基本数据类型,并将最终结果返回。
二、架构
我们把Mybatis的功能架构分为三层:
(1)API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
(2)数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
(3)基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
image
image.png
Mybatis的处理详细流程
1、SqlSessionFactoryBuilder【全局的对象】
每一个MyBatis的应用程序的入口是:SqlSessionFactoryBuilder。
它的作用是通过XML配置文件创建Configuration对象(当然也可以在程序中自行创建),然后通过build方法创建SqlSessionFactory对象。
2、SqlSessionFactory【全局的对象】
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。
SqlSessionFactory是由SqlSessionFactoryBuilder 从 XML 配置文件或通过Java的方式构建出 的实例,主要功能是创建SqlSession(会话)对象;SqlSessionFactory对象一个必要的属性是Configuration对象;
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,建议使用单例模式或者静态单例模式。一个SqlSessionFactory对应配置文件中的一个环境(environment),如果你要使用多个数据库就配置多个环境分别对应一个SqlSessionFactory。
3、 SqlSession
作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能。SqlSession通过调用api的Statement ID找到对应的MappedStatement对象。
SqlSession是一个接口,它有2个实现类,分别是DefaultSqlSession(默认使用)以及SqlSessionManager;默认使用DefaultSqlSession,它有两个必须配置的属性:Configuration和Executor,SqlSession通过内部存放的执行器(Executor)来对数据进行CRUD。
由于不是线程安全的,所以SqlSession对象的作用域需限制方法内;每一次操作完数据库后都要调用close对其进行关闭,官方建议通过try-finally来保证总是关闭SqlSession。
4、 Executor
MyBatis执行器,是MyBatis 调度的核心。Executor对象在创建Configuration对象的时候创建,并且缓存在Configuration对象里。Executor:负责SQL语句的生成,调用StatementHandler访问数据库,查询缓存的维护;
Executor(负责动态SQL的生成和查询缓存的维护)将MappedStatement对象进行解析,sql参数转化、动态sql拼接,生成jdbc Statement对象;
**Executor(执行器)接口有两个实现类,其中BaseExecutor有三个继承类分别是BatchExecutor(重用语句并执行批量更新),ReuseExecutor(重用预处理语句prepared statements),SimpleExecutor(普通的执行器)。
5、 StatementHandler
封装了JDBC Statement操作,负责对JDBCstatement的操作,如设置参数、将Statement结果集转换成List集合,是真正访问数据库的地方,并调用ResultSetHandler处理查询结果。
6、ResultSetHandler
-
ParameterHandler 负责将用户传递的参数转换成JDBC Statement 所需要的参数
-
ResultSetHandler
负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;
处理查询结果。 -
TypeHandler
负责java数据类型和jdbc数据类型之间的映射和转换
7、 MappedStatement :
MappedStatement就是用来存放我们SQL映射文件中的信息包括sql语句,输入参数,输出参数等等。一个SQL节点对应一个MappedStatement对象。
借助MappedStatement中的结果映射关系,将返回结果转化成HashMap、JavaBean等存储结构并返回。
8、 SqlSource
负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
-
BoundSql
表示动态生成的SQL语句以及相应的参数信息 -
Configuration
MyBatis所有的配置信息都维持在Configuration对象之中
最后,分享常见的 一些mybatis面试题:
1.JDBC有几个步骤?
JDBC大致可以分为六个步骤:
-
加载驱动程序
-
获得数据库连接
-
创建一个Statement对象
-
操作数据库,实现增删改查
-
获取结果集
-
关闭资源
2.什么是Mybatis?
如果在面试的时候被问到,只要你说出下面三种即可:
❝MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
❞
——来自官网。推荐大家按照官方的来回答。
3.什么是ORM?
全称为Object Relational Mapping。对象-映射-关系型数据库。对象关系映射(简称ORM,或O/RM,或O/R mapping),用于实现面向对象编程语言里不同类型系统的数据之间的转换。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象与关系数据库相互映射。
ORM提供了实现持久化层的另一种模式,它采用映射元数据来描述对象关系的映射,使得ORM中间件能在任何一个应用的业务逻辑层和数据库层之间充当桥梁。
4.说说ORM的优缺点
「优点」1.提高了开发效率。由于ORM可以自动对Entity对象与数据库中的Table进行字段与属性的映射,所以我们实际可能已经不需要一个专用的、庞大的数据访问层。2.ORM提供了对数据库的映射,不用sql直接编码,能够像操作对象一样从数据库获取数据。
「缺点」牺牲程序的执行效率和会固定思维模式,降低了开发的灵活性。
从系统结构上来看,采用ORM的系统一般都是多层系统,系统的层次多了,效率就会降低。ORM是一种完全的面向对象的做法,而面向对象的做法也会对性能产生一定的影响。
在我们开发系统时,一般都有性能问题。性能问题主要产生在算法不正确和与数据库不正确的使用上。
ORM所生成的代码一般不太可能写出很高效的算法,在数据库应用上更有可能会被误用,主要体现在对持久对象的提取和和数据的加工处理上,如果用上了ORM,程序员很有可能将全部的数据提取到内存对象中,然后再进行过滤和加工处理,这样就容易产生性能问题。
在对对象做持久化时,ORM一般会持久化所有的属性,有时,这是不希望的。但ORM是一种工具,工具确实能解决一些重复,简单的劳动。这是不可否认的。但我们不能指望工具能一劳永逸的解决所有问题,有些问题还是需要特殊处理的,但需要特殊处理的部分对绝大多数的系统,应该是很少的。
5.说说Mybaits的优缺点
优点
① 基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。
② 与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接;
③ 很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)。
④ 能够与Spring很好的集成;
⑤ 提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。
缺点
① SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。
② SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
MyBatis框架适用场合:
(1)MyBatis专注于SQL本身,是一个足够灵活的DAO层解决方案。
(2)对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis将是不错的选择。
6.Mybatis是如何进行分页的?
Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页,先把数据都查出来,然后再做分页。
可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
7.分页插件的基本原理是什么?
分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql(SQL拼接limit),根据dialect方言,添加对应的物理分页语句和物理分页参数,用到了技术JDK动态代理,用到了责任链设计模式。
8.简述Mybatis的插件运行原理?
Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
9.如何编写一个插件?
编写插件:实现Mybatis的Interceptor接口并复写intercept()方法,然后再给插件编写注解,指定要拦截哪一个接口的哪些方法即可,最后在配置文件中配置你编写的插件。推荐:建议收藏,mybatis插件原理详解
10.Mybatis动态sql有什么用?
Mybatis动态sql可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值完成逻辑判断 并动态调整sql的功能。
Mybatis提供了9种动态sql标签:trim | where | set | foreach | if | choose | when | otherwise | bind
。
11.Xml映射文件中有哪些标签?
除了常见的select|insert|updae|delete标签之外,还有:
<resultMap>
、<parameterMap>
、<sql>
、<include>
、<selectKey>
,加上动态sql的9个标签,其中<sql>
为sql片段标签,通过<include>
标签引入sql片段,<selectKey>
为不支持自增的主键生成策略标签。
12.Mybatis是否支持延迟加载?
Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false
。
它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。
当然了,不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的。
13.延迟加载的基本原理是什么?
延迟加载的基本原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法。
比如调用a.getB().getName()
,拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b)
,于是a的对象b属性就有值了,接着完成a.getB().getName()
方法的调用。
当然了,不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的。
14.mapper.xml文件对应的Dao接口原理是?
简单说:使用了JDK动态代理和反射,把接口和xml绑定在一起而搞定的。
15.Dao接口里的方法,参数不同时能重载吗?
不能重载。
16.#{}和${}的区别是什么?
${}是字符串替换,#{}是预处理;
Mybatis在处理时,就是把{}直接替换成变量的值。而Mybatis在处理#{}时,会对sql语句进行预处理,将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
使用#{}可以有效的防止SQL注入,提高系统安全性。
17.Mybatis执行批量插入,能返回数据库主键列表吗?
1、对于支持生成自增主键的数据库:增加 useGenerateKeys和keyProperty ,<insert>
标签属性。
2、不支持生成自增主键的数据库:使用<selectKey>
。
方式:
<insert id="insertAuthor" useGeneratedKeys="true"
keyProperty="id">
insert into Author (username, password, email, bio) values
<foreach item="item" collection="list" separator=",">
(#{item.username}, #{item.password}, #{item.email}, #{item.bio})
</foreach>
</insert>
注意Mybatis的版本,官方在这个3.3.1版本中加入了批量新增返回主键id的功能 。
18.不同的Xml映射文件,id是否可以重复?
不同的Xml映射文件,如果配置了namespace,那么id可以重复;如果没有配置namespace,那么id不能重复;毕竟namespace不是必须的,只是最佳实践而已。
原因就是namespace+id是作为Map<String, MappedStatement>的key使用的,如果没有namespace,就剩下id,那么,id重复会导致数据互相覆盖。有了namespace,自然id就可以重复,namespace不同,namespace+id自然也就不同。