美文网首页
MyBatis面试题

MyBatis面试题

作者: Hitooo | 来源:发表于2020-06-21 18:03 被阅读0次

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 的插件运行原理,以及如何编写一个插件
  1. 实现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就会导致覆盖了。

相关文章

网友评论

      本文标题:MyBatis面试题

      本文链接:https://www.haomeiwen.com/subject/pteyxktx.html