mybatis面试

1.说一下对Mybatis的理解

1.1 mybatis概念

mybatis是一个半自动的持久层ORM框架

1.2 什么是ORM

Object Relational Mapping【对象 关系 映射】,将Java中的对象与数据库中建议映射关系

1.3 Mybatis与Hibernate对比

Mybatis是一个半自动化,需要手写SQL;Hibernate是全自动化,无需手写SQL

1.4 Mybatis与JDBC对比

Mybatis是在JDBC基础之上封装的;JDBC是Java提供的能够直接使用Java代码来编写SQL语句并执行数据库操作的标准API,JDBC需要开发人员在Java代码中编写sql,耦合度高,mybatis可以通过xml方式和Java代码解耦

2. Mybatis的xml映射文件的规则

  1. xml映射文件的位置:resources/mapper文件夹下
  2. 映射文件名称要和Java接口名称一致
  3. 映射文件的namespace与接口的全限定名一致
  4. 映射文件的标签id和接口方法名称一致

3. 自定义映射

3.1 resultMap和resultType区别

  1. resultMap和resultType都是用来映射查询结果集的,当数据库字段属性名和Java对应的实体相同时可以直接使用resultType
  2. 如果当数据库字段属性名和Java对应的实体不相同,例如实体中字段是userName,数据库是user_name,那么就查询出的结果集就和Java实体对应不上,有两种解决办法
  • 起别名 例如 user_name as userName
    <select id="selectUser" resultType="User">
        select id,user_name as userName from user where id = #{id}
    </select>
  • 使用resultMap配置映射关系
    <resultMap id="userMap" type="com.lby.dto.User">
        <id column="id" jdbcType="INTEGER" property="id" />
        <result column="user_name" jdbcType="VARCHAR" property="userName" />
    </resultMap>
    <select id="selectUser" resultMap="userMap">
        select id,user_name from user where id = #{id}
    </select>
  1. resultMap和resultType只能使用一个

3.2 association和collection 映射

    <resultMap id="userAndDeptResultMapAssociation" type="employee">
        <!--  定义主键字段与属性关联关系 -->
        <id column="id" property="id"></id>
        <!--  定义非主键字段与属性关联关系-->
        <result column="user_name" property="userName"></result>
        <!--  用戶所属部门,自定义关联关系-->
        <association property="dept" javaType="com.lby.mybatis.pojo.Dept">
            <id column="dept_id" property="deptId"></id>
            <result column="dept_name" property="deptName"></result>
        </association>
    </resultMap>
    
    <resultMap id="userAndEmpResultMap" type="dept">
        <id property="deptId" column="dept_id"></id>
        <result property="deptName" column="dept_name"></result>
        <collection property="empList" ofType="com.lby.mybatis.pojo.Employee">
            <id column="id" property="id"></id>
            <result column="last_name" property="lastName"></result>
            <result column="email" property="email"></result>
            <result column="salary" property="salary"></result>
        </collection>
    </resultMap>
  • association标签:定义一对一或多对一关联关系
    • colunm:设置SQL中参数
    • property:SQL关联实体的属性
    • javaType:关联实体属性的类型
    • select:设置分步查询SQL全路径
    • fetchType:设置局部延迟加载【懒加载】是否开启
  • collection标签:定义一对多的关联关系
    • property:SQL关联实体的属性
    • ofType:关联实体属性的类型
    • fetchType:设置局部延迟加载【懒加载】是否开启

4. Mybatis插入数据的时候,怎样获取插入数据后的主键id

  • useGeneratedKeys:启用主键生成策略,添加成功后可以返回主键值,并存储到keyProperty
  • keyProperty:设置存储属性值,对应的是Java实体中的属性
    <insert id="addUser" useGeneratedKeys="true" keyProperty="id" parameterType="User">
        insert into user (id,user_name) values (#{id},#{userName})
    </insert>

5. mybatis中的参数是如何传递的

5.1 自定义个数参数

@Param可以将接口的参数和xml中#{}的参数进行映射

public List<Employee> selectEmpByNamed(@Param("lName")String lastName, @Param("salary") double salary);

5.2 复杂类型类类型Bean传参

    Integer updateByPrimaryKey(UserDTO dto);
    <update id="updateByPrimaryKey" parameterType="UserDTO" >
        update user
        <set >
            <if test="userName != null" >
                user_name = #{userName,jdbcType=VARCHAR},
            </if>
        </set>
        where id = #{id,jdbcType=BIGINT}
    </update>

5.3 map参数

    <select id="queryUserList" resultMap="UserMap" parameterType="java.util.Map">
        select
        <include refid="Base_Column_List" />
        from user u
        where 1=1
        <if test="userName != null">
            and  u.user_name = #{userName,jdbcType=VARCHAR}
        </if>
        ORDER BY u.id
    </select>

5.4集合参数传递

public List<User> selectUserList(@Param("ids") List<Integer> ids);
    <select id="selectUserByIds" resultType="User">
        SELECT `id`, user_name FROM `tbl_employee`
        <where>
            `id` in 
            (
                <foreach collection="ids" item="id" separator=",">
                    #{id}
                </foreach>
            )
        </where>
    </select>

6. 参数传递#和$区别

  1. 【#】底层执行SQL语句的对象,使用PreparedStatementd,预编译SQL,防止SQL注入安全隐患,相对比较安全。
  2. 【$】底层执行SQL语句的对象使用Statement对象,未解决SQL注入安全隐患,相对不安全。

7. mybatis查询返回值的集中情况

7.1 返回单个对象

	public User selectUserById(int id);
    <select id="selectUserById" resultType="user">
        SELECT id, user_name as userName FROM user WHERE id= #{id}
    </select>

7.2 返回对象集合

	public List<User> selectAllUser();
    <select id="selectAllUser" resultType="user">
        SELECT id, user_name as userName FROM user
    </select>

如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。

7.3 返回Map集合

  • 对象的id作为key
  • 对象作为value
	@MapKey("id")
	public Map<Integer,User> selectUserReturnMap();
    <select id="selectUserReturnMap" resultType="map">
        SELECT id, user_name as userName FROM user
    </select>

8. mybatis的分步查询

  1. 为什么要分步查询?
    多表关联查询时,可能会导致性能低下,通过分步查询,可以减少表连接的次数
  2. 如何进行分布查询?
    在一对多的association情况下,可以借助select属性,引用查询,具体实现如下
  • java代码
@Data
public class User {
    private int id;
    private String username;
    private List<Order> orders;
}
@Data
public class Order {
    private int id;
    private int userId;
    private String product;
}
  • Mapper接口
import java.util.List;
public interface UserMapper {
    User getUserById(int userId);
}
  • XML映射文件
<!-- UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">

    <resultMap id="userResultMap" type="User">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <!-- 使用association的select属性实现分步查询 -->
        <association property="orders" javaType="List" select="getOrdersByUserId"/>
    </resultMap>

    <select id="getUserById" resultMap="userResultMap" parameterType="int">
        SELECT * FROM users WHERE id = #{userId}
    </select>

    <select id="getOrdersByUserId" resultMap="orderResultMap" parameterType="int">
        SELECT * FROM orders WHERE user_id = #{userId}
    </select>

</mapper>

9. Mybatis延迟加载

  1. 什么是延迟加载
    需要时加载,不需要不加载
  2. 有什么好处
    只在必要的时候才查询,提高性能
  3. 如何实现延迟加载
  • 全局配置
<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 设置加载的数据是按需加载3.4.2及以后的版本该步骤可省略-->
<setting name="aggressiveLazyLoading" value="false"/>
  • 局部配置
    使用fetchType属性:
    eager:关闭局部延迟加载
    lazy:开启局部延迟加载
<association property="dept"
            select="com.atguigu.mybatis.mapper.DeptMapper.selectDeptByDeptId"
            column="dept_id"
            fetchType="eager">
</association>

10. 动态sql

  • if标签:用于完成简单的判断
  • where标签:用于解决where关键字及where后第一个and或or的问题
  • trim标签: 可以在条件判断完的SQL语句前后添加或者去掉指定的字符
    • prefix: 添加前缀
    • prefixOverrides: 去掉前缀
    • suffix: 添加后缀
    • suffixOverrides: 去掉后缀
  • set标签:主要用于解决set关键字及多出一个【,】问题
  • choose标签:类似java中if-else【switch-case】结构
  • foreach标签:类似java中for循环
    • collection: 要迭代的集合
    • item: 当前从集合中迭代出的元素
    • separator: 元素与元素之间的分隔符
    • open: 开始字符
    • close:结束字符
  • sql标签:提取可重用SQL片段
    <sql id="emp_col">
        id,
        last_name,
        email,
        salary
    </sql>
    <sql id="select_employee">
        select
            id,
            last_name,
            email,
            salary
        from
            tbl_employee
    </sql>
	<!-- 按条件查询员工信息【条件不确定】-->
    <select id="selectEmpByOpr" resultType="employee">
        <include refid="select_employee"></include>
        <where>
            <if test="id != null">
               and id = #{id}
            </if>
            <if test="lastName != null">
                and last_name = #{lastName}
            </if>
            <if test="email != null">
                and email = #{email}
            </if>
            <if test="salary != null">
                and salary = #{salary}
            </if>
        </where>
    </select>

    <select id="selectEmpByOprTrim" resultType="employee">
        <include refid="select_employee"></include>
        <trim prefix="where" suffixOverrides="and">
            <if test="id != null">
                id = #{id} and
            </if>
            <if test="lastName != null">
                last_name = #{lastName} and
            </if>
            <if test="email != null">
                email = #{email} and
            </if>
            <if test="salary != null">
                salary = #{salary}
            </if>
        </trim>
    </select>

    <update id="updateEmpByOpr">
        update
            tbl_employee
        <set>
            <if test="lastName != null">
                last_name=#{lastName},
            </if>
            <if test="email != null">
                email=#{email},
            </if>
            <if test="salary != null">
                salary=#{salary}
            </if>
        </set>
        where
            id = #{id}
    </update>


    <select id="selectEmpByOneOpr" resultType="employee">
        select
            <include refid="emp_col"></include>
        from
            tbl_employee
        <where>
            <choose>
                <when test="id != null">
                    id = #{id}
                </when>
                <when test="lastName != null">
                    last_name = #{lastName}
                </when>
                <when test="email != null">
                    email = #{email}
                </when>
                <when test="salary != null">
                    salary = #{salary}
                </when>
                <otherwise>
                    1=1
                </otherwise>
            </choose>
        </where>
    </select>

    <select id="selectEmpByIds" resultType="employee">
        select
            id,
            last_name,
            email,
            salary
        from
            tbl_employee
        <where>
            id in(
            <foreach collection="ids" item="id" separator=",">
                #{id}
            </foreach>
            )
        </where>

    </select>

    <insert id="batchInsertEmp">
        INSERT INTO
            tbl_employee(last_name,email,salary)
        VALUES
            <foreach collection="employees" item="emp" separator=",">
                (#{emp.lastName},#{emp.email},#{emp.salary})
            </foreach>
    </insert>

11. mybatis缓存机制

mybaits有二级缓存

11.1 一级缓存

  1. 基本概念
    一级缓存的模式有两种,默认是sqlSession级别对当前回话有效,另一种是statement是sql级别,sqlSession级别缓存在分布式环境下,一个sqlSession更新了数据,其他缓存结果的sqlSession是看不到更新后数据的,所以建议将缓存级别设定为Statetment
  2. sqlSession缓存的原理
    第一次获取数据,先从数据库加载数据,将数据缓存到Mybatis一级缓存中,一级缓存使用的是map,其中key是 hashCode + 查询sql的id + 编写sql的查询语句 + 参数构成;再次获取数据时,先从一级缓存获取,未获取到再成数据库中获取
  3. 一级缓存的失效情况
  • sql相同,但是sqlSession不同
  • 相同sqlSession中,两次相同sql查询中间执行了任何的增删改操作(这个操作会导致清空缓存)
  • 相同sqlSession中的两次相同搞的sql查询中间提交了事务

11.2 二级缓存

  1. 概念
    二级缓存是SqlSessionFactory级别的缓存,二级缓存默认关闭,二级缓存需要提交sqlSession或者关闭sqlSession才会缓存。
  2. 二级缓存如何使用
  • 全局配置文件开启二级缓存
<setting name="cacheEnabled" value="true"/>
  • 开始二级缓存对应的实体PO需要实现Serializable接口
  • 在对应需要序列化的xml映射文件标签中使用
  1. 二级缓存原理
    第一次查询先从数据库获取数据,让后将书记缓存到一级缓存,当SqlSession提交或者关闭后,将数据缓存到二级缓存;再次获取数据时先从一级缓存获取,一级缓存没有数据再从二级缓存获取,二级缓存也没有从数据库获取
  2. 二级缓存失效情况
    两次查询中见,执行增删改操作,一级二级缓存都会被清空

12. Mybatis字段映射原理

  1. 通过JDBC API向数据库发送SQL查询语句,获取查询结果集
  2. 结果集每一行的数据都封装成ResultSet对象
  3. 每一行数据,mybatis都会根据Java对象的属性和结果集的列名称匹配,匹配成功,则将结果映射到对应属性中
  4. 对于实体属性和对应表的列不一致,可以在sql中使用AS,或者通过resultMap定义映射关系
  5. 最终mybatis会将所有的映射成功的Java对象封装成一个List集合返回

13. 获取SqlSession的流程是怎样的

  1. SqlSessionFactoryBuilder解析配置文件,生成一个Configration对象,这个对象中包含了MyBatis需要的所有配置,然后会用这个Configration对象创建一个SqlSessionFactory对象,这个对象中包含了Configration对象。
  2. 调用SqlSessionFactory的openSesison方法,这个方法会创建一个Sql执行器(Executor组件中包含了Transaction对象),这个Sql执行器会代理你配置的拦截器方法。
  3. 获得上面的Sql执行器后,会创建一个SqlSession(默认使用DefaultSqlSession),这个SqlSession中也包含了Configration对象和上面创建的Executor对象,所以通过SqlSession也能拿到全局配置;获得SqlSession对象后就能执行各种CRUD方法了。

14. mysql的Sql是怎样执行的

  1. 调用SqlSession的getMapper方法,获得Mapper接口的动态代理对象MapperProxy
  2. 调用Mapper接口的所有方法都会调用MapperProxy的invoke方法(动态代理机制)创建MapperMethod对象
  3. 调用MapperMethod的execute方法,sqlSession会作为execute方法的入参
  4. 进入Executor组件的query方法,这个方法中会创建一个StatementHandler对象,这个对象中同时会封装ParameterHandler和ResultSetHandler对象。
  5. 使用ParameterHandler来给sql设置参数。
  6. 使用StatementHandler的增删改查方法获得结果,ResultSetHandler对结果进行封装转换,请求结束。

Executor、StatementHandler 、ParameterHandler、ResultSetHandler,Mybatis的插件会对上面的四个组件进行动态代理。

  • 11
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SuperLBY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值