您当前的位置:首页 > 计算机 > 编程开发 > MyBatis

Mybatis知识点(面试题)整理

时间:01-31来源:作者:点击数:

如题,MyBatis这里知识点我打算从经典面试题入手,然后整理这些面试题的答案。下面是我从网上搜集的一些面试题

什么是MyBatis

MyBatis是一款优秀的支持自定义SQL查询,存储过程和高级映射的持久层框架,写出了几乎所有的JDBC代码和参数的手动设置以及结果集的检索。MyBatis可以使用XML或注解进行配置或映射,MyBatis通过将参数映射到配置的SQL形成最终执行的SQL语句,最后将执行的SQL的结果映射成Java对象返回。(摘自《MyBatis从入门到精通》)

MyBatis的#{}和{}的区别

#{}是需要预编译的,${}是之间将数值拼接到sql里面。#{}可以防止sql注入,提高系统安全性

MyBatis的pageHelper插件使用

pageHelper是一款好用的分页插件,可以自动对select结果进行分页查询,非复杂情况下不需要开发人员操心如何分页。这里主要介绍一下pageHelper在Spring Boot项目中常见用法

(1)引入maven依赖

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>最新版本</version>
</dependency>

(2)pageHelper相关配置

#pagehelper
pagehelper.helperDialect=mysql
pagehelper.reasonable=true
pagehelper.supportMethodsArguments=true
pagehelper.params=count=countSql

(3)如何使用

使用的话,分好几种,这是从源代码的说明中摘抄的。但是我在项目中主要使用第二种方式

//第一种,RowBounds方式的调用
List<User> list = sqlSession.selectList("x.y.selectIf", null, new RowBounds(0, 10));

//第二种,Mapper接口方式的调用,推荐这种使用方式。
PageHelper.startPage(1, 10);
List<User> list = userMapper.selectIf(1);

//第三种,Mapper接口方式的调用,推荐这种使用方式。
PageHelper.offsetPage(1, 10);
List<User> list = userMapper.selectIf(1);

//第四种,参数方法调用
//存在以下 Mapper 接口方法,你不需要在 xml 处理后两个参数
public interface CountryMapper {
    List<User> selectByPageNumSize(
            @Param("user") User user,
            @Param("pageNum") int pageNum, 
            @Param("pageSize") int pageSize);
}
//配置supportMethodsArguments=true
//在代码中直接调用:
List<User> list = userMapper.selectByPageNumSize(user, 1, 10);

//第五种,参数对象
//如果 pageNum 和 pageSize 存在于 User 对象中,只要参数有值,也会被分页
//有如下 User 对象
public class User {
    //其他fields
    //下面两个参数名和 params 配置的名字一致
    private Integer pageNum;
    private Integer pageSize;
}
//存在以下 Mapper 接口方法,你不需要在 xml 处理后两个参数
public interface CountryMapper {
    List<User> selectByPageNumSize(User user);
}
//当 user 中的 pageNum!= null && pageSize!= null 时,会自动分页
List<User> list = userMapper.selectByPageNumSize(user);

//第六种,ISelect 接口方式
//jdk6,7用法,创建接口
Page<User> page = PageHelper.startPage(1, 10).doSelectPage(new ISelect() {
    @Override
    public void doSelect() {
        userMapper.selectGroupBy();
    }
});
//jdk8 lambda用法
Page<User> page = PageHelper.startPage(1, 10).doSelectPage(()-> userMapper.selectGroupBy());

//也可以直接返回PageInfo,注意doSelectPageInfo方法和doSelectPage
pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(new ISelect() {
    @Override
    public void doSelect() {
        userMapper.selectGroupBy();
    }
});
//对应的lambda用法
pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(() -> userMapper.selectGroupBy());

//count查询,返回一个查询语句的count数
long total = PageHelper.count(new ISelect() {
    @Override
    public void doSelect() {
        userMapper.selectLike(user);
    }
});
//lambda
total = PageHelper.count(()->userMapper.selectLike(user));
//分页时,实际返回的结果list类型是Page<E>,如果想取出分页信息,需要强制转换为Page<E>,
assertEquals(182, ((Page) list).getTotal());

//或者使用PageInfo接收
//获取第1页,10条内容,默认查询总数count
PageHelper.startPage(1, 10);
List<User> list = userMapper.selectAll();
//用PageInfo对结果进行包装
PageInfo page = new PageInfo(list);
//测试PageInfo全部属性
//PageInfo包含了非常全面的分页属性
assertEquals(1, page.getPageNum());
assertEquals(10, page.getPageSize());
assertEquals(1, page.getStartRow());
assertEquals(10, page.getEndRow());
assertEquals(183, page.getTotal());
assertEquals(19, page.getPages());
assertEquals(1, page.getFirstPage());
assertEquals(8, page.getLastPage());
assertEquals(true, page.isFirstPage());
assertEquals(false, page.isLastPage());
assertEquals(false, page.isHasPreviousPage());
assertEquals(true, page.isHasNextPage());

注意:pageHelper的分页调用是对下一行代码做分页操作,所以必须紧挨着查询,并且在查询调用的前面。如果下一行代码并非查询操作,在pageHelper中创建的ThreadLocal属性无法回收,会出现内存泄漏的情况。

使用MyBatis的接口调用有哪些要求

(1)Mapper接口类的路径要和mapper.xml的namespace的mapper接口类的路径相同

(2)Mapper接口类命名方法id、参数名要和mapper.xml的方法参数相对应


(3)Mapper接口类返回值要和mapper.xml的返回值对应


MyBatis的xml对应的dao接口工作原理是什么?可以重载吗?

Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所代表的sql,然后将sql执行结果返回。

Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MappedStatement.在Mybatis中,每一个<select>/<insert>/<update>/<delete>标签,都会被解析成一个MappedStatement.

Dao接口里面的方法不能重载。因为虽然重载的参数列表不一致,但是方法名是一样的,这样在mapper.xml中查找会冲突,因此不能重载。

MyBatis是否支持延迟加载

Mybatis支持延迟加载。

使用场景:一对一,多对一:立即加载;一对多,多对多:延迟加载。

Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=tree|false.

它的原理是:使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送实现保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b树形就有值了,接着完成a.getB(),getName()方法的调用。这就是延迟加载的原理。不光是Mybatis,几乎所有的包括hibernate,支持延迟加载的原理。

Mybatis插件运行原理

Mybatis的插件利用JDK动态代理和责任链设计模式的总和运用。

Mybatis是对四大接口进行拦截,Mybatis的四大接口:

Executor(update,query,flushStatements,commit,rollback,getTransaction, close,isClosed)Mybatis的执行器,用于执行增删改查;

StatementHandle(getParameterObject,setParameters)处理SQL的参数对象;

ResultSetHandle(handleResultSets,HandleOutputParameters)处理SQL的返回结果集;

ParameterHander(prepare,parameterize,batch,update,query)拦截SQL的语法构建的处理.

Mybatis仅可以编写针对上面四种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4中接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。编写插件需要实现Mybatis的Interceptor接口并腹泻intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,还需要在配置文件中配置编写的插件。

MyBatis和hibernate的区别

(1)mybatis是半自动的ORM框架,hibernate是全自动的ORM框架。ORM是在关系型数据库和对象之间作一个映射。因此hibernate可以直接使用dao层的语句生成对数据库操作的sql,mybatis是需要在mapper.xml中编写sql。

(2)hibernate的数据库移植性好,mybatis则移植性低。hibernate是利用dao层的代码去生成对应的不同的数据库sql,因此可以适应不同种类的数据库语言。但是mybatis是需要在mapper.xml中编写sql,只能针对某种数据库语言,因此移植性步入hibernate。

(3)hibernate拥有完整的日志系统,mybatis则欠缺一些。hibernate日志系统非常健全,涉及广泛,包括:sql记录,关系异常,优化警告,缓存提示,脏数据警告等。mybatis则除了基本记录功能外,功能薄弱很多。

(4)sql优化上,mybatis比hibernate方便很多。由于mybatis的sql都是写在xml里,因此优化sql比hibernate方便很多。而hibernate的sql很多都是自动生成的,无法直接维护sql;所有hql,但是功能不如sql强大。写sql的灵活度上hibernate不如mybatis强大。

(5)缓存机制上,hibernate要比mybatis更好一些。Mybatis的二级缓存配置都是在每个具体的表-对象映射中进行详细配置,这样针对不同的表可以自定义不同的缓存机制。并且Mybatis可以在命名空间中共享相同的缓存配置和实例,通过Cache-ref来实现。而Hibernate对查询对象有着良好的管理机制,用户无需关心SQL.所以在使用二级缓存时如果出现脏数据,系统会报出错误并提示。

MyBatis的一级缓存和二级缓存

一级缓存是指sqlSession级别的缓存。一级缓存的作用域默认是一个SqlSession.Mybatis默认开启一级缓存。也就是在同一个SqlSession中,执行相同的查询SQL,第一次会去数据库进行查询,并写到缓存中;第二次以后直接去缓存中取。当执行sql查询的过程中发生了增删改操作,Mybatis会将SqlSession的缓存清空。一级缓存的范围有SESSION和STATEMENT两种,默认是SESSION,如果不想使用一级缓存,可以把一级缓存的范围指定为STATEMENT,这样每次执行完一个Mapper中的语句后都会讲一级缓存清除。如果需要更改一级缓存的范围,可以在Mybatis的配置文件中,通过localCacheScope执行。

<setting name="localCacheScope"value="STATEMENT"/>

二级缓存是在多个SqlSession在同一个Mapper文件中共享的缓存,是Mapper级别的。

(1)流程:

当开启二级缓存后,在配置文件中配置<setting name="cacheEnabled" value="true"/>这行代码,Mybatis会为SqlSession对象生成Executor对象时,还会生成一个对象:CachingExecutor,我们称之为装饰者,这里用到了装饰器模式。那么CachingExecutor的作用是什么呢?就是当一个查询请求过来时,CachingExecutor会接到请求,先进行二级缓存的查询,如果没命中,就交给真正的Executor来查询,再到一级缓存中查询,如果还没命中,再到数据库中查询。然后把查询到的结果再返回CachingExecutor,它进行二级缓存,最后再返回给请求方。它是executor的装饰者,增强executor的功能,具有查询缓存的作用。当配置<setting name="cacheEnabled" value="false"/>时,请求过来时,BaseExecutor这个抽象类会接到请求,就不进行二级缓存的查询。

(2)开启:

(1)在配置文件mybatis-config.xml中配置二级缓存,默认是开启的
<setting name="cacheEnabled" value="true"/>

(2)在mapper文件中配置,开启当前文件的二级缓存
<!-- 每个Mapper文件使用一个缓存对象 -->
<cache/>

<!-- 如果是多个Mapper文件共用一个缓存对象 -->
<cache-ref />

(3)针对要查询的statement使用缓存,即在<select>节点中配置如下属性:
useCache="true"

(3)说明 (参考:https://www.cdsy.xyz/computer/programme/mybatis/230131/cd39955.html

  • 映射语句文件中的所有 select 语句将会被缓存。
  • 映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。
  • 缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。
  • 根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。
  • 缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。
  • 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
  • 二级缓存在被多个文件共同使用时可能会出现脏数据情况。

MyBatis执行批量插入,如何返回主键ID列表

这种批量插入返回主键ID的只适应于主键自增的情况:使用 useGeneratedKeys="true" keyProperty="主键属性"这两个属性配置插入sql,适用单条数据插入。

mapper.xml中sql:   
    <insert id="getIdsBySaveBatch" useGeneratedKeys="true" keyProperty="id">
        insert into user(name,age)
        values
        <foreach collection="list" item="item" separator=",">
            (#{item.name}, #{item.age})
        </foreach>
    </insert>
mapper接口
    int getIdsBySaveBatch(List<User> users);

结果:

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门