Mybatis面试题整理

Mybatis面试题整理

面试题均是从各处搜集出来,慢慢整理

什么是Mybatis?

Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。
程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高。

Mybaits的优点:

基于SQL语句编程,相当灵活
SQL写在XML里,解除sql与程序代码的耦合,便于统一管理
能够与Spring很好的集成

MyBatis框架的缺点

SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

MyBatis与Hibernate有哪些不同?

Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句。
Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件,如果用hibernate开发可以节省很多代码,提高效率。

#{}和${}的区别是什么?

#{}是预编译处理,KaTeX parse error: Expected 'EOF', got '#' at position 23: …换。 Mybatis在处理#̲{}时,会将sql中的#{}替…{}时,就是把${}替换成变量的值。
使用#{}可以有效的防止SQL注入,提高系统安全性。

当实体类中的属性名和表中的字段名不一样 ,怎么办 ?

起别名

<select id=”selectorder” parametertype=”int” resultetype=”me.gacl.domain.order”>
	select order_id id, order_no orderno ,order_price price form orders where order_id=#{id};
</select>	

通过来映射字段名和实体类属性名的一一对应的关系。

<select id="getOrder" parameterType="int" resultMap="orderresultmap">
    select * from orders where order_id=#{id}
</select>
<resultMap type="me.gacl.domain.order" id="orderresultmap">
	<!--用id属性来映射主键字段-->
	<id property="id" column="order_id">
	<!--用result属性来映射非主键字段,property为实体类属性名,column为数据表中的属性-->
	<result property = “orderno" column =”order_no"/>
	<result property="price" column="order_price" />
</reslutMap>

模糊查询like语句该怎么写?

<!--在Java代码中添加sql通配符。-->
string wildcardname = “%smi%”;
list<name> names = mapper.selectlike(wildcardname);

<select id=”selectlike”>
	select * from foo where bar like #{value}
</select>
    
<!--在sql语句中拼接通配符,会引起sql注入-->
string wildcardname = “smi”;
list<name> names = mapper.selectlike(wildcardname);

<select id=”selectlike”>
	select * from foo where bar like "%"#{value}"%"
</select>

通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?不能重载

Dao接口即Mapper接口。接口的全限名,就是映射文件中的namespace的值;接口的方法名,就是映射文件中Mapper的Statement的id值;

接口方法内的参数,就是传递给sql的参数。

Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MapperStatement。
在Mybatis中,每一个

list<string> names = new arraylist();
names.add(“fred”);
names.add(“barney”);
names.add(“betty”);
names.add(“wilma”);
// 注意这里 executortype.batch
sqlsession sqlsession = sqlsessionfactory.opensession(executortype.batch);
try {
	namemapper mapper = sqlsession.getmapper(namemapper.class);
    for (string name : names) {
        mapper.insertname(name);
    }
	sqlsession.commit();
}catch(Exception e){
	e.printStackTrace();
	sqlSession.rollback();
	throw e;
}
finally {
	sqlsession.close();
}

一对一、一对多的关联查询

<!--一对多用collection-->
<!--一对一用association-->
<!--property代表的是实体类的字段 Javatype 代表的是返回的类型 oftype 是代表包含的对象-->
<association property="tmProject" javaType="tmProject">
<collection property="sysUserList" javaType="list" ofType="sysUser">
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.study.demo.dao.TaskDao">
<select id="queryTaskById" resultMap="taskData">
    SELECT
        tt.task_id,
        tt.title,
        tc.check_name,
        tp.project_name,
        su.user_name
    FROM tt_task tt
    LEFT JOIN tm_check tc ON tt.check_id = tc.check_id
    LEFT JOIN tm_project tp ON tt.project_id = tp.project_id
    LEFT JOIN tt_task_to_user tttu ON tt.task_id = tttu.task_id
    LEFT JOIN sys_user su ON tttu.user_id = su.user_id
    WHERE
         tt.task_id = #{id}
</select>
    <resultMap id="taskData" type="ttTask">
        <result property="taskId" column="task_id" />
        <association property="tmCheck" javaType="tmCheck">
            <result property="checkName" column="check_name"/>
        </association>
        <association property="tmProject" javaType="tmProject">
            <result property="projectName" column="project_name"/>
        </association>
        <collection property="sysUserList" javaType="list" ofType="sysUser">
            <result property="userName" column="user_name"/>
        </collection>
    </resultMap>
</mapper>

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,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的。

Mybatis的一级、二级缓存:

1)一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。
2)二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。
默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置 ;
3)对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear 掉并重新更新,如果开启了二级缓存,则只根据配置判断是否刷新。

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

① Mapper接口方法名和mapper.xml中定义的每个sql的id相同;

② Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同;

③Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同;

④ Mapper.xml文件中的namespace即是mapper接口的类路径。

作用域(Scope)和生命周期

作用域理解

  • SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在于创建SqlSessionFactory 的方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
    SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为 MyBatis 的本质就是 Java 对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用 MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。
  • 由于 SqlSessionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个 SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。
  • 因此在一般的应用中我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。所以说 SqlSessionFactory 的最佳作用域是应用作用域。如果说 SqlSessionFactory 相当于数据库连接池,那么 SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的commit、rollback 等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try…catch…finally… 语句来保证其正确关闭。
  • 所以 SqlSession 的最佳的作用域是请求或方法作用域。

@Param 使用mybatis的注解

@Param 中定义的变量名必须和 mapper中保持一致

// 用户登录
UserInfo signin(@Param("account")String account,@Param("passcode")String passcode);
<!-- 用户登录 -->
<select id="signin" resultType="UserInfo">
    select *
    from t_userinfo info
    where account=#{account} and passcode=#{passcode}
</select>

pagehelper

@Test
public void queryPageHelper(){
    PageHelper.startPage(1,2);
    List<TtTask> ttTasks = taskDao.queryAll();
    PageInfo<TtTask> ttTaskPageInfo = new PageInfo<>(ttTasks);
    System.out.println(ttTaskPageInfo.getList());
}

PageHelper的原理是基于拦截器实现的。拦截器的配置有两种方法,一种是在mybatis的配置文件中配置,一种是直接在spring的配置文件中进行:

<plugins>
    <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageHelper">
        <property name="dialect" value="mysql"/>
        <!-- 该参数默认为false -->
        <!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 -->
        <!-- 和startPage中的pageNum效果一样-->
        <property name="offsetAsPageNum" value="true"/>
        <!-- 该参数默认为false -->
        <!-- 设置为true时,使用RowBounds分页会进行count查询 -->
        <property name="rowBoundsWithCount" value="true"/>
        <!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 -->
        <!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型)
        <property name="pageSizeZero" value="true"/>-->
        <!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 -->
        <!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 -->
        <!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 -->
        <property name="reasonable" value="true"/>
            <!-- 3.5.0版本可用 - 为了支持startPage(Object params)方法 -->
            <!-- 增加了一个`params`参数来配置参数映射,用于从Map或ServletRequest中取值 -->
            <!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值 -->
            <!-- 不理解该含义的前提下,不要随便复制该配置
            <property name="params" value="pageNum=start;pageSize=limit;"/> -->
    </plugin>
</plugins>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>