IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Mybatis源码分析 -> 正文阅读

[Java知识库]Mybatis源码分析

一、Mybatis源码调用过程

对于下面一段常用的代码,分析其调用过程

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config.xml"));
SqlSession sqlSession = factory.openSession(true);
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
System.out.println(studentMapper.getStudentById(1));
sqlSession.close();

在这里插入图片描述

  • 1、根据mybatis-config.xml构建SqlSessionFactory接口实例,默认是DefaultSqlSessionFactory实现类
  • 2、调用SqlSessionFactory.openSession方法创建SqlSession接口实例,默认是DefaultSqlSession实现类,该实例里面持有Executor对象
  • 3、调用SqlSession.getMapper方法,获取用户定义的Mapper接口代理类,通过操作代理类的用户定义方法,来操作底层数据库,Mapper代理类使用JDK动态代理生成,其中的MapperProxy作为InvocationHandler角色出现
  • 4、代理类中MapperProxy根据不同的SQL调用sqlSession不同的方法,比如selectOne,insert,update等等,也就是形如sqlSession.selectOne(statementId, params);
  • 5、调用执行器Executor的相应方法,比如query、update等,Executor中处理一级缓存和二级缓存相关的逻辑,如果从缓存中查询到了数据,就不用再查数据库,也就是不用再执行下面的步骤
  • 6、执行器创建StatementHandler实例,通过StatementHandler创建真正的JDBC标准的Statement对象
  • 7、执行StatementHandler的query或update方法,底层执行的是JDBC标准的方法
  • 8、使用ResultSetHandler处理结果集并返回
  • 9、关闭sqlSession,归还connection连接

二、Mybatis一级缓存和二级缓存

缓存原理

无论是一级缓存还是二级缓存,底层都是map数据结构的支持,根据key来获取value,这个key在mybatis中是个复合值,用CacheKey对象表示,里面包含statementId、分页大小、分页offset、原生sql、查询参数五个条件,当且仅当这五个条件都命中时,才会匹配到对应的key。

注意:这里的statementId形如mapper.StudentMapper.getStudentById

一级缓存

mybatis默认开启的是一级缓存,一级缓存范围默认是SESSION,也就是说一级缓存只在同一个SqlSession中生效,要想关闭一级缓存,在mybatis-config.xml中设置localCacheScope为STATEMENT即可

  • 关闭一级缓存

    <?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>
        <settings>
            <setting name="localCacheScope" value="STATEMENT"/>
        </settings>
    </configuration>
    
  • 开启一级缓存时的请求流程

在这里插入图片描述

一级缓存是sqlSession级别的,因为一级缓存保存在baseExecutor中,而baseExecutor生命周期同sqlSession

二级缓存

mybatis二级缓存的全局配置默认也是开启的,但是还需要在mapper映射文件中分别配置开启缓存才会最终生效,当然也可以在mybatis-config.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>
        <settings>
            <setting name="cacheEnabled" value="false"/>
        </settings>
    </configuration>
    
  • 开启二级缓存时的请求流程

在这里插入图片描述

二级缓存是全局的,因为每个statementId对应的MappedStatement是单例,每个MappedStatement对象都持有一个Cache,相同namespace下的MappedStatement共享同一个Cache,CachingExecutor获取到MappedStatement对象的Cache后,根据cacheKey获取缓存中的值。

三、Mybatis拦截器

Mybatis支持对Executor、StatementHandler、PameterHandler和ResultSetHandler 接口进行拦截,也就是说会对这4种对象进行代理,其核心原理是暴露扩展接口(plugin方法)给用户,对上述4种对象进行动态代理,因此,plugin方法返回的对象必须是代理对象或者原始对象,可用使用Plugin.wrap方法生成该代理对象,代理对象中会调用拦截器的intercept方法,可以通过@Intercepts注解来告知哪些方法需要被拦截。当然,你也可以使用自己的方式去创建代理对象,并通过自己的方式去判断方法是否需要被拦截。

比如下面这个拦截器是我自定义的,目的是对SQL语句SELECT id, name, age FROM student WHERE id = ?进行分表,分表策略为id % 2

package interceptor;

import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;

import java.sql.Connection;
import java.util.Properties;

/**
 * mybatis分表拦截器
 *
 * @author debo
 */
@Intercepts({
        @Signature(
                type = StatementHandler.class,
                method = "prepare",
                args = {Connection.class, Integer.class}
        )
})
public class TableShardInterceptor implements Interceptor {

    private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
    private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
    private static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory();

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        if (invocation.getTarget() instanceof RoutingStatementHandler) {
            try {
                RoutingStatementHandler statementHandler = (RoutingStatementHandler) invocation.getTarget();
                BoundSql boundSql = statementHandler.getBoundSql();
                String originSql = boundSql.getSql();
                Object parameter = statementHandler.getParameterHandler().getParameterObject();
                // 计算分表后缀
                int tIdx = Integer.parseInt(parameter.toString()) % 2;
                String newSql = originSql.replace("student", "student_" + tIdx);
                // MetaObject是mybatis里面提供的一个工具类,类似反射的效果
                MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, REFLECTOR_FACTORY);
                // 把新语句设置回去
                metaStatementHandler.setValue("delegate.boundSql.sql", newSql);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        // 当目标类是RoutingStatementHandler类型时,才包装目标类,否者直接返回目标本身,减少目标被代理的次数
        return (target instanceof RoutingStatementHandler) ? Plugin.wrap(target, this) : target;
    }

    @Override
    public void setProperties(Properties properties) {
    }
}
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-02-27 10:57:09  更:2022-02-27 10:57:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年2日历 -2025/2/1 6:07:21-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码