MyBatis基础及经典面试题

​一、概述

MyBatis是一款优秀的开源持久层框架,支持定制SQL,存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和结果集映射的封装,大大解放了生产力,今天我们来看它的简单实现以及常见的面试题。


二、基础回顾

首先我们来回顾一下MyBatis的基本用法,创建工程,引入坐标,如下图

添加jdbc.properties配置文件,输入数据库的连接信息,添加sqlMapConfig.xml,该配置文件用来添加MyBatis的别名映射包、数据库、以及mapper映射文件等信息,如下图

sqlMapConfig.xml配置文件内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
​
<configuration>
  <!--加载外部的properties文件-->
  <properties resource="jdbc.properties"></properties>
  <!--给实体类的全限定类名给别名-->
  <typeAliases>
    <!--给单独的实体起别名-->
    <!--  <typeAlias type="com.lagou.pojo.User" alias="user"></typeAlias>-->
    <!--批量起别名:该包下所有的类的本身的类名:别名还不区分大小写-->
    <package name="com.gzh.mybatis.pojo"/>
  </typeAliases> 
  <!--environments:运行环境-->
  <environments default="development">
    <environment id="development">
      <!--当前事务交由JDBC进行管理-->
      <transactionManager type="JDBC"></transactionManager>
      <!--当前使用mybatis提供的连接池-->
      <dataSource type="POOLED">
        <property name="driver" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
      </dataSource>
    </environment>
  </environments>
  <!--引入映射配置文件-->
  <mappers>
    <mapper resource="UserMapper.xml"></mapper>
  </mappers>
</configuration>

然后添加实体类GzhUser、UserInfo、WorkExperience

再添加GzhUserDao接口,内容如下

添加UserMapper.xml配置文件,内容如下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
​
<mapper namespace="com.gzh.mybatis.dao.GzhUserDao">
  <!--namespace : 名称空间:与id组成sql的唯一标识
    resultType:表明返回值类型-->
  <resultMap id="user" type="com.gzh.mybatis.pojo.GzhUser">
    <id column="id" property="id"/>
    <result column="username" property="username"/>
    <result column="password" property="password"/>
    <result column="phone" property="phone"/>
    <association property="userInfo" javaType="com.gzh.mybatis.pojo.UserInfo">
      <id column="i_id" property="id"/>
      <result column="user_id" property="userId"/>
      <result column="age" property="age"/>
      <result column="gender" property="gender"/>
      <result column="address" property="address"/>
    </association>
    <collection property="workExperiences" ofType="com.gzh.mybatis.pojo.WorkExperience">
      <id column="w_id" property="id"/>
      <result column="user_id" property="userId"/>
      <result column="company_name" property="companyName"/>
      <result column="salary" property="salary"/>
    </collection>
  </resultMap>
  <!--抽取sql片段-->
  <sql id="selectUser">
         select * from gzh_user
    </sql>
  <sql id="fields">
    a.id,a.username,a.password,a.phone,b.age,b.gender,b.address,c.company_name,c.salary
  </sql>
  <!--查询用户-->
  <select id="findAll" resultType="gzhUser">
    <include refid="selectUser"/>
  </select>
  <!--多条件组合查询:演示if-->
  <select id="findByUsername" resultType="gzhUser">
    <include refid="selectUser"/>
    <where>
      <if test="username !=null">
        username = #{username}
      </if>
    </where>
  </select>
  <!--多值查询:演示foreach-->
  <select id="findByIds" parameterType="list" resultType="gzhUser">
    <include refid="selectUser"/>
    <where>
      <foreach collection="array" open="id in (" close=")" item="id" separator=",">
        #{id}
      </foreach>
    </where>
  </select>
  <!--更新演示-->
  <update id="updatePwdById">
    update gzh_user set password = #{pwd}
    <where>
      id = #{id}
    </where>
  </update>
  <!--一对一查询演示-->
  <select id="findUserInfoByUsername" resultMap="user">
    <include refid="selectUser"/>
    a left join gzh_user_info b on a.id = b.user_id
    <where>
      <if test="username !=null">
        username = #{username}
      </if>
    </where>
  </select>
  <!--一对多查询演示-->
  <select id="findUserInfoAndWorksById" resultMap="user">
    select
    <include refid="fields"/>
    from gzh_user a left join gzh_user_info b on a.id = b.user_id left join work_experience c on a.id=c.user_id
    <where>
      <if test="id !=null">
        a.id = #{id}
      </if>
    </where>
  </select>
</mapper>

最后添加测试类UserTest,内容如下:

public class UserTest {
​
    @Test
    public void test1() throws IOException {
        // 获取配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
​
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
​
        SqlSession sqlSession = sqlSessionFactory.openSession(); //事务默认不提交
​
        GzhUserDao userDao = sqlSession.getMapper(GzhUserDao.class);
​
        List<GzhUser> users = userDao.findAll();
​
        users.forEach(item -> {
            System.out.println(item.toString());
        });
        System.out.println("=======findAll测试-END=======");
        GzhUser user = userDao.findByUsername("lisi");
​
        System.out.println(user.toString());
        System.out.println("=======findByUsername测试-END=======");
        List<GzhUser> userDaoByIds = userDao.findByIds(new Integer[]{1});
        userDaoByIds.forEach(item -> {
            System.out.println(item.toString());
        });
        System.out.println("=======userDaoByIds测试-END=======");
        boolean b = userDao.updatePwdById("123456", 1);
        sqlSession.commit();
        System.out.println(b);
        System.out.println("=======updatePwdById测试-END=======");
​
        GzhUser lisi = userDao.findUserInfoByUsername("lisi");
        System.out.println(lisi);
        System.out.println("=======一对一 测试-END=======");
​
        GzhUser userInfoAndWorksById = userDao.findUserInfoAndWorksById(1);
        System.out.println(userInfoAndWorksById);
        System.out.println("=======一对一和一对多 测试-END=======");
    }
}

test文件执行结果如下:

至此,MyBatis的基础已经演示完毕,主要介绍了单表查询,一对一查询(关键标签:association),一对多查询(关键标签:collection)。

代码连接:https://gitee.com/GPF1217/java-from-xiaobai-to-architect.git


三、精选面试题

  • Mybatis的mapper接口里方法名相同参数不同时支持方法的重载吗?

    不支持,因为Mybatis是通过接口的全限定类名+方法名作为唯一的key找到对应的sql,若是方法名相同,则key就不唯一了。
  • Mybatis中#{}和${}有什么区别?

    #{}是预编译处理,Mybatis在处理#{}时会将该符号替换为?,调用PreparedStatement的set方法来赋值;${}是字符串替换,Mybatis处理时会直接将该字符号替换为变量值。

    使用#{}可以防止sql注入攻击。

  • Mybatis有那些动态SQL,他们有什么用?

    Mybatis的动态sql主要通过标签来完成的,常见的动态sql标签有:where、if、choose、otherwise、foreach等等。

    Mybatis通过不同的标签,动态的拼接不同的sql,这样可以大大提高sql的复用率,减少代码的冗余。

  • Mybatis缓存机制了解吗?能简单说说吗?

    MyBatis分为一级缓存和二级缓存,一级缓存默认开启,二级缓存默认不开启。

    一级缓存是SqlSession级别,二级缓存是Mapper级别,两者当数据进行增删改操作时缓存会被删除。

    MyBatis二级缓存是单机情况下工作的,如果想要在集群环境下工作,需要将二级缓存放入分布式缓存中。

  • Mybatis插件机制主要对那些组件进行了拦截?

    Executor:执行器

    StatementHandler:SQL语法构建器

    ParameterHandler:参数处理器

    ResultSetHandler:结果集映射处理器

    主要对这四大核心组件进行额拦截

  • 如何自定义Mybatis插件?

    Mybatis可以对Executor、StatementHandler、ParameterHandler、ResultSetHandler进行拦截,利用动态代理做方法的增强,其插件原理就是基于此。

    自定义插件主要是实现Intercept接口,用注解标记需要拦截的组件,然后重写里面的intercept()方法,编写代码逻辑​​​​

  • Mybatis的延迟加载实现原理是什么?

    Mybatis仅支持association和collection关联对象的延迟加载。

    它的原理是:使用动态代理创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,拦截器方法发现关联对象的属性值为null,就会单独查询关联对象的sql,把关联对象的属性查出来,然后再通过反射赋值,这就是延迟加载的原理

  • Mybatis的执行原理是什么?

    Mybatis会将mapper映射文件中的sql封装成MappedStatement对象存储在内存中,根据每条sql的id为其生成动态代理对象,当用户通过接口调用方法时,Mybatis会根据接口的全限定类名+方法名,找到对应的动态代理对象,然后根据传入的参数拼接好对应的sql,通过代理对象执行该sql,得到返回值之后,根据配置的resultType反射成具体的对象,最后返回结果集。

感兴趣的读者朋友可以 关注本公众号,和我们一起学习探究。


本人因所学有限,如有错误之处,望请各位指正!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值