1、#{}和${}的区别是什么?
${}
是 Properties 文件中的变量占位符,它可以用于标签属性值和 sql 内部,属于静态文本替换,比如${driver}会被静态替换为com.mysql.jdbc.Driver
。#{}
是 sql 的参数占位符,MyBatis 会将 sql 中的#{}
替换为?号,在 sql 执行前会使用 PreparedStatement 的参数设置方法,按序给 sql 的?号占位符设置参数值,比如 ps.setInt(0, parameterValue),#{item.name}
的取值方式为使用反射从参数对象中获取 item 对象的 name 属性值,相当于param.getItem().getName()
2、Xml 映射文件中,除了常见的 select|insert|updae|delete 标签之外,还有哪些标签?
注:这道题是京东面试官面试我时问的。
答:还有很多其他的标签,<resultMap>
、<parameterMap>
、<sql>
、<include>
、<selectKey>
,加上动态 sql 的 9 个标签,trim|where|set|foreach|if|choose|when|otherwise|bind
等,其中为 sql 片段标签,通过<include>
标签引入 sql 片段,<selectKey>
为不支持自增的主键生成策略标签。
3、最佳实践中,通常一个 Xml 映射文件,都会写一个 Dao 接口与之对应,请问,这个 Dao 接口的工作原理是什么?Dao 接口里的方法,参数不同时,方法能重载吗?
注:这道题也是京东面试官面试我时问的。
答:Dao 接口,就是人们常说的 Mapper
接口,接口的全限名,就是映射文件中的 namespace 的值,接口的方法名,就是映射文件中MappedStatement
的 id 值,接口方法内的参数,就是传递给 sql 的参数。Mapper
接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为 key 值,可唯一定位一个MappedStatement
,举例:com.mybatis3.mappers.StudentDao.findStudentById
,可以唯一找到 namespace 为com.mybatis3.mappers.StudentDao
下面id = findStudentById
的MappedStatement
。在 MyBatis 中,每一个<select>
、<insert>
、<update>
、<delete>
标签,都会被解析为一个MappedStatement
对象。
Dao 接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。
Dao 接口的工作原理是 JDK 动态代理,MyBatis 运行时会使用 JDK 动态代理为 Dao 接口生成代理 proxy 对象,代理对象 proxy 会拦截接口方法,转而执行MappedStatement
所代表的 sql,然后将 sql 执行结果返回。
5、简述 MyBatis 的插件运行原理,以及如何编写一个插件。
注:我出的。
答:MyBatis 仅可以编写针对 ParameterHandler
、ResultSetHandler
、StatementHandler
、Executor
这 4 种接口的插件,MyBatis 使用 JDK 的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这 4 种接口对象的方法时,就会进入拦截方法,具体就是 InvocationHandler
的 invoke()
方法,当然,只会拦截那些你指定需要拦截的方法。
实现 MyBatis 的 Interceptor 接口并复写 intercept()
方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。
6、MyBatis 执行批量插入,能返回数据库主键列表吗?
注:我出的。
答:能,JDBC 都能,MyBatis 当然也能。
7、MyBatis 动态 sql 是做什么的?都有哪些动态 sql?能简述一下动态 sql 的执行原理不?
注:我出的。
答:MyBatis 动态 sql 可以让我们在 Xml 映射文件内,以标签的形式编写动态 sql,完成逻辑判断和动态拼接 sql 的功能,MyBatis 提供了 9 种动态 sql 标签 trim|where|set|foreach|if|choose|when|otherwise|bind
。
其执行原理为,使用 OGNL 从 sql 参数对象中计算表达式的值,根据表达式的值动态拼接 sql,以此来完成动态 sql 的功能
8、MyBatis 是如何将 sql 执行结果封装为目标对象并返回的?都有哪些映射形式?
注:我出的。
答:第一种是使用<resultMap>
标签,逐一定义列名和对象属性名之间的映射关系。第二种是使用 sql 列的别名功能,将列别名书写为对象属性名,比如 T_NAME AS NAME,对象属性名一般是 name,小写,但是列名不区分大小写,MyBatis 会忽略列名大小写,智能找到与之对应对象属性名,你甚至可以写成 T_NAME AS NaMe,MyBatis 一样可以正常工作。
有了列名与属性名的映射关系后,MyBatis 通过反射创建对象,同时使用反射
11、MyBatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否可以重复?
注:我出的。
答:不同的 Xml 映射文件,如果配置了 namespace,那么 id 可以重复;如果没有配置 namespace,那么 id 不能重复;毕竟 namespace 不是必须的,只是最佳实践而已。
原因就是 namespace+id 是作为 Map<String, MappedStatement>
的 key 使用的,如果没有 namespace,就剩下 id,那么,id 重复会导致数据互相覆盖。有了 namespace,自然 id 就可以重复,namespace 不同,namespace+id 自然也就不同。
12、MyBatis 中如何执行批处理?
注:我出的。
答:使用 BatchExecutor 完成批处理。