• Mybatis部分面试题


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

    动态sql:顾名思义就是动态的根据属性值来拼接数据库执行的sql语句,也就是多次查询或变更操作,根据传入的属性值不同,动态拼接出不同的可执行sql。包含判断为空、循环等

    动态sql包含:wheresetifforeachtrimwhenchoose

     

    动态sql执行原理:

    第一部分:在启动加载解析xml配置文件的时候进行解析,根据关键标签封装成对应的handler处理对象,封装成sqlSource对象存在mappedStatement

    调用流程:

    ISqlSessionFactoryBuilderbuilder对象的时候,调用XMLConfigBuilder解析sqlMapConfig.xml配置文件,在解析过程中使用到了私有的mapperElement(XNode parent)方法

    II、上面方法中通过构建XMLMapperBuilder,获取到所有的配置mapper配置,

    在调用private void configurationElement(XNode context)方法进行解析mapper.xml,通过void buildStatementFromContext(List<XNode> list, String requiredDatabaseId)方法解析mapper.xml内的每一个标签

    III、循环中构建XMLStatementBuilder对象,调用parseStatementNode()方法来封装mappedStatment对象,

    IIII、在过程中需要构建sqlSource对象,通过XMLLanguageDriver对象进行处理,在XMLLanguageDriver中构建解析动态标签对象XMLScriptBuilder

    第二部分:在执行过程中获取sqlSource中获取bondSql对象时,执行相应的标签handler

    调用查询执行到BaseExecutorquery方法时候会去getBoundSql并且将参数传进去,

    sqlSource接口DynamicSqlSource实现类中,调用getBoundSql方法执行过程共创建DynamicContext对象进行判定解析封装成SqlSource对象返回。

    构建出来的sqlSource如下图:

     

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

    mybatis支持延迟加载,主要包括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都有哪些Executor执行器?它们之间的区别是什么?

    SimpleExecutor:每执行一次updateselect,就开启一个Statement对象,用完立刻关闭Statement对象。

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

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

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

    默认是SimplExcutor,需要配置在创建SqlSession对象的时候指定执行器的类型即可

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

    1)一级缓存:Mybatis的一级缓存是指SqlSession级别的,作用域是SqlSessionMybatis默认开启一级缓存,在同一个SqlSession中,相同的Sql查询的时候,第一次查询的时候,就会从缓存中取,如果发现没有数据,那么就从数据库查询出来,并且缓存到HashMap中,如果下次还是相同的查询,就直接从缓存中查询,就不在去查询数据库,对应的就不在去执行SQL语句。当查询到的数据,进行增删改的操作的时候,缓存将会失效。在spring容器管理中每次查询都是创建一个新的sqlSession,所以在分布式环境中不会出现数据不一致的问题

     

    2)二级缓存:二级缓存是mapper级别的缓存,多个SqlSession去操作同一个mappersql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession。第一次调用mapper下的sql 的时候去查询信息,查询到的信息会存放到该mapper对应的二级缓存区域,第二次调用namespace下的mapper映射文件中,相同的SQL去查询,回去对应的二级缓存内取结果,使用值需要开启cache标签,在select上添加useCache属性为true,在更新和删除时候需要手动开启flushCache刷新缓存。

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

    编写插件

    1、创建插件类实现interceptor接口并且使用注解标注拦截对象与方法

    package city.albert;
    
    /**
     * @author niunafei
     * @function
     * @email niunafei0315@163.com
     * @date 2020/6/11  6:08 PM
     */
    
    import org.apache.ibatis.executor.Executor;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.plugin.*;
    import org.apache.ibatis.session.ResultHandler;
    import org.apache.ibatis.session.RowBounds;
    
    import java.lang.reflect.Method;
    import java.util.Properties;
    
    /**
     * 注解声明mybatis当前插件拦截哪个对象的哪个方法
     * <p>
     * type表示要拦截的目标对象 Executor.class StatementHandler.class  ParameterHandler.class ResultSetHandler.class
     * method表示要拦截的方法,
     * args表示要拦截方法的参数
     *
     * @author niuanfei
     */
    @Intercepts({
            @Signature(type = Executor.class, method = "query",
                    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
    public class TestInterceptor implements Interceptor {
    
        /**
         * 拦截目标对象的目标方法执行
         *
         * @param invocation
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            //被代理对象
            Object target = invocation.getTarget();
            //代理方法
            Method method = invocation.getMethod();
            //方法参数
            Object[] args = invocation.getArgs();
            // do something ...... 方法拦截前执行代码块
            //执行原来方法
            Object result = invocation.proceed();
            // do something .......方法拦截后执行代码块
            return result;
        }
    
        /**
         * 包装目标对象:为目标对象创建代理对象
         *
         * @param target
         * @return
         */
        @Override
        public Object plugin(Object target) {
            System.out.println("MySecondPlugin为目标对象" + target + "创建代理对象");
            //this表示当前拦截器,target表示目标对象,wrap方法利用mybatis封装的方法为目标对象创建代理对象(没有拦截的对象会直接返回,不会创建代理对象)
            Object wrap = Plugin.wrap(target, this);
            return wrap;
        }
    
        /**
         * 设置插件在配置文件中配置的参数值
         *
         * @param properties
         */
        @Override
        public void setProperties(Properties properties) {
            System.out.println(properties);
        }
    }
    View Code

    2、在配置文件中写入plugins标签

        <plugins>
            <plugin interceptor="city.albert.TestInterceptor">
                <property name="name" value="name"/>
            </plugin>
        </plugins>
  • 相关阅读:
    通过Appium获取iOS应用元素定位的方法
    如何关闭 iPhone的“Automation Running”滚动?
    推荐一款非常牛的二维码生成器
    VNC Viewer 如何复制黏贴文本
    关于远程Mac电脑
    PyCharm 部署与配置 Vue 项目
    Xcode编译失败(build failed) 但是没有报错
    java怎么根据用户设定的时间执行定时任务
    【hyperf2.2】Hyperf框架 基于JsonRpc与Consul的微服务搭建
    Windows Phone 应用之虾米电台
  • 原文地址:https://www.cnblogs.com/niunafei/p/13096782.html
Copyright © 2020-2023  润新知