干翻Mybatis源码系列之第七篇:Mybatis提供的集成缓存方案

简介: 干翻Mybatis源码系列之第七篇:Mybatis提供的集成缓存方案


第一章:Mybatis Orm的缓存

Mybatis定义了一个对象缓存,是Mybatis对缓存的封装,为了屏蔽实现的差异,这被定义成了一个接口Interface,这样的话,Mybatis的缓存基本上是存储于JVM内存中的。

一:Cache源码

public interface Cache {
  String getId(); //每一个MapStatement都会有一个Cache,需要有一个编号。     
  void putObject(Object key, Object value); // 向缓存中添加
  Object getObject(Object key); // 从缓存中获取数据
  Object removeObject(Object key);//从缓存中删除数据
  void clear();//清空
  int getSize();//容量
  ReadWriteLock getReadWriteLock();//读写锁是解决并发解决的。
}

Cache整个的存储结构,很类似于Java中Map。他使用的是JVM的内存。类似于键值对的这种结构。

第二章:自写Cache实现类

一:Map做容器

实现方式1:存多个数据,并且存键值对的数据。基本上容器就是Map了。

public class MyMybatisCache implements Cache {
    private Map<Object,Object> internalCache  = new HashMap();
    @Override
    public String getId() {
        return getClass().getName() ;
    }
    @Override
    public void putObject(Object key, Object value) {
          internalCache.put(key,value);
    }
    @Override
    public Object getObject(Object key) {
        return internalCache.get(key);
    }
    @Override
    public Object removeObject(Object key) {
        return internalCache.remove(key);
    }
    @Override
    public void clear() {
        internalCache.clear();
    }
    @Override
    public int getSize() {
        return internalCache.size();
    }
    @Override
    public ReadWriteLock getReadWriteLock() {
        return new ReentrantReadWriteLock();
    }
}

二:Jedis做容器

实现方式2:存多个数据,并且存键值对的数据。这里使用Redis也是可以的。

public class MyMybatisCache2 implements Cache {
    @Override
    public String getId() {
        return null;
    }
    @Override
    public void putObject(Object key, Object value) {
        // redis java client  ---> jedis
         //Jedis jedis = JedisUtils.openJedis();
        //key ---> json  value--->json
        //jedis.set(String,String);
        //key --- byte[] value --- byte[]
        //jedis.set(byte[],byte[]);
        byte[] k = SerializationUtils.serialize((Serializable) key);
        byte[] v = SerializationUtils.serialize((Serializable) value);
        //jedis.set(k, v);
    }
    @Override
    public Object getObject(Object key) {
        if (key != null) {
            byte[] k = SerializationUtils.serialize((Serializable) key);
            //Jedis jedis = JedisUtils.openJedis();
           // byte[] bytes = jedis.get(k);
//            if(bytes !=null){
//                Object value = SerializationUtils.deserialize(bytes);
//                return value;
//            }
        }
        return null;
    }
    @Override
    public Object removeObject(Object key) {
        return null;
    }
    @Override
    public void clear() {
    }
    @Override
    public int getSize() {
        return 0;
    }
    @Override
    public ReadWriteLock getReadWriteLock() {
        return null;
    }
}

org.apache.commons下的commons-lang3这个包是阿帕奇对于Java.lang包下的功能的增强(String、toString、equals、hashCode、Serializable…)

第三章:Mybatis的Cache实现类

以下是Mybatis的Cache实现类的截图:

Mybatis对于Cache的实现类大致位于两个包:org.apache.ibatis.cache.decorators,org.apache.ibatis.cache.impl

核心的Cache实现位于Impl包下:PerpetualCache,剩下的都是基于装饰器模式,(装饰器的核心目的是为了他的目标增加功能)所以这些装饰器都不是核心,这里的核心就是PerpetualCache。

一:核心PerpetualCache

public class PerpetualCache implements Cache {
  private final String id;
  private Map<Object, Object> cache = new HashMap<Object, Object>();
  public PerpetualCache(String id) {
    this.id = id;
  }
  @Override
  public String getId() {
    return id;
  }
  @Override
  public int getSize() {
    return cache.size();
  }
  @Override
  public void putObject(Object key, Object value) {
    cache.put(key, value);
  }
  @Override
  public Object getObject(Object key) {
    return cache.get(key);
  }
  @Override
  public Object removeObject(Object key) {
    return cache.remove(key);
  }
  @Override
  public void clear() {
    cache.clear();
  }
  @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }
  @Override
  public boolean equals(Object o) {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    if (this == o) {
      return true;
    }
    if (!(o instanceof Cache)) {
      return false;
    }
    Cache otherCache = (Cache) o;
    return getId().equals(otherCache.getId());
  }
  @Override
  public int hashCode() {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    return getId().hashCode();
  }
}

底层使用HashMap来完成的,这个已经可以完成一个Cache基本的存储功能了。再次基础上想让Cache功能更强大,我们才用了装饰器的这样的Cache。

二:装饰器Cache

核心目标:为了让PerpetualCache的功能更加强大。

1:FifoCache和LruCache

增强了换出功能,换出功能最为核心的两种算法就是:Fifo先入先出,Lru最小使用。

Lru是默认的装饰器。

public class FifoCache implements Cache {
  private final Cache delegate;
  private final Deque<Object> keyList;
  private int size;
  public FifoCache(Cache delegate) {
    this.delegate = delegate;
    this.keyList = new LinkedList<Object>();
    this.size = 1024;
  }
  @Override
  public String getId() {
    return delegate.getId();
  }
  @Override
  public int getSize() {
    return delegate.getSize();
  }
  public void setSize(int size) {
    this.size = size;
  }
  @Override
  public void putObject(Object key, Object value) {
    cycleKeyList(key);
    delegate.putObject(key, value);
  }
  @Override
  public Object getObject(Object key) {
    return delegate.getObject(key);
  }
  @Override
  public Object removeObject(Object key) {
    return delegate.removeObject(key);
  }
  @Override
  public void clear() {
    delegate.clear();
    keyList.clear();
  }
  @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }
  private void cycleKeyList(Object key) {
    keyList.addLast(key);
    if (keyList.size() > size) {
      Object oldestKey = keyList.removeFirst();
      delegate.removeObject(oldestKey);
    }
  }
}
public class LruCache implements Cache {
  private final Cache delegate;
  private Map<Object, Object> keyMap;
  private Object eldestKey;
  public LruCache(Cache delegate) {
    this.delegate = delegate;
    setSize(1024);
  }
  @Override
  public String getId() {
    return delegate.getId();
  }
  @Override
  public int getSize() {
    return delegate.getSize();
  }
  public void setSize(final int size) {
    keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
      private static final long serialVersionUID = 4267176411845948333L;
      @Override
      protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
        boolean tooBig = size() > size;
        if (tooBig) {
          eldestKey = eldest.getKey();
        }
        return tooBig;
      }
    };
  }
  @Override
  public void putObject(Object key, Object value) {
    delegate.putObject(key, value);
    cycleKeyList(key);
  }
  @Override
  public Object getObject(Object key) {
    keyMap.get(key); //touch
    return delegate.getObject(key);
  }
  @Override
  public Object removeObject(Object key) {
    return delegate.removeObject(key);
  }
  @Override
  public void clear() {
    delegate.clear();
    keyMap.clear();
  }
  @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }
  private void cycleKeyList(Object key) {
    keyMap.put(key, key);
    if (eldestKey != null) {
      delegate.removeObject(eldestKey);
      eldestKey = null;
    }
  }
}

如何体现装饰器设计模式的:

@Test
    public void test2() {
      //原始核心Cache只有基本功能
        PerpetualCache perpetualCache = new PerpetualCache("sunshuai");
        //套一层之后,引入了换出功能
        LruCache lruCache = new LruCache(perpetualCache);
        //再次套一层之后,引入了日志功能。
        LoggingCache loggingCache = new LoggingCache(lruCache);
    }

本质上就是一个套娃的过程,所有的装饰器和他的目标继承都是这个吊样。

2:LoggingCache

简单说明:增加日志打印功能

3:BlockingCache

简单说明:增加同一时间只会有一个线程去缓存中找某个key的值。

4:ScheduleCache

简单说明:定期刷新缓存,一段时间之后就自动清空缓存,这里边可以设置一个时间间隔,大于时间间隔之后就直接清空缓存。尽量解决脏数据的问题。

5:SerializableCache

简单说明:自动完成key,value的序列化和反序列化的过程。

6:TransactionalCache

简单说明:加上这个装饰器之后,只有在事务操作成功时,才会对应数据放到缓存中。

7:SoftCache和WeakCache

简单说明:这两个基本上就是纯概念性的东西,根部用不到

弱引用:xxxxxx

软引用:Tomcat在设计HttpSession的时候使用的是软引用。

设计模式的区分:装饰器模式和代理设计模式:

装饰器设计模式:

Mybatis当中大量增加了装饰器设计模式,目标是为了核心目标拓展功能,使用套娃的方式,一层一层的增加拓展工功能。

代理设计模式:

为目标原始目标增加额外功能。

如何去区分和权衡呢?

装饰器和代理设计模式,他们的原始功能是不一样的,装饰器设计模式拓展的功能是他的本职工作,比方说Mybatis当中的装饰器模式,增强的是就是他的本质核心原始功能;而代理设计模式增加的是额外功能,跟他们本质原始功能是有根本区别的,比如说是对于Service增加了事务的功能,事务和我们原始Service的功能是不一致的,这是二者最本质的区别。

装饰器设计模式能套娃,代理设计模式不行,这样的描述对么?

一般代理很少套娃,但是不代表他不能套娃。代理也是可以套娃的,JDK的动态代理就可以实现。这里可以补充一下代码。

代理能做到无中生有

当你给我一个接口的时候,我只能通过动态代理这种方式帮你自动的创建接口的实现类,这件事装饰器做不了。最典型的就是SqlSesission中的getMapper();

GOF4人帮只提供了23中设计模式,除了这几种之后,还有委托设计模式…他们不属于23中经典的范畴。这些个设计模式他有这种开发中有这些特点:

1:Java开发中用不全23中设计模式。有些设计模式不适用于微服务和web开发

2:23中设计模式有很多很类似的东西,很像,这样的话,我们就需要区分,比如说我们的代理和装饰器。甚至,我们很多时候会把代理和装饰器和适配器做区分。

3:Mybatis当中还有工厂、建造者等等设计模式。

相关文章
|
6月前
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于 xml 的整合
本教程介绍了基于XML的MyBatis整合方式。首先在`application.yml`中配置XML路径,如`classpath:mapper/*.xml`,然后创建`UserMapper.xml`文件定义SQL映射,包括`resultMap`和查询语句。通过设置`namespace`关联Mapper接口,实现如`getUserByName`的方法。Controller层调用Service完成测试,访问`/getUserByName/{name}`即可返回用户信息。为简化Mapper扫描,推荐在Spring Boot启动类用`@MapperScan`注解指定包路径避免逐个添加`@Mapper`
269 0
|
4月前
|
缓存 Java 数据库
SpringBoot集成Ehcache缓存使用指南
以上是SpringBoot集成Ehcache缓存的基本操作指南,帮助你在实际项目中轻松实现缓存功能。当然,Ehcache还有诸多高级特性,通过学习和实践,你可以更好地发挥它的威力。
438 20
|
6月前
|
缓存 Java 数据库连接
Mybatis一级缓存、二级缓存详讲
本文介绍了MyBatis中的查询缓存机制,包括一级缓存和二级缓存。一级缓存基于同一个SqlSession对象,重复查询相同数据时可直接从缓存中获取,减少数据库访问。执行`commit`操作会清空SqlSession缓存。二级缓存作用于同一namespace下的Mapper对象,支持数据共享,需手动开启并实现序列化接口。二级缓存通过将数据存储到硬盘文件中实现持久化,为优化性能,通常在关闭Session时批量写入缓存。文章还说明了缓存的使用场景及注意事项。
197 7
Mybatis一级缓存、二级缓存详讲
|
8月前
|
Java 关系型数据库 数据库连接
简单易懂的 MyBatis 分库分表方案
本文介绍了一种基于 MyBatis 框架的数据库分库分表方案——shardino。不同于复杂插件方式,该方案通过客户端代码包装实现简便易懂的操作,显式处理分库分表逻辑,确保开发者清晰了解数据分布。项目地址:[https://github.com/pyloque/shardino](https://github.com/pyloque/shardino)。方案中,帖子表按 userId 字段 hash 分为 64 张表,平均分配到多个主从库中,配置文件管理 MySQL 组对象,支持读写分离和权重随机选择从库。代码示例展示了如何计算 partition number 并进行具体操作。
241 22
简单易懂的 MyBatis 分库分表方案
|
6月前
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于注解的整合
本文介绍了Spring Boot集成MyBatis的两种方式:基于XML和注解的形式。重点讲解了注解方式,包括@Select、@Insert、@Update、@Delete等常用注解的使用方法,以及多参数时@Param注解的应用。同时,针对字段映射不一致的问题,提供了@Results和@ResultMap的解决方案。文章还提到实际项目中常结合XML与注解的优点,灵活使用两者以提高开发效率,并附带课程源码供下载学习。
506 0
|
6月前
|
Java 数据库连接 数据库
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——MyBatis 介绍和配置
本文介绍了Spring Boot集成MyBatis的方法,重点讲解基于注解的方式。首先简述MyBatis作为持久层框架的特点,接着说明集成时的依赖导入,包括`mybatis-spring-boot-starter`和MySQL连接器。随后详细展示了`properties.yml`配置文件的内容,涵盖数据库连接、驼峰命名规范及Mapper文件路径等关键设置,帮助开发者快速上手Spring Boot与MyBatis的整合开发。
832 0
|
8月前
|
存储 缓存 Java
Java中的分布式缓存与Memcached集成实战
通过在Java项目中集成Memcached,可以显著提升系统的性能和响应速度。合理的缓存策略、分布式架构设计和异常处理机制是实现高效缓存的关键。希望本文提供的实战示例和优化建议能够帮助开发者更好地应用Memcached,实现高性能的分布式缓存解决方案。
152 9
|
8月前
|
缓存 NoSQL Java
Mybatis学习:Mybatis缓存配置
MyBatis缓存配置包括一级缓存(事务级)、二级缓存(应用级)和三级缓存(如Redis,跨JVM)。一级缓存自动启用,二级缓存需在`mybatis-config.xml`中开启并配置映射文件或注解。集成Redis缓存时,需添加依赖、配置Redis参数并在映射文件中指定缓存类型。适用于查询为主的场景,减少增删改操作,适合单表操作且表间关联较少的业务。
155 6
|
10月前
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
11月前
|
缓存 Java 数据库连接
使用MyBatis缓存的简单案例
MyBatis 是一种流行的持久层框架,支持自定义 SQL 执行、映射及复杂查询。本文介绍了如何在 Spring Boot 项目中集成 MyBatis 并实现一级和二级缓存,以提高查询性能,减少数据库访问。通过具体的电商系统案例,详细讲解了项目搭建、缓存配置、实体类创建、Mapper 编写、Service 层实现及缓存测试等步骤。
109 2

热门文章

最新文章