Mybatis源码细节探究:MappedStatement和Cache对象对照关系研究

简介: Mybatis源码细节探究:MappedStatement和Cache对象对照关系研究


前言

一:Mapper.xml被读取发生的事

1:Mapper.xml文件中一个标签会对应一个MappedStatement对象,那么没一个查询Select标签也是对应一个MappedStatement

2:Cache对象是基于Mapper.xml文件中的Cache标签创建。一个Cache标签只能创建出来一个Cache对象

结论就是一个Cache对象会被多个MS对象引用,属于一对多的关系。

细节分析

一:缓存内部就是PerpetualCache

PerpetualCache本质上就是个Hashmap,上边两个Select标签标签查询出来的数据都会缓存到这个Map缓存的数据当中。

二:缓存key

Cache中的key被封装成了CacheKey这样的一个对象。

1:key的构成

那什么玩意作为key呢?value这个玩意就存在于List当红在哪个就好了,那么key呢?

key的构成规则大概就是:integer + namespace.id + SQL + 参数 大体上是这么个玩意构成

2:真实key举例

-976698875:6380909722:com.baizhiedu.dao.UserDAO.queryAllUsers:0:2147483647:select id,name from t_user:default

通过这里我们可以知道,基于CacheKey也是能够实现拿到拼接SQL的一种方式,有了对应的SQL和参数,我们可以手动拼接SQL

3:什么时候入缓存?

按照上述的两个SQL语句执行完毕,并且事务提交之后,HashMap当中会有两条缓存数据

4:资源占用问题

一个Select标签对应的MP对象都会存储Cache,这样不是很占用内存资源么?

只是引用,Cache对象只有一个。

证明MappedStatement和Cache对象的多对一关系

一个十分重要的细节:必须事务提交之后,才能把Cache放入到缓存当中。

@Test
    public void test2() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        SqlSession sqlSession3 = sqlSessionFactory.openSession();
        UserDAO userDAO1 = sqlSession1.getMapper(UserDAO.class);
        UserDAO userDAO2 = sqlSession2.getMapper(UserDAO.class);
        UserDAO userDAO3 = sqlSession3.getMapper(UserDAO.class);
       /* List<User> users1 = userDAO1.queryAllUsersByPage();//--- ms ---> cache
        for (User user : users1) {
            System.out.println("user = " + user);
        }*/
        User user1 = userDAO1.queryUserById(4); //---ms ---> cache
        sqlSession1.commit();
        //userDAO1.queryAllUsers();
        System.out.println("-----------------------------------------");
        /*List<User> users2 = userDAO2.queryAllUsersByPage();
        for (User user : users2) {
            System.out.println("user = " + user);
        }*/
        sqlSession2.commit();
        System.out.println("-----------------------------------------");
//
 /*       User user = new User(4, "xiaowb");
        userDAO3.update(user);
        sqlSession3.commit();*/
    }

重点关注这行代码

User user1 = userDAO1.queryUserById(4); //---ms ---> cache
        sqlSession1.commit();

sqlSession1.commit();执行后才会放到缓存当中。

在这个位置上边的代码走过ms.getCache的时候,Cache对象size是0;

通过debug来证明的,不同的MappedStatement对应的Cache对象是一个。

自问自答

一:不同的Mapper文件查询标签缓存数据能不能放到一个Cache当中呢?

可以

这样做的目的就是:引用别的Mapper文件中的Cache来存储数据。

二:使用缓存后脏数据怎么办?

所谓脏数据就是对UseDao做更新操作,Mybatis对缓存数据是怎么处理的?

更新操作包括,update,delete,insert,Mybatis会将Mapper对应Cache对象当中所有的key以及对应的内容都清空!也就是Mapper所有缓存数据都干掉!

clear()操作根源是发生在TransactionalCache当中,这Cache的一个装饰器。他的作用就是事务执行完毕之后,完成一些操作,事务完成之后才会写入内存

如果是增删改操作,一定是事务提交的时候,会走将事务清空

public void commit() {
    if (clearOnCommit) {
      delegate.clear();//删除操作
    }
    flushPendingEntries();//方缓存操作
    reset();//中间对象清楚操作。
  }
  private void flushPendingEntries() {
    for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
      delegate.putObject(entry.getKey(), entry.getValue());
    }
    for (Object entry : entriesMissedInCache) {
      if (!entriesToAddOnCommit.containsKey(entry)) {
        delegate.putObject(entry, null);
      }
    }
  }

这个TransactionalCache的装饰器是非常好的,只要有增删改,Mapper对应的Cache数据全部被删除,避免脏数据。

关联查询怎么避免缓存脏数据?

关联查询怎么玩呢?也是往缓存中楞塞,但是有这样的一个问题,我们在UserDao.XML当中使用缓存,这样更新User数据会清空Cache。

但是如果有User和Order的关联查询,那么情空Order需要在使用Cache-ref标签的情况下才会出现,更新order数据会刷新User当中Cache标签的问题,因为ref标签会导致两个Mapper.xml用的是一个Cache对象。

相关文章
|
7月前
|
SQL 存储 Java
Mybatis源码解析:详述初始化过程
以上就是MyBatis的初始化过程,这个过程主要包括SqlSessionFactory的创建、配置文件的解析和加载、映射文件的加载、SqlSession的创建、SQL的执行和SqlSession的关闭。这个过程涉及到了MyBatis的核心类和接口,包括SqlSessionFactory、SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、Configuration、SqlSession和Executor等。通过这个过程,我们可以看出MyBatis的灵活性和强大性,它可以很好地支持定制化SQL、存储过程以及高级映射,同时也避免了几
140 20
|
Java 数据库连接 数据库
mybatis查询数据,返回的对象少了一个字段
mybatis查询数据,返回的对象少了一个字段
1057 9
|
SQL XML Java
mybatis-源码深入分析(一)
mybatis-源码深入分析(一)
|
SQL Java 数据库连接
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
|
前端开发 Java 数据库连接
表白墙/留言墙 —— 中级SpringBoot项目,MyBatis技术栈MySQL数据库开发,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
本文是一份全面的表白墙/留言墙项目教程,使用SpringBoot + MyBatis技术栈和MySQL数据库开发,涵盖了项目前后端开发、数据库配置、代码实现和运行的详细步骤。
318 0
表白墙/留言墙 —— 中级SpringBoot项目,MyBatis技术栈MySQL数据库开发,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
|
Java 数据库连接 mybatis
Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码
该文档详细介绍了如何在Springboot Web项目中整合Mybatis,包括添加依赖、使用`@MapperScan`注解配置包扫描路径等步骤。若未使用`@MapperScan`,系统会自动扫描加了`@Mapper`注解的接口;若使用了`@MapperScan`,则按指定路径扫描。文档还深入分析了相关源码,解释了不同情况下的扫描逻辑与优先级,帮助理解Mybatis在Springboot项目中的自动配置机制。
802 0
Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码
|
5月前
|
Java 数据库连接 数据库
Spring boot 使用mybatis generator 自动生成代码插件
本文介绍了在Spring Boot项目中使用MyBatis Generator插件自动生成代码的详细步骤。首先创建一个新的Spring Boot项目,接着引入MyBatis Generator插件并配置`pom.xml`文件。然后删除默认的`application.properties`文件,创建`application.yml`进行相关配置,如设置Mapper路径和实体类包名。重点在于配置`generatorConfig.xml`文件,包括数据库驱动、连接信息、生成模型、映射文件及DAO的包名和位置。最后通过IDE配置运行插件生成代码,并在主类添加`@MapperScan`注解完成整合
1023 1
Spring boot 使用mybatis generator 自动生成代码插件
|
8月前
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于注解的整合
本文介绍了Spring Boot集成MyBatis的两种方式:基于XML和注解的形式。重点讲解了注解方式,包括@Select、@Insert、@Update、@Delete等常用注解的使用方法,以及多参数时@Param注解的应用。同时,针对字段映射不一致的问题,提供了@Results和@ResultMap的解决方案。文章还提到实际项目中常结合XML与注解的优点,灵活使用两者以提高开发效率,并附带课程源码供下载学习。
704 0
|
10月前
|
前端开发 Java 数据库连接
Java后端开发-使用springboot进行Mybatis连接数据库步骤
本文介绍了使用Java和IDEA进行数据库操作的详细步骤,涵盖从数据库准备到测试类编写及运行的全过程。主要内容包括: 1. **数据库准备**:创建数据库和表。 2. **查询数据库**:验证数据库是否可用。 3. **IDEA代码配置**:构建实体类并配置数据库连接。 4. **测试类编写**:编写并运行测试类以确保一切正常。
467 2
|
Java 数据库连接 Maven
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和MyBatis Generator,使用逆向工程来自动生成Java代码,包括实体类、Mapper文件和Example文件,以提高开发效率。
600 2
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。