MyBatis面试题总结
#{}和${}的区别是什么
- ${} 是 Properties ⽂件中的变量占位符,它可以⽤于标签属性值和 sql 内部,属于静态⽂本
替换。拿着字符串直接拼接。 - #{} 是sql占位符,MyBatis会将sql中的#{}替换为?号,然后在执行sql前使用PreparedStatement 的参数设置方法,按序给 sql 的?号占位符设置参数值。拿着数值放在引号号内部后拼接.
优先使用 #{}。因为 ${} 会导致 sql 注入的问题。若不得不使用“${xxx}”这样的参数,要手工地做好过滤工作,来防止sql注入攻击。
Xml 映射⽂件中,除了常⻅的 select|insert|updae|delete 标签之外,还有哪些标签?
- resultMap SQL查询结果字段与实体属性的映射关系
<!-- 与java对象对应的列不是数据库中表的列名,而是查询后结果集的列名 -->
<resultMap id="BaseResultMap" type="com.online.charge.model.Student">
<id property="id" column="id" />
<result column="NAME" property="name" />
<result column="HOBBY" property="hobby" />
<result column="MAJOR" property="major" />
</resultMap>
-
动态sql标签
if
,foreach
<!-- foreach多用于in这样的范围 --> <select id="selectIn" resultMap="BaseResultMap"> select name,hobby from student where id in <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select>
定义常量
<sql>
,引用常量<include>
通常⼀个Xml映射文件,都会写一个Dao接口与之对应,请问这个Dao接口的工作原理是什么?Dao 接口里的方法,参数不同时,方法能重载吗?
不能
Dao 接⼝,就是⼈们常说的 Mapper 接⼝,接⼝的全限名通过XML中namespace标签与MyBatis定义的语句相关联。这个类并没有实现类,我们调用的时候,是如何执行SQL的呢? 显然MyBatis为我们实现了,因为接口定义明确,通过JDK的动态代理就可以实现,那动态代理实际做了什么工作?
首先一个namespace下的每⼀个 <select> 、 <insert> 、 <update> 、 <delete> 标签,都会被解析为⼀个 MappedStatement 对象,里面有具体的执行sql。然后当调用接口方法时,接口全限名+方法名确定一个MappedStatement,代理对象拦截接口方法执行MappedStatement中的sql,然后返回结果。
Mybatis 是如何进行分页的?分页插件的原理是什么?
Mybatis 使⽤ RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页,可以在 sql 内直接书写带有物理分页的参数limit(startIndex, len)
来完成物理分页功能,也可以使用分页插件来完成物理分页。
插件原理:使用Mybatis 提供的插件接口,实现自定义插件,在插件的拦截⽅法内拦截待执行的 sql,然后重写 sql,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。
简述 Mybatis 的插件运行原理,以及如何编写一个插件
- 实现Interceptor 2. 配置文件声明
@Intercepts({
@Signature(method = "query", type = Executor.class, args = { MappedStatement.class, Object.class, RowBounds.class,ResultHandler.class }) })
public class CustomInterceptor implements Interceptor {
private static final Logger logger = LoggerFactory.getLogger(CustomInterceptor.class);
@Override
public Object intercept(final Invocation invocation) throws Throwable {
// MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
// 可以轻松获取原始语句信息,并作处理
System.out.println("拦截到了mybatis的BaseExecutor中的方法");
return invocation.proceed();
}
@Override
public Object plugin(final Object target) {
if (target instanceof BaseExecutor || target instanceof CachingExecutor) {
return Plugin.wrap(target, this);
}
return target;
}
@Override
public void setProperties(final Properties properties) {
}
}
<plugins>
<plugin interceptor="CustomInterceptor"></plugin>
</plugins>
Mybatis 仅可以编写针对ParameterHandler 、 ResultSetHandler 、 StatementHandler 、 Executor 这 4 种接口的插件,Mybatis 使⽤ JDK 的动态代理,为需要拦截的接⼝⽣成代理对象以实现接⼝⽅法拦截功能,每当执行这 4 种接口对象的方法时,就会进⼊拦截方法,具体就是 InvocationHandler 的invoke() ⽅法,当然,只会拦截那些你指定需要拦截的方法。
Mybatis 能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别
<resultMap id="detailedBlogResultMap" type="Blog">
<constructor>
<idArg column="blog_id" javaType="int"/>
</constructor>
<result property="title" column="blog_title"/>
<!--一对一,一篇博客一个作者-->
<association property="author" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
</association>
<!--一对多,一篇博客发表多次-->
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<association property="author" javaType="Author"/>
<!--多对多-->
<collection property="comments" ofType="Comment">
<id property="id" column="comment_id"/>
</collection>
<collection property="tags" ofType="Tag" >
<id property="id" column="tag_id"/>
</collection>
<!--鉴别器-->
<discriminator javaType="int" column="draft">
<case value="1" resultType="DraftPost"/>
</discriminator>
</collection>
</resultMap>
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 是半自动ORM 映射⼯具?它与全自动的区别在哪里?
Hibernate 属于全自动ORM 映射工具,使用Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而 Mybatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以,称之为半自动ORM 映射工具。
Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复
如果配置了namespace那么当然是可以重复的,因为我们的Statement实际上就是namespace+id,如果没有配置namespace的话,那么相同的id就会导致覆盖了。