MyBatis-RedisCache源码分析

回顾

在前面,我们通过 redis​ 集成了 MyBatis​ 的二级缓存,440.MyBatis的二级缓存整合redis ,接下来,我们来分析一下 RedisCache​ 的源码。

源码分析

RedisCache 主要是通过实现 Cache 接口来做的。数据存储和获取主要是通过操作 jedis 来实现。

public final class RedisCache implements Cache {
    private final ReadWriteLock readWriteLock = new DummyReadWriteLock();
    private String id;
    private static JedisPool pool;

    public RedisCache(String id) {
        if (id == null) {
            throw new IllegalArgumentException("Cache instances require an ID");
        } else {
            this.id = id;
            RedisConfig redisConfig = RedisConfigurationBuilder.getInstance().parseConfiguration();
            pool = new JedisPool(redisConfig, redisConfig.getHost(), redisConfig.getPort(), redisConfig.getConnectionTimeout(), redisConfig.getSoTimeout(), redisConfig.getPassword(), redisConfig.getDatabase(), redisConfig.getClientName());
        }
    }
}

Terwer...大约 2 分钟MyBatis后端开发开源框架通过方法分析实现读取
MyBatis的二级缓存整合redis

MyBatis 自带的二级缓存存在的问题

在前面我们使用 @CacheNamespace​ 实现了 430.MyBatis的二级缓存 ,这个底层使用 HashMap​ 来实现。在 单机环境 下没有问题,但是在 分布式环境 下就不行了。


Terwer...大约 2 分钟MyBatis后端开发开源框架二级缓存使用问题加上环境
MyBatis的二级缓存

二级缓存的原理

二级缓存的原理和一级缓存一样,第一次查询会将数据放到缓存中,然后第二次查询直接去缓存读取。

但是一级缓存是基于 SqlSession 的,二级缓存是基于 mapper 的 namespace 的。

也就是说,多个 SqlSession 可以共享同一个二级缓存区域。如果两个 mapper 的 namespace 相同,那么即使是两个 mapper,这两个 mapper 执行 sql 查询的数据也将保存在相同的二级缓存区域中。


Terwer...大约 3 分钟MyBatis后端开发开源框架二级缓存mybatis
MyBatis的一级缓存

在 MyBatis 中,一级缓存是默认开启的。

通过场景来理解:

场景一

1、在一个 SqlSession 中,对 User 表进行两次根据 ID 的查询,查看发出 sql 语句的情况。

/**
 * 根据ID查询用户
 *
 * @param id
 * @return
 */
@Select("select * from user where id=#{id}")
User findUserById(Integer id);

@Before
public void before() throws Exception {
    System.out.println("before...");
    InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    //根据 sqlSessionFactory 产生 session
    sqlSession = sqlSessionFactory.openSession();
    // 这样也是可以的,这样的话后面就不用每次都设置了
    // sqlSession = sqlSessionFactory.openSession(true);
    userMapper = sqlSession.getMapper(IUserMapper.class);
}

@Test
public void testFindUserById() {
    //第一次查询,发出sql语句,并将查询出来的结果放进缓存中
    User user = userMapper.findUserById(1);
    System.out.println(user);

    //第二次查询,由于是同一个sqlSession,会在缓存中查询结果 //如果有,则直接从缓存中取出来,不和数据库进行交互
    User user2 = userMapper.findUserById(1);
    System.out.println(user2);
}

Terwer...大约 5 分钟MyBatis后端开发开源框架查询缓存mybatis一级缓存
MyBatis缓存的概念

缓存回顾

缓存就是内存中的数据,常常来自对数据库查询结果的保存。

使用缓存可以避免频繁与数据库交互,进而提高响应速度。

MyBatis 对缓存的支持

MyBatis 也提供了对缓存的支持,分为 一级缓存二级缓存。可以用下面的图来理解:

  1. 一级缓存是 SqlSession 级别的缓存。在操作数据库时,需要构造 SqlSession 对象,在对象中有一个 HashMap 的数据结构用于存储缓存数据。不同的 SqlSession 之间缓存的数据区域(HashMap)是不同的。
  2. 二级缓存是 Mapper 级别的缓存。多个 SqlSession 操作同一个 Mapper 的 sql 语句,多个 SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。

Terwer...大约 1 分钟MyBatis后端开发开源框架缓存二级缓存数据库数据mybatis一级缓存
MyBatis的注解实现复杂映射开发

xml 配置方式实现复杂映射回顾

实现复杂映射我们之前可以在映射文件中通过配置来实现,使用注解开发后,我们可以通过 @Results 注解,@Result 注解,@One 注解和 @Many 注解组合完成复杂关系的配置。

注解 说明
@Results
代替的是标签 <ResultMap>,该注解中可以使用单个的 @Result 注解,也可以使用 @Result 集合。
使用方式:@Results({@Result(), @Result()}) 或者 @Results(@Result())
@Result
代替了 <id> 标签和 <result> 标签
@Result 中的属性介绍
column:数据库中的列名
property:要装配的属性名
one:需要使用 @One 注解( @Result(one=@One)()
many:需要使用 @Many 注解( @Result(many=@many)()
@One(一对一) 代替了 <assocation> 标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。
@One 注解属性介绍
select:指定用来多表查询的 sqlmapper
使用格式:@Result(column="", property="", one=(select=""))
@Many(多对一) 代替了 <collection> 标签,是多表查询的关键,在注解中用来指定子查询返回对象集合。
使用格式:@Result(property="", column="", many=@many(select=""))

Terwer...大约 10 分钟MyBatis后端开发开源框架注解mybatiscomplexframeworkmybatis-14mapping
MyBatis常用注解及基本增删改查的注解实现

MyBatis 的常用注解

注解可以减少 Mapper 文件的编写,常用注解如下;

@Insert:实现新增

@Update:实现更新

@Delete:实现删除

@Select:实现查询

@Result:实现结果集封装

@Results:可以和@Result 一起使用,封装多个结果集

@One:实现一对一结果集封装


Terwer...大约 4 分钟MyBatis后端开发开源框架mybatisannotationframeworkmybatis-13
MyBatis复杂映射开发之多对多查询

多对多查询的模型

用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用。

多对多查询的需求:查询所有用户的同时查询出该用户对应的所有角色。

@startuml

!theme plain
top to bottom direction
skinparam linetype ortho

class sys_role {
   rolename: varchar(255)
   roleDesc: varchar(255)
   id: int(11)
}
class sys_user_role {
   userid: int(11)
   roleid: int(11)
}
class node2 as "user  /* 用户表 */" {
   username: varchar(50)
   password: varchar(50)
   birthday: varchar(50)
   id: int(11)
}

sys_user_role  ^-[#595959,plain]-  sys_role      : "roleid:id"
sys_user_role  ^-[#595959,plain]-  node2         : "userid:id"
@enduml

Terwer...大约 3 分钟MyBatis后端开发开源框架mybatisframework多对多many-to-manymybatis-12
MyBatis复杂映射开发之一对多查询

一对多查询模型

用户和订单表的关系为,一个用户有多个订单,一个订单只能属于一个用户。

一对多查询需求:查询多有用户,与此同时查询用户具有的订单信息。

一对多查询语句

对应的sql语句

select u.*,o.ordertime,o.total,o.uid from user u left join orders o on u.id = o.uid;

Terwer...大约 3 分钟MyBatis后端开发开源框架mybatisframeworkone-to-manymappingmybatis-11
MyBatis复杂映射开发之一对一查询

一对一查询需求

用户表和订单表的关系为:一个用户可以有多个订单,一个订单只能从属于一个用户

一对一查询需求:查询一个订单,同时查询出该订单所对应的用户

对应的sql语句:select * from orders o,user u where o.uid=u.id

查询结果如下:

id ordertime total uid id username password birthday
1 2022-03-17 17:15:33 3000 1 1 lucy 123 2022-03-17 17:15:56
2 2022-03-17 17:15:33 4000 1 1 lucy 123 2022-03-17 17:15:56
3 2022-03-17 17:15:33 5000 2 2 tom 123 2022-03-17 17:15:56

Terwer...大约 3 分钟MyBatis后端开发开源框架frameworkmybatisquerymybatis-10
2
3