一文搞定 MyBatis,经典问题集锦

1、啥是 MyBatis?

MyBatis 是一个半自动 ORM 持久层框架,她内部对 JDBC 做了封装,并使用 XML 或注解来配置和映射数据库信息,开发人员只需要关注 SQL 语句本身,避免了处理繁琐的数据库映射过程,使 Java 代码和 SQL 完全分离,有利于分层开发,提高开发效率和灵活性。

2、MyBatis 的优缺点和适用场景?

1)优点

  • 基于SQL语句编程,非常灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除SQL与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。
  • 与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接;
  • 很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)。
  • 能够与Spring很好的集成;
  • 提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。

2)缺点

  • SQL语句的编写工作量较大,尤其当字段多、关联表多时。
  • 不支持方言,数据库移植性差。
  • SQL存在XML文件中,调试不方便。
  • 对动态SQL的支持不是很好,提供的标签过于简单。
  • 由于xml里标签id必须唯一,导致DAO中方法不支持方法重载。

3)适用场景

MyBatis 解耦了 SQL 和 程序代码,适合业务逻辑相对复杂或者变动频繁的场景,可以使开发人员专注于 SQL 逻辑的修改。目前,在互联网、金融等项目中,MyBatis 是不错的选择。

3、MyBatis 与 Hibernate 的区别?

  • Hibernate 是全自动 ORM框架,而 MyBatis是一个 半自动 ORM 框架,MyBatis 需要程序员自己编写 SQL 语句。
  • MyBatis 直接编写原生态 SQL,可以严格控制 SQL执行性能,灵活度高,适合需求变化频繁的项目,但是对数据库的依赖较大,移植性差。Hibernate 的灵活度相对较低,但是做到了数据库的无关性,移植性较好。

总结:MyBatis 半自动,SQL分离,灵活度高,移植性差。Hibernate 全自动,SQL不分离,灵活度差,移植性好。

4、#{} 和 ${} 的区别,防注入?(重点)

#{} 是预编译处理,${}是字符串替换。

  • MyBatis 在处理#{}时,会将 sql 中的#{}替换为?号,sql语句解析会加上"",调用 PreparedStatement 的 set 方法来赋值,使用#{}可以有效的防止 SQL 注入,提高系统安全性。
  • MyBatis 在处理 ${} 时,就是把 ${} 替换成变量的值。${} 方式一般用于传入数据库对象,例如传入表名或配置信息。但是一般能用#的就别用$。

5、XML 映射文件中的常见标签?

除了最常见的 select|insert|updae|delete 外,还有,加上动态sql的9个标签,trim|where|set|foreach|if|choose|when|otherwise|bind等,其中为sql片段标签,通过标签引入sql片段,为不支持自增的主键生成策略标签。

6、MyBatis 的核心组件?

  • SqlSessionFactoryBuilder(构造器),根据代码或配置生成SqlSessionFactory,采用分布构建的 Builder 模式。
  • SqlSessionFactory(工厂接口),生成SqlSession,采用的是工厂模式。
  • SqlSession(会话),既可以发送SQL去执行并返回结果,也可以获取Mapper的接口。
  • SQL Mapper(映射器),由一个Java接口和一个XML文件(或注解)构成,需要给出对应的SQL和映射规则,负责发送SQL并返回结果。

7、MyBatis 如何分页?

MyBatis 本身提供了 RowBounds 对象进行分页,她是针对ResultSet结果集执行的内存分页,而非物理分页。可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。

  • RowBounds 对象进行分页,针对ResultSet结果集执行的内存分页,非物理分页。
  • 插件分页,实现Mybatis提供的插件接口,如 PageHelper,物理分页,在插件的拦截方法内拦截待执行的sql,然后重写sql,添加对应的物理分页语句和物理分页参数。

8、MyBatis 都有哪些 Executor 执行器?

MyBatis有四种常见的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor  CachingExecutor

  • SimpleExecutor:单次执行。每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
  • ReuseExecutor:重复使用。执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。
  • BatchExecutor:批量操作。执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。
  • CachingExecutor:启用二级缓存。启用于二级缓存时的执行器,采用静态代理,代理一个 Executor 对象。执行 update 方法前判断是否清空二级缓存,执行 query 方法前先在二级缓存中查询,命中失败再通过被代理类查询。

作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。

9、MyBatis 的一级缓存和二级缓存?

  • 一级缓存:SqlSession 级别的缓存。基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。
  • 二级缓存:Mapper 级别的缓存。与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置

对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。

10、MyBatis 的插件运行原理,如何编写一个插件?

Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。

编写插件:实现Mybatis的Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。

11、最佳实践中,通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?

Dao接口,就是人们常说的Mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,就是映射文件中MappedStatement的id值,接口方法内的参数,就是传递给sql的参数。Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MappedStatement。在Mybatis中,每一个