精选Mybatis面试题
MyBatis 执行步骤
- 创建SqlSessionFactory对象。
- 通过SqlSessionFactory获取SqlSession对象。
- 通过Session对象获取Mapper代理对象。
- 通过Mapper代理对象,执行数据库操作。
- 执行成功,则使用SqlSession提交事务。
- 执行失败,则使用SqlSession回滚事务。
- 关闭会话。
#{}和${}的区别是什么?
是 P r o p e r t i e s 文 件 的 变 量 占 位 符 , 它 可 以 用 于 X M L 标 签 属 性 值 和 S Q L 内 部 , 属 于 字 符 串 替 换 , {}是Properties文件的变量占位符,它可以用于XML标签属性值和SQL内部,属于字符串替换, 是Properties文件的变量占位符,它可以用于XML标签属性值和SQL内部,属于字符串替换,{} 也可以对传递进来的参数原样拼接在 SQL 中(实际场景下,不推荐这么做,因为可能有SQL注入的风险)。
.#{}是SQL的参数占位符,Mybatis会将SQL中的#{}替换为?号,在SQL在 SQL 执行前会使用 PreparedStatement 的参数设置方法,按序给SQL的?号占位符设置参数值。所以#{}是预编译处理,可以有效防止SQL注入,提高系统安全性。
另外,#{} 和 ${} 的取值方式非常方便。例如:#{item.name} 的取值方式,为使用反射从参数对象中,获取 item 对象的 name 属性值,相当于 param.getItem().getName() 。
Mapper的接口绑定有几种实现方式,分别是怎么实现的?
- 通过XML Mapper里面写SQL来绑定的,这种情况下,XML映射文件中的namespace必须为Mapper接口的全路径名。
- 通过注解绑定,在Mapper接口的方法上加上Select、@Update、@Insert、@Delete 注解,里面包含 SQL 语句来绑定。
- 也是通过注解绑定在接口的方法上面加上@SelectProvider、@UpdateProvider、@InsertProvider、@DeleteProvider 注解,通过 Java 代码,生成对应的动态 SQL 。
实际开发中比较推荐第一种方式,因为,SQL 通过注解写在Java代码中,会非常杂乱。而写在XML中,更加有整体性,并且可以更加方便的使用OGNL表达式。
在 Mapper 中如何传递多个参数?
- 通过Object传递参数
- 通过Map集合传递
- 使用@Param注解传递
- 不使用@Param注解,逐个使用#{param1}、#{param2}、#{param3}不断往下获取参数。
Mybatis 都有哪些 Executor 执行器?它们之间的区别是什么?
- SimpleExecutor:每执行一次update或select操作,就创建一个Statement对象,用完立即关闭Statement对象。
- ReuseExcutor:执行update或select操作,以SQL作为key查找缓存的Statement对象,存在就使用,不存在就创建,用完后不关闭Statement对象,而是放置于缓存 Map<String, Statement> 内,供下一次使用。简言之,就是重复使用 Statement 对象。
- BatchExecutor:执行update操作(没有select操作,因为JDBC批处理不支持select操作),将所有的SQL都添加到批处理,等待统一执行,它缓存了多个Statement对象,每个Statement对象都是调用addBatch方法执行后,等待一次执行executeBatch批处理。
- CachingExecutor:在上述三个执行器上,添加二级缓存的功能。
通过设置 <setting name="defaultExecutorType" value="" />
的 “value” 属性,可传入 SIMPLE、REUSE、BATCH 三个值,分别使用 SimpleExecutor、ReuseExecutor、BatchExecutor 执行器。
通过设置 <setting name="cacheEnabled" value="" />
的 “value” 属性为 true 时,创建 CachingExecutor 执行器。
Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么?
Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载。其中,association 指的就是一对一,collection 指的就是一对多查询。
在 Mybatis 配置文件中,可以配置 <setting name="lazyLoadingEnabled" value="true" />
来启用延迟加载的功能。默认情况下,延迟加载的功能是关闭的。
MyBatis 与 Hibernate 有哪些不同?
Mybatis与Hibernate不同,它不完全是一个ORM框架,因为Mybatis需要开发者自己编写SQL,不过Mybatis可以通过XML或注解方式灵活配置需要运行的SQL语句,并将Java对象和SQL语句映射生成最终执行的SQL,最后在将SQL的执行结果映射生成Java对象。
Mybatis学习门槛低,简单易学,开发者直接编写原生态的SQL,可严格控制SQL执行性能,灵活度高。但是灵活的前提是Mybatis无法做到与数据库的无关性,如果需要支持多种数据库的软件则需要定义多套SQL映射文件,工作量大。
Hibernate对象/映射能力强,数据库无关性好。如果用Hibernate开发可以节省很多代码,提高效率,但是Hibernate的缺点就是学习门槛高,要精通门槛更高,而且怎么设计O/R隐射,在性能和对象模型之间如何权衡,以及如何用好Hibernate是需要具有很强的经验和能力才行。
- Hibernate属于全自动ORM映射框架,使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取。
- Mybatis 属于半自动 ORM 映射工具,在查询关联对象或关联集合对象时,需要手动编写 SQL 来完成。
Mybatis一级缓存和二级缓存
MyBatis缓存是查询缓存,只针对查询语句,MyBatis默认开启了一级缓存。
一级缓存:一级缓存的范围是SqlSession级别的,也就是说查询用同一个SqlSession查询相同的数据会先走缓存,SqlSession存到内存中,并且只要执行commit就回收了,所以一级缓存是SqlSession级别的,因为一级缓存中的值存到SqlSession对象中。
二级缓存:二级缓存的范围是Mapper级别的,也就是说查询同一个namespace,只要缓存中有数据就会先走缓存,二级缓存是属于序列化的,也就说Mybatis实际上就是把数据放到硬盘文件中去了,因此使用二级缓存POJO必须实现序列化接口Serializable。
Mybatis的工作原理
创建SqlSessionFactoryBuilder对象,调用build()方法读取配置文件(包括Mapper.xml配置文件中的SQL每条SQL语句,映射到一个MapperStatement中,并以键值对的方式存到Configuration对象中)并返回SqlSessionFactory对象,由SqlSessionFactory对象创建SqlSession对象,通过动态代理的方式(反射)让Mapper接口以及POJO实体和XML配置文件进行映射。SqlSession在执行SQL语句的时候使用statementId调取Configuration对象中的MapperStatement对象以及参数,传给Executor执行器,委托Excutor去执行SQL语句。Excutor执行器会根据参数解析动态SQL,解析完SQL后Excutor会创建一个Statement(JDBC的核心对象)对象来执行JDBC的操作,并生成一个BoundSql对象提供给StatementHandler使用,Statement执行后返回ResultSet并转换成集合返回。(有兴趣可以分析下源码)