mybatis面试题总结(version-0.0)
为什么要是用Mybatis框架?
当我们使用传统的JDBC去操作数据库的时候,有很大的问题
- jdbc底层是没有使用连接池的, 我们操作数据库的时候需要频繁的创建和关毕连接,很消耗资源(因为这涉及到网络IO , 和磁盘IO)。
- JDBC的代码写在java文件中, 一旦我们要修改sql的话, java需要整体的重新编译,不利于系统维护
- 使用 PreparedStatement预编译的话对变量进行设置 1,2,3数字 这样的序号不利于维护
- 返回 result 结果集也需要硬编码
mybatis的优缺点
优点
-
基于Sql语句编程,十分灵活,不会对应用程序或者数据库现有的设计造成任何的影响, sql写在xml中,跟用用程序的代码解耦,便于统一管理,提供xml标签,支持动态SQL
-
跟JDBC相比,减少了至少一半 的代码量,消除了大量冗余的代码, 不需要手动的开关连接
-
很好的与各种数据库兼容(因为mybatis 封装了JDBC,所以只要是支持JDBC 数据库, myvbatis 都支持)
-
能够很好的与spring集成(只需要把在pom文件中引入相关的依赖即可)
-
提供了映射标签,支持对象与数据库的ORM字段关系映射
- 例如:
<resultMap id="listMap" type="Student"> <id column="id" property="id" jdbcType="INTEGER"></id> <result column="studentName" property="name"></result> <result column="gender" property="gender"></result> <result column="age" property="age"></result> <result column="info" property="info"></result> </resultMap>
缺点
- SQL编写工作量大,当字段多,关联表多的时候, 对于开发人员编写SQL 的功力有一定的要求
- SQL语句依赖数据库,导致数据库移植性差,不能随意更换数据库。
从mybatis 到 mysql
mybatis核心流程图
SqlSessionFactoryBuilder
:用于创建session工厂,且创建完session工厂后即可销毁:- 创建session工厂时需读取mybatis的主配文件。
- 主配文件用于配置mybatis的环境信息和Mapper配置文件路径等。
- Mapper配置文件用于配置CRUD语句块和SQL语句相关配置信息。
SqlSessionFactory
:session工厂用于获取一个session会话,可以重复使用,但不该被重复创建,建议以单例模式来管理。SqlSession
:session会话用于获取数据库连接的接口,每个线程都该独享一个session会话,建议设置为局部变量。Executor
:session会话中的SQL执行者,有基本和缓存两种执行器实现。MappedStatement
:SQL执行者中,专门负责SQL组装和结果转换的的SQL管理者:- 每个SQL块都通过id属性对应一个SQL管理者者。
- 输入映射:在执行SQL前,将输入的java实体类中的属性映射到SQL代码中。
- 输出映射:在执行SQL后,将返回的SQL结果映射到java实体类中保存。
3、#{}和${}的区别是什么?
- #{
}是预编译处理,${
} 是字符串替换。
- Mybatis在处理 #{
} 时,会将sql中的 #{
} 替换为 ? 号,调用PreparedStatement的set方法来赋值;
- Mybatis在处理 ${
} 时,就是把${
}替换成变量的值。
所以使用#{
}可以有效的防止SQL注入,提高系统安全性。
4、Mybatis是如何进行分页的?分页插件的原理是什么?
-
Mybatis使用
RowBounds
对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页。可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。 -
分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。
-
插件接口:
- parameterHandler: 对sql参数进行处理
- ResultHandler: 对结果集进行处理
- StatementHandler :对sql语句进行处理
- Executor:执行器,执行增删改查
这个插件其实就是一个拦截器,mybatis通过动态代理为需要拦截的接口生成代理对象 --》实现接口方法拦截功能,每当执行这四种接口对象的方法时:
判断执行的方法是否是需要代理的方法,如果是,则执行插件类的增强方法(重写),进行方法的拦截处理。
这是典型的AOP的思想。
如何编写一个插件?
- 实现 Mybatis 的
**Interceptor 接口**
并复写**intercept()**
方法, - 然后再给插件编写注解,指定要拦截哪一个接口的哪些方法即可
- 最后在配置文件中配置你编写的插件。
5 Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复?
- 不同的Xml映射文件,如果配置了
namespace
,那么 id 可以重复;如果没有配置namespace
,那么id不能重复; - 毕竟namespace不是必须的,只是最佳实践而已。
- 原因就是
namespace+id
是作为Map<String, MappedStatement> 的key
使用的,如果没有namespace,就剩下id,那么,id重复会导致数据互相覆盖。有了namespace,自然id就可以重复,namespace不同,namespace+id自然也就不同。
6. Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
-
Mybatis仅支持
association(一对一查询)
关联对象和collection(一对多查询)
关联集合对象的延迟加载,在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。
-
当我们调用方法的时候并不会立刻发送SQL进行查询,只有当用到数据的时候,才会真正的进行查询
-
这样做的好处就是:
节省我们的内存空间,因为我们立刻查询出来之后,有可能并不会立刻用到数据,但是这些数据却还占着宝贵的内存空间,所以当我们用到的时候,再进行查询,就可以节省内存空间 -
它的原理是:
-
使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法
- 比如调用
a.getB().getName()
,拦截器 invoke()方法发现a.getB()
是null值, - 那么就会单独发送事先保存好的查询关联 B 对象的 sql,把B查询上来,
- 然后调用
a.setB(b)
,于是a的对象 b 属性就有值了, - 接着完成
a.getB().getName()
方法的调用。这就是延迟加载的基本原理。
- 比如调用
-
当然了,不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的
-
-