MyBatis 源码解析 面试题总结

MyBatis源码学习环境下载

文章目录

    • 1、工作原理
      • 1.1 初始化
        • 1.1.1 系统启动的时候,加载解析全局配置文件和相应的映射文件
        • 1.1.2 建造者模式帮助我们解决复杂对象的创建:
      • 1.2 处理SQL请求的流程
        • 1.2.1 通过sqlSession中提供的API方法来操作数据库
        • 1.2.2 获取接口的代码对象-得到的其实是 通过JDBC代理模式获取的一个代理对象
        • 1.2.3 处理完请求之后,需要关闭会话SqlSession
      • 1.3 底层
        • 1.3.1 原理图:
        • 1.3.2 源码结构:
    • 2、MyBatis中的缓存
      • 2.1 缓存的作用
      • 2.2 缓存的设计
        • 2.2.1 架构设计
        • 2.2.2 一级缓存和二级缓存
          • 一级缓存:session级别,默认开启
          • 开启二级缓存:
        • 2.2.3 缓存的处理顺序
          • 2.2.3.1 先在二级缓存中查找
          • 2.2.3.2 Executor.query()先走一级缓存查询,一级缓存也没有的话,则进行DB查询
    • 3、如何扩展MyBatis中的缓存
      • 3.1 架构理解
      • 3.2 实际开发
        • 3.2.1 自定义三级缓存
    • 4、MyBatis中的涉及的设计模式
      • 4.1 从整体架构设计分析
        • 4.1.1 基础模块:
          • cache缓存模块:装饰器模式
          • logging日志模块:适配器模式、策略模式、代理模式
          • reflection反射模块:工程模式、装饰器模式
          • datasource数据源:工程模式
          • transaction事务模块:工厂模式
          • SqlSessionFactory:SqlSessionFactoryBuilder建造者模式
    • 5、谈谈你对SqlSessionFactory的理解
    • 6、谈谈你读SqlSession的理解
      • 6.1 SqlSession
      • 6.2 SqlSession的安全问题
        • 6.2.1 非线程安全:
        • 6.2.2 Spring中是如何解决DefaultSqlSession的数据安全问题?
    • 7、谈谈你对MyBatis的理解
    • 8、谈谈MyBatis中分页的理解
      • 8.1 谈谈分页的理解:
      • 8.2 分页的实现
        • 8.2.1 逻辑分页:RowBounds
        • 8.2.2 物理分页:拦截器实现,执行分页语句的组装
    • 9、谈谈MyBatis中的插件原理
      • 9.1 插件设计的目的:
      • 9.2 实现原理:
        • 9.2.1 创建自定义Java类,通过@Interceptor注解来定义相关的方法签名
        • 9.2.2 在对应的配置文件中通过plugin来注册自定义的拦截器
        • 9.2.3 拦截器的作用
    • 10、不同Mapper中的id是否可以相同?
    • 11、谈谈对MyBatis架构设计的理解
      • 11.1 接口层
      • 11.2 核心层
      • 11.3 基础模块
    • 12、谈谈对传统JDBC开发的不足
    • 13、MyBatis中数据源模块的设计
      • 连接池工作原理
    • 14、MyBatis中事务模块的设计
      • 14.1 事务的理解ACID
      • 14.2 MyBatis中的事务
        • 14.2.1 事务的配置
        • 14.2.2 事务接口的定义:定义了事务的基本行为
        • 14.2.3 在MyBatis中的事务管理有两个选择
        • 14.2.4 如何设置事务管理的方式
        • 14.2.5 在MyBatis中执行DML操作事务的处理逻辑
    • 15、谈谈你对Mapper接口的设计理解
      • 15.1 接口的对应的规则
      • 15.2 接口的设计原理
      • 15.3 代理对象执行的逻辑的本质还是会执行SqlSession中相关的DML操作的方法
      • 15.4 为何要多包一层代理对象
    • 16、谈谈你对Reflector模块的理解
    • 17、MyBatis的类型转换模块
    • 18、整合MyBatis
      • 18.1 单纯Spring整合MyBatis
      • 18.2 SpringBoot整合MyBatis

1、工作原理

1.1 初始化

1.1.1 系统启动的时候,加载解析全局配置文件和相应的映射文件

// 1.获取配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

全局配置文件:mybatis-config.xml

映射文件:mapper/*.xml

// 2.加载解析配置文件并获取 SqlSessionFactory 对象
// SqlSessionFactory 的实例我们没有通过 DefaulSqlSessionFactory 直接来获取
// 而是通过一个 Builder 对象来建造的
// SqlSessionFactory 生产 SqlSession 对象的 SqlSessionFactory 应该是单例
// 全局配置文件和映射文件 也只需要在 系统启动的时候完成加载操作
// 通过建造者模式来 构建复杂的对象: 1.完成配置文件的加载解析  2.完成 SqlSessionFactory 的创建
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

加载解析的相关信息存储在SqlSessionFactory对象的Configuration属性里,

1.1.2 建造者模式帮助我们解决复杂对象的创建:

  1. 完成配置文件的加载解析
  2. 完成 SqlSessionFactory 的创建

1.2 处理SQL请求的流程

通过工厂得到SqlSession对象

// 3.根据 SqlSessionFactory 对象获取 SqlSession 对象
SqlSession sqlSession = factory.openSession();

1.2.1 通过sqlSession中提供的API方法来操作数据库

// 4.通过sqlSession中提供的API方法来操作数据库
List<User> list = sqlSession.selectList("com.boge.mapper.UserMapper.selectUserList");

1.2.2 获取接口的代码对象-得到的其实是 通过JDBC代理模式获取的一个代理对象

// 获取接口的代码对象-得到的其实是 通过JDBC代理模式获取的一个代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);

1.2.3 处理完请求之后,需要关闭会话SqlSession

//5.关闭会话
sqlSession.close();//关闭session  清空一级缓存

1.3 底层

全局配置文件的加载解析:Configuration

映射文件的加载解析:Configuration.mappedStatements

/**
     * MappedStatement 映射
     *
     * KEY:`${namespace}.${id}`
     */
    protected final Map<String, MappedStatement> mappedStatements = new StrictMap<>("Mapped Statements collection");

生产了DefaultSqlsession实例对象,完成了Executor对象的创建,以及二级缓存CachingExecutor的装饰,同时完成了插件逻辑的植入。

selectOne():二级缓存->一级缓存->数据库插入

    // 1. 读取配置文件,读成字节输入流,注意:现在还没解析
    InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

    // 2. 解析配置文件,封装Configuration对象   创建DefaultSqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

    // 3. 生产了DefaultSqlsession实例对象   设置了事务不自动提交  完成了executor对象的创建
    SqlSession sqlSession = sqlSessionFactory.openSession();

    // 4.(1)根据statementid来从Configuration中map集合中获取到了指定的MappedStatement对象
       //(2)将查询任务委派了executor执行器
    User user =  sqlSession.selectOne("com.lagou.mapper.IUserMapper.findById",1);
    System.out.println(user);

    User user2 =  sqlSession.selectOne("com.lagou.mapper.IUserMapper.findById",1);
    System.out.println(user2);

    // 5.释放资源
    sqlSession.close();

1.3.1 原理图:

MyBatis 源码解析 面试题总结_第1张图片

1.3.2 源码结构:

MyBatis 源码解析 面试题总结_第2张图片

2、MyBatis中的缓存

2.1 缓存的作用

降低数据源的访问频率,从而提高数据源的处理能力,提高服务器的响应速度。

2.2 缓存的设计

2.2.1 架构设计

MyBatis 源码解析 面试题总结_第3张图片

通过装饰者模式对Cache接口的工作做增强

MyBatis 源码解析 面试题总结_第4张图片

装饰者 作用
BlockingCache 阻塞的 Cache 实现类
FifoCache 基于先进先出的淘汰机制的 Cache 实现类
LoggingCache 支持打印日志的 Cache 实现类
LruCache 基于最少使用的淘汰机制的 Cache 实现类
ScheduledCache 定时清空整个容器的 Cache 实现类
SerializedCache 支持序列化值的 Cache 实现类
SoftCache 软引用缓存装饰器
SynchronizedCache 同步的 Cache 实现类
TransactionalCache 支持事务的 Cache 实现类,主要用于二级缓存中
WeakCache 弱引用缓存装饰器

2.2.2 一级缓存和二级缓存

一级缓存:session级别,默认开启

<setting name="localCacheScope" value="STATEMENT"/>

二级缓存:SqlSessionFactory级别(工厂/进程级别)

开启二级缓存:
  • 在mybatis配置文件中配置cacheEnabled为true

    
    <setting name="cacheEnabled" value="true"/>
    
    <setting name="lazyLoadingEnabled" value="true"/>
    
  • 在映射文件中添加cache标签,可以在cache标签中更细致的增加配置

    
    <cache />
    
  • 命名空间下的所有标签放开二级缓存

  • 可以通过在标签中添加 useCache=false 指定api不走二级缓存

mybatis-config.xml

MyBatis 源码解析 面试题总结_第5张图片

2.2.3 缓存的处理顺序

  • 获取mapper映射文件中cache标签里的配置MappedStatement.getCache()
  • 如果cache配置不为空,从二级缓存中查找(List) TransactionalCacheManager.getObject(cache, key);
  • 如果没有值,则执行查询, Executor.query()这个查询实际也是先走一级缓存查询,
  • 一级缓存也没有的话,则进行DB查询
  • 先将查询到的结果放入缓存TransactionalCacheManager.putObject(cache, key, list),再返回结果
2.2.3.1 先在二级缓存中查找

原因:找到概率更高,性能角度。

一级缓存 二级缓存
作用域 SqlSession级别 SqlSessionFactory级别
找到概率 5% 90%
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
            throws SQLException {

        // 从 MappedStatement 中获取 Cache,注意这里的 Cache 是从MappedStatement中获取的
        // 也就是我们上面解析Mapper中标签中创建的,它保存在Configration中
        // 我们在初始化解析xml时分析过每一个MappedStatement都有一个Cache对象,就是这里
        Cache cache = ms.getCache();

        // 如果配置文件中没有配置 ,则 cache 为空
        if (cache != null) {
            //如果需要刷新缓存的话就刷新:flushCache="true"
            flushCacheIfRequired(ms);
            if (ms.isUseCache() && resultHandler == null) {
                // 暂时忽略,存储过程相关
                ensureNoOutParams(ms, boundSql);
                @SuppressWarnings("unchecked")
                // 从二级缓存中,获取结果
                List<E> list = (List<E>) tcm.getObject(cache, key);
                if (list == null) {
                    // 如果没有值,则执行查询,这个查询实际也是先走一级缓存查询,一级缓存也没有的话,则进行DB查询
                    list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                    // 缓存查询结果
                    tcm.putObject(cache, key, list); // issue #578 and #116
                }
                // 如果存在,则直接返回结果
                return list;
            }
        }
        // 不使用缓存,则从数据库中查询(会查一级缓存)
        return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }
2.2.3.2 Executor.query()先走一级缓存查询,一级缓存也没有的话,则进行DB查询
    /**
     * 记录嵌套查询的层级
     */
    protected int queryStack;
    /**
     * 本地缓存,即一级缓存
     */
    protected PerpetualCache localCache;

    @SuppressWarnings("unchecked")
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
        // 已经关闭,则抛出 ExecutorException 异常
        if (closed) {
            throw new ExecutorException("Executor was closed.");
        }
        // 清空本地缓存,如果 queryStack 为零,并且要求清空本地缓存。
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
            clearLocalCache();
        }
        List<E> list;
        try {
            // queryStack + 1
            queryStack++;
            // 从一级缓存中,获取查询结果
            list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
            // 获取到,则进行处理
            if (list != null) {
                handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
            // 获得不到,则从数据库中查询
            } else {
                list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
            }
        } finally {
            // queryStack - 1
            queryStack--;
        }
        if (queryStack == 0) {
            // 执行延迟加载
            for (DeferredLoad deferredLoad : deferredLoads) {
                deferredLoad.load();
            }
            // issue #601
            // 清空 deferredLoads
            deferredLoads.clear();
            // 如果缓存级别是 LocalCacheScope.STATEMENT ,则进行清理
            if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                // issue #482
                clearLocalCache();
            }
        }
        return list;
    }

3、如何扩展MyBatis中的缓存

3.1 架构理解

3.2 实际开发

/**
 * 永不过期的 Cache 实现类,基于 HashMap 实现类
 *
 * @author Clinton Begin
 */
public class PerpetualCache implements Cache {
    /**
     * 缓存容器
     */
    private Map<Object, Object> cache = new HashMap<>();
        @Override
    public void putObject(Object key, Object value) {
        cache.put(key, value);
    }
}

3.2.1 自定义三级缓存

创建Cache接口的实现,重写putObject和getObject方法

在mapper映射文件中的cache标签里增加type属性,关联自定义的Cache接口的实现

<cache type="org.mybatis.caches.ehcache.EhcacheCache" />

如果未添加type,会默认读取 PERETUAL (二级缓存)

4、MyBatis中的涉及的设计模式

4.1 从整体架构设计分析

4.1.1 基础模块:

cache缓存模块:装饰器模式

Cache接口 定义了缓存的基本行为

PerpetualCache基于Cache实现,针对于缓存的功能 1.缓存数据淘汰;2.缓存数据的存放机制;3.缓存数据添加是否同步【阻塞】;4.缓存对象是否同步处理…做了增强处理–>代理模式

以及很多装饰类,灵活增强出适用于不同业务场景的Cache实现

logging日志模块:适配器模式、策略模式、代理模式

帮助我们适配不同的日志框架

Log接口针对不同日志框架,有不同的实现类,做增强处理

reflection反射模块:工程模式、装饰器模式
datasource数据源:工程模式
transaction事务模块:工厂模式
SqlSessionFactory:SqlSessionFactoryBuilder建造者模式

5、谈谈你对SqlSessionFactory的理解

  • 目的:创建SqlSession对象
  • 单例,在应用程序(服务)中只保存唯一的一份
  • SqlSessionFactory对象的创建是通过SqlSessionFactoryBuilder,
  • 同时也完成了全局配置文件Configuration和相关映射文件Mapper的加载和解析操作。
  • 涉及到了工厂模式和建造者模式
    /**
     * 构造 SqlSessionFactory 对象
     *
     * @param reader Reader 对象
     * @param environment 环境
     * @param properties Properties 变量
     * @return SqlSessionFactory 对象
     */
    @SuppressWarnings("Duplicates")
    public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        try {
            // 创建 XMLConfigBuilder 对象
            XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
            // 执行 XML 解析
            // 创建 DefaultSqlSessionFactory 对象
            return build(parser.parse());
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            ErrorContext.instance().reset();
            try {
                reader.close();
            } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
            }
        }
    }

    /**
    * 1.我们最初调用的build
    */
    public SqlSessionFactory build(InputStream inputStream) {
        //调用了重载方法
        return build(inputStream, null, null);
    }

    /**
     * 解析 XML
     *
     * 具体 MyBatis 有哪些 XML 标签,参见 《XML 映射配置文件》http://www.mybatis.org/mybatis-3/zh/configuration.html
     *
     * @param root 根节点
     */
    private void parseConfiguration(XNode root) {
        try {
            //issue #117 read properties first
            // 解析  标签
            propertiesElement(root.evalNode("properties"));
            // 解析  标签
            Properties settings = settingsAsProperties(root.evalNode("settings"));
            // 加载自定义的 VFS 实现类
            loadCustomVfs(settings);
            // 解析  标签
            typeAliasesElement(root.evalNode("typeAliases"));
            // 解析  标签
            pluginElement(root.evalNode("plugins"));
            // 解析  标签
            objectFactoryElement(root.evalNode("objectFactory"));
            // 解析  标签
            objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            // 解析  标签
            reflectorFactoryElement(root.evalNode("reflectorFactory"));
            // 赋值  到 Configuration 属性
            settingsElement(settings);
            // read it after objectFactory and objectWrapperFactory issue #631
            // 解析  标签
            environmentsElement(root.evalNode("environments"));
            // 解析  标签
            databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            // 解析  标签
            typeHandlerElement(root.evalNode("typeHandlers"));
            // 解析  标签
            mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
    }

6、谈谈你读SqlSession的理解

6.1 SqlSession

  • 作用:通过相关API来实现对应的数据的操作

  • SqlSession对象的获取需要通SqlSessionFactory来实现,

  • 作用域是会话级别,当一个新的会话到来的时候,需要新建一个SqlSession对象;当一个会话结束后,需要关闭相关会话资源

  • 处理请求的方式:

    1.通过相关crud的API直接处理

    2.通过getMapper(xx.xml)来获取相关mapper接口的代理对象来处理

6.2 SqlSession的安全问题

6.2.1 非线程安全:

MyBatis 源码解析 面试题总结_第6张图片

6.2.2 Spring中是如何解决DefaultSqlSession的数据安全问题?

  • DefaultSqlSession是非线程安全的,也就意味着我们不能把DefaultSqlSession声明在成员变量中。
  • 每个线程都应该有自己的SqlSession实例。
  • 最佳作用域是请求或方法作用域
  • 决不能将SqlSession实例引用放在一个类的静态域,甚至一个类的实例变量也不行。
  • 应该将SqlSession放在一个和HTTP请求相似的作用域中,每次请求打开一个SqlSession,返回一个响应后就关闭他,关闭操作放在finally块中。

在这里插入图片描述

  • Spring中提供了SqlSessionTemplate来实现SqlSession的相关定义。
  • 其中每一个方法都通过SqlSessionProxy来操作,这是一个动态代理对象。
  • 在动态代理对象中通过方法级别的DefaultSqlSession来实现相关的数据库操作。

7、谈谈你对MyBatis的理解

  • 使用频率最高的ORM框架、持久层框架
  • 提供了非常方便的API实现CRUD
  • 支持灵活的缓存处理方案,一级缓存、二级缓存、三级缓存
  • 支持相关的延迟数据加载处理
  • 还提供了非常多的灵活标签,来实现复杂的业务处理,if forech where trim set bind…
  • 相比Hibernate(全自动化)会更加灵活

8、谈谈MyBatis中分页的理解

8.1 谈谈分页的理解:

  • 数据库层面SQL:

    MySQL:LIMIT

    Oracle:rowid

8.2 分页的实现

8.2.1 逻辑分页:RowBounds

8.2.2 物理分页:拦截器实现,执行分页语句的组装

MyBatis 源码解析 面试题总结_第7张图片

9、谈谈MyBatis中的插件原理

9.1 插件设计的目的:

方便开发人员实现对MyBatis功能的增强

设计中MyBatis允许映射语句执行过程中的某一点进行拦截调用,允许使用插件拦截的方法包括:

在这里插入图片描述

9.2 实现原理:

9.2.1 创建自定义Java类,通过@Interceptor注解来定义相关的方法签名

MyBatis 源码解析 面试题总结_第8张图片

9.2.2 在对应的配置文件中通过plugin来注册自定义的拦截器

    <plugins>
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <property name="dialect" value="mysql"/>
        plugin>
    plugins>

9.2.3 拦截器的作用

  • 检查执行的SQL
  • 对执行SQL的参数做处理
  • 对查询的结果做装饰处理
  • 对查询SQL做分表处理

10、不同Mapper中的id是否可以相同?

可以相同,每一个映射文件的namespace都会设置为对应的mapper接口的全类路径名称

保证了每个Mapper映射文件的namespace是唯一的。

11、谈谈对MyBatis架构设计的理解

11.1 接口层

面向开发者,提供相关API

11.2 核心层

核心功能的实现:增删改查操作

11.3 基础模块

支撑核心层来完成核心的功能

MyBatis 源码解析 面试题总结_第9张图片

12、谈谈对传统JDBC开发的不足

JDBC MyBatis
资源 频繁的创建和释放数据库的连接对象,造成系统资源的浪费 通过全局配置文件设置相关的数据连接池
sql维护 sql语句直接写在了代码中,维护成本高,动态性要求较高 sql语句写在Mapper映射文件中的标签里
参数 向SQL中传递参数麻烦,where条件不一定,占位符和参数需要一一对应 自动完成Java对象和SQL中参数的映射
结果集 映射麻烦,需要循环封装,SQL本身的变化会导致解析的难度 通过ResultHandler自动将结果集映射到Java对象
拓展 不支持事务、缓存、延迟加载等功能 提供了相关实现
性能 运行性能更高 开发效率更高

13、MyBatis中数据源模块的设计

MyBatis 源码解析 面试题总结_第10张图片

完成相关标签的解析,存储在configuration中

    private void environmentsElement(XNode context) throws Exception {
        if (context != null) {
            // environment 属性非空,从 default 属性获得
            if (environment == null) {
                environment = context.getStringAttribute("default");
            }
            // 遍历 XNode 节点
            for (XNode child : context.getChildren()) {
                // 判断 environment 是否匹配
                String id = child.getStringAttribute("id");
                if (isSpecifiedEnvironment(id)) {
                    // 解析 `` 标签,返回 TransactionFactory 对象
                    TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
                    // 解析 `` 标签,返回 DataSourceFactory 对象
                    DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
                    DataSource dataSource = dsFactory.getDataSource();
                    // 创建 Environment.Builder 对象
                    Environment.Builder environmentBuilder = new Environment.Builder(id)
                            .transactionFactory(txFactory)
                            .dataSource(dataSource);
                    // 构造 Environment 对象,并设置到 configuration 中
                    configuration.setEnvironment(environmentBuilder.build());
                }
            }
        }
    }

MyBatis 源码解析 面试题总结_第11张图片

  • UnpooledDataSource:非数据库连接池的实现
  • PooledDataSource:数据库连接池的实现
  • 从连接池中获取连接对象:如果有空闲连接直接返回,活跃连接数是否超过了最大连接数,是否有连接超时的连接
  • 数据库连接池关闭连接,如果空闲连接没有超过最大连接数那么就返回空闲队列中。
  • 否则关闭真实的连接。

连接池工作原理

MyBatis 源码解析 面试题总结_第12张图片

MyBatis 源码解析 面试题总结_第13张图片

14、MyBatis中事务模块的设计

14.1 事务的理解ACID

14.2 MyBatis中的事务

14.2.1 事务的配置

MyBatis 源码解析 面试题总结_第14张图片

14.2.2 事务接口的定义:定义了事务的基本行为

MyBatis 源码解析 面试题总结_第15张图片

14.2.3 在MyBatis中的事务管理有两个选择

JDBC:在MyBatis中自己处理事务的管理

Managed:在MyBatis中没有处理任何的事务操作,这种情况下事务的处理会交给Spring容器来管理

14.2.4 如何设置事务管理的方式

在这里插入图片描述

14.2.5 在MyBatis中执行DML操作事务的处理逻辑

SqlSession.commit()

15、谈谈你对Mapper接口的设计理解

15.1 接口的对应的规则

名称需要对应

15.2 接口的设计原理

代理模式的使用

MyBatis 源码解析 面试题总结_第16张图片

创建动态代理对象

MyBatis 源码解析 面试题总结_第17张图片

15.3 代理对象执行的逻辑的本质还是会执行SqlSession中相关的DML操作的方法

MyBatis 源码解析 面试题总结_第18张图片

15.4 为何要多包一层代理对象

每调用一个mapper接口都需要在命名空间中指向接口路径

在这里插入图片描述

16、谈谈你对Reflector模块的理解

  • Reflector是MyBatis中提供的一个针对反射封装简化的模块:简化反射的相关操作。
  • 表结构的数据和Java对象中数据的映射,不可避免的会存在非常多的反射操作。
  • Reflector是一个独立的模块,可以把这个模块单独抽取出来,直接使用的。
  • 反射器,每个 Reflector 对应一个类。

MyBatis 源码解析 面试题总结_第19张图片

反射模块的具体设计:

MyBatis 源码解析 面试题总结_第20张图片

17、MyBatis的类型转换模块

MyBatis是如何解决Java中的类型和数据库中的字段类型的映射

MyBatis 源码解析 面试题总结_第21张图片

类型处理器:

  • 写:Java类型 --> JDBC类型

  • 读:JDBC类型 --> Java类型

  • SQL操作:读 写

  • 本质上执行的JDBC操作:

    String sql = "SELECT id,user_name,phone from user where id = ? and user_name = ?";
    ps = conn.prepareStatement(sql);
    ps.setInt(1,2);
    ps.setString(2,"张三");
    

TypeHandler --> BaseTypeHandler —> 具体的TypeHandler

18、整合MyBatis

18.1 单纯Spring整合MyBatis

  • 添加mybatis-spring依赖

            
            <dependency>
                <groupId>org.mybatisgroupId>
                <artifactId>mybatis-springartifactId>
                <version>1.2.2version>
            dependency>
    
  • 在spring配置文件spring-context.xml中添加数据源信息和sqlSessionFactory,将sqlSessionFactory注入容器

    	
    	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
    	    
    	    <property name="driverClassName" value="${jdbc.driver}" />
    		
    		<property name="url" value="${jdbc.url}" />
    		<property name="username" value="${jdbc.username}" />
    		<property name="password" value="${jdbc.password}" />
    		
    		<property name="initialSize" value="${jdbc.pool.init}" />
    		<property name="minIdle" value="${jdbc.pool.minIdle}" />
    		<property name="maxActive" value="${jdbc.pool.maxActive}" />
    		
    		<property name="maxWait" value="300000" />
    		
    		<property name="timeBetweenEvictionRunsMillis" value="60000" />
    		
    		<property name="minEvictableIdleTimeMillis" value="300000" />
    	bean>
     	
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            
            <property name="dataSource" ref="dataSource"/>
            
            <property name="typeAliasesPackage" value="com.hlframe"/>
            <property name="typeAliasesSuperType" value="com.hlframe.common.persistence.BaseEntity"/>
            
            <property name="mapperLocations" value="classpath:/mappings/**/*.xml"/>
            
    		<property name="configLocation" value="classpath:/mybatis-config.xml">property>
        bean>
    

18.2 SpringBoot整合MyBatis

  • 添加mybatis-plus-boot-starter依赖

            
            <dependency>
                <groupId>com.baomidougroupId>
                <artifactId>mybatis-plus-boot-starterartifactId>
                <version>${mybatis-plus.version}version>
                <scope>provided scope>
            dependency>
    
  • 将sqlSessionFactory注入容器

MyBatis 源码解析 面试题总结_第22张图片

你可能感兴趣的:(Spring全家桶及相关框架,面试题,mybatis,java,开发语言)