美文网首页
模块一_MyBatis模块习题

模块一_MyBatis模块习题

作者: 西西弗斯XD | 来源:发表于2020-08-04 10:54 被阅读0次

序言:

文章内容输出来源:拉勾教育Java高薪训练营。
本篇文章是学习课程中的一部分课后笔记

自定义框架流程.png

一、自定义持久层框架Ipersistence是如何解决JDBC存在的问题?

    1. 使用配置文件解决硬编码问题
    1. 使用C3P0连接池解决了频繁创建释放数据库连接问题
    1. 在simpleExecute中使用了反射进行了参数的设置
    1. 在simpleExecute中使用了内省进行返回结果集的封装

二、进行自定义持久层框架Ipersistence优化时主要为了解决哪些问题?

    1. 应用在Dao层,整个操作的模板代码重复
    1. 调用sqlSession方法时、参数statementId硬编码

三、关于configuration 与 mappedStatement配置类的说法:

    1. 使用dom4j对sqlMapConfig.xml解析时会将解析出来的内容以不同形式封装到Configuration对象中
    1. 使用dom4j对mapper.xml解析时,每一个<select>标签的内容均对应一个mappedStatement对象
    1. configuration 中包含了mappedStatement的引用

四、关于genericTokenParser类的说法:

    1. 该类只能通过有参构造进行创建
    1. genericTokenParser是通用的标记解析类
    1. genericTokenParser在解析#{}占位符时必须通过标记处理类tokenHandler的配合
    1. genericTokenParser三个构造参数分别为开始标记、结束标记、标记处理器

五、关于sqlessionFactoyBuilder,sqlessionFactoy ,sqlession的说法:

    1. sqlessionFactoyBuilder最佳范围为方法范围,可定义为本地方法变量
    1. sqlessionFactoy 最佳范围是应用范围
    1. sqlession最佳范围是方法范围或请求范围

六、关于resultType与入参的说法:

    1. resultType表示返回值类型为:完整类名或别名,允许使用基本数据类型,String、int等
    1. resultType和resultMap的数据结构一样的,都是map结构
    1. mybatis中,除了使用@param注解来实现多个参数入参,还可以用Map对象实现多参数传递

七、mybatis中接口开发规范:

    1. mapper.xml中的namespace与mapper接口的类路径相同
    1. mapper接口方法名和mapper.xml中定义的每个statement的id相同
    1. mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType类型相同
    1. mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType类型相同

八、关于mybatis源码内容的说法:

    1. 涉及到的设计模式有:代理模式、builder模式、工厂模式、迭代器模式
    1. 功能架构可以分为三层:接口层、数据处理层、框架支撑层
    1. 支持用插件对statementHandler、paramterHandle、resultsetHandler等核心对象进行拦截
    1. Executor是执行器,负债sql的生成和查询缓存的维护
    1. statementtHandler封装了jdbc statement操作,负责对jdbc statement的操作
    1. typeHandler 负责java数据类型和jdbc数据类型之间的映射和转换
    1. sqlSource 负责根据用户传递的parameterObject动态生产sql语句,将信息封装到boundsql对象中

九、Mybatis动态sql是做什么的?都有哪些动态sql?简述一下动态sql的执行原理?

1.动态sql:
  • 在数据库执行sql之前,需要对sql进行整合处理,根据传入的参数值,以及匹配的条件,有可能需要动态的去
    判断是否为空,拼接sql。
2.动态sql:

<where> 、 <if> 、<include> 、<foreach>

<select id="selectUserByCondition" resultType="com.admin.entity.User">
        select
       <include refid="userInfo"/> 
        from
        user 
        <where> 
        <if test="name !=null and name !=''">
            and name = #{name }
        </if>
        <if test="roleIdList!=null and roleIdList.size() > 0 ">
            and role_id in
            <foreach collection="roleIdList" item="item" open="(" close=")" separator=",">
                #{item}
            </foreach>
        </if>
        </where>
</select>

map 入参

<select id="selectByMap" resultType="com.admin.entity.User">
  select * from user where
  <foreach collection="map" index="key" item="value"  separator="=">
    ${key} = #{value}
  </foreach>
</select>

<choose>、 <when>、 <otherwise>

<select id="selectUserInfo" resultType="com.admin.entity.User">
select * from user
 <where>
   <choose>
     <when test="name != null and name != ''">
       and name = #{name }
     </when>
     <otherwise>
       1 = 1
     </otherwise>
   </choose>
 </where>
</select>


<set>、 <trim>

<update id="updateUser" parameterType="com.admin.entity.User">
  update user
  <set>
    <if test="name != null and name != ''">
      name = #{name },
    </if>
  </set>
  where id = #{id}
</update>

<select id="selectUserByTrim" resultType="com.admin.entity.User">
  select * from user
  <trim prefix="where" suffix="order by id" prefixOverrides="and | or" suffixOverrides=",">
    <if test="name != null and name != ''">
      and name = #{name }
    </if>
    <if test="id != null and id !='' ">
      and id = #{id} 
    </if>
  </trim>
</select>


 -- 批量更新
<update id="updateUserInfoByList" parameterType="java.util.List">
        update user
        <trim prefix="set" suffixOverrides=",">
            <trim prefix="name =case" suffix="end,">
                <foreach collection="userList" item="item">
                    <if test="item.name!=null">
                        when id=#{item.id} then #{item.name}
                    </if>
                </foreach>
            </trim>
            <trim prefix="role_id =case" suffix="end,">
                <foreach collection="userList" item="item">
                    <if test="item.roleId!=null and item.roleId!=''">
                        when id=#{item.id} then #{item.roleId}
                    </if>
                </foreach>
            </trim>
        </trim>
        <where>
            <foreach collection="userList" separator="or" item="item">
                id = #{item.id}
            </foreach>
        </where>
    </update>

3.动态sql的执行原理:
  • MyBatis的动态SQL是基于OGNL表达式
  • 在解析xml配置文件的时候,会有一个createSqlSource的操作。
  • createSqlSource底层使用了XMLScriptBuilder调用parseScriptNode()的方法,解析xml的标签。
  • 在parseScriptNode()的方法中有一个parseDynamicTags()方法,会对nodeHandlers里的标签根据不同的handler来处理不同的标签 , 然后把DynamicContext结果放回SqlSource中。
  • 在Executor执行的时候,调用DynamicSqlSource的解析方法,并返回解析好的BoundSql,排好序,替换好参数的sql。

十、Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?

1.Mybatis支持延迟加载
  • 仅支持 association 关联对象和 collection 关联集合对象的延迟加载;
  • association 指的是一对一,collection 指的是一对多查询。
  • 配置文件中,可以配置是否启用延迟加载 lazyLoadingEnabled=true|false。
<settings>
    <!-- 打开延迟加载的开关 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!-- 将积极加载改为消极加载,即延迟加载 -->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>
2.实现原理
  • 使用 CGLIB 创建目标对象的代理对象,当调用目标方法时,进入拦
    截器方法,当数据需要的时候在调用sql查询DB。
  • 例如 : 调用 a.getB().getName(),拦截器 invoke()方法发现 a.getB()是
    null 值,那么就会单独发送事先保存好的查询关联 B 对象的 sql,把 B 查询上来,
    然后调用 a.setB(b),于是 a 的对象 b 属性就有值了,接着完成 a.getB().getName()方法的调用。

十一、Mybatis都有哪些Executor执行器?它们之间的区别是什么?

1.三种Executor:
  • SimpleExecutor 、ReuseExecutor 、 BatchExecutor
2.区别
  • SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。

  • ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。
    简言之,就是重复使用Statement对象。

  • BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中addBatch(),等待统一执行executeBatch(),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。
    与JDBC批处理相同。

  • 作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内

  • 在Mybatis配置文件中,可以指定默认的ExecutorType执行器类型,也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数。

十二、简述下Mybatis的一级、二级缓存(分别从存储结构、范围、失效场景。三个方面来作答)?

1.存储结构:
  • 一级缓存、二级缓存都是缓存到HashMap结构中。
2.范围:
  • 一级缓存SqlSession级别的,作用域是SqlSession,Mybatis默认开启一级缓存,在同一个SqlSession中,相同的Sql查询的时候,第一次查询的时候,就会从缓存中取,如果发现没有数据,那么就从DB查询出来,并且缓存到HashMap中,第二次查询直接在缓存中取,有数据就直接返回,不查DB。

  • 二级缓存mapper级别的,多个SqlSession去操作同一个mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession。
    第一次调用mapper下的sql 的时候去查询信息,查询到的信息会存放到该mapper对应的二级缓存区域,第二次调用namespace下的mapper映射文件中,相同的SQL去查询,直接在二级缓存内取结果。

3.失效场景:
  • 一级缓存 当进行增删改的操作的时候,缓存将会失效。
    在spring容器管理中每次查询都是创建一个新的sqlSession,所以在分布式环境中不会出现数据不一致的问题。

  • 二级缓存 使用时需要开启cache标签,在select上添加useCache属性为true,
    更新和删除时候需要手动开启flushCache刷新缓存。

十三、简述Mybatis的插件运行原理,以及如何编写一个插件?

1.运行原理:
  • mybatis可以编写针对Executor、StatementHandler、ParameterHandler、ResultSetHandler四个接口的插件,mybatis使用JDK的动态代理为需要拦截的接口生成代理对象,然后实现接口的拦截方法,所以当执行需要拦截的接口方法时,会进入拦截方法(AOP思想)。
2.如何编写:
  • 1.编写Intercepror接口的实现类

  • 2.设置插件的签名,告诉mybatis拦截哪个对象的哪个方法

  • 3.最后将插件注册到全局配置文件中

  • 示例:

//插件签名,告诉mybatis当前插件拦截哪个对象的哪个方法
//type表示要拦截的目标对象,method表示要拦截的方法,args表示要拦截方法的参数
@Intercepts({
    @Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
})
public class MySecondPlugin implements Interceptor {

    //拦截目标对象的目标方法执行
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("MySecondPlugin拦截目标对象:"+invocation.getTarget()+"的目标方法:"+invocation.getMethod());
        
        /*
         * 插件的主要功能:在执行目标方法之前,可以对sql进行修改已完成特定的功能
         * 例如增加分页功能,实际就是给sql语句添加limit;还有其他等等操作都可以
         * */
        
        //执行目标方法
        Object proceed = invocation.proceed();
        //返回执行后的返回值
        return proceed;
    }
    //包装目标对象:为目标对象创建代理对象
    @Override
    public Object plugin(Object target) {
        System.out.println("MySecondPlugin为目标对象"+target+"创建代理对象");
        //this表示当前拦截器,target表示目标对象,wrap方法利用mybatis封装的方法为目标对象创建代理对象(没有拦截的对象会直接返回,不会创建代理对象)
        Object wrap = Plugin.wrap(target, this);
        return wrap;
    }
    //设置插件在配置文件中配置的参数值
    @Override
    public void setProperties(Properties properties) {
        System.out.println("MySecondPlugin配置的参数:"+properties);
    }

}

  • 插件注册到全局配置文件中
<plugins>
    <plugin interceptor="com.mybatis_demo.plugin.MySecondPlugin"></plugin>
</plugins>

相关文章

网友评论

      本文标题:模块一_MyBatis模块习题

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