您当前的位置: 首页 >  缓存

white camel

暂无认证

  • 1浏览

    0关注

    442博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

利用MyBatis自身本地缓存结合Redis实现分布式缓存 (一)

white camel 发布时间:2020-07-27 18:07:30 ,浏览量:1

目录
  • 一、Redis实现分布式缓存
    • 1、本地缓存分布式缓存的区别?
    • 2、利用MyBatis自身本地缓存结合Redis实现分布式缓存
  • 二、搭建SpringBoot和MyBatis整合测试
  • 三、自定义RedisCache缓存
  • 四、关于赠删改的RedisCache的操作
一、Redis实现分布式缓存

跳转到目录

1、什么是缓存?
  • 计算机内存中的一段数据
2、内存中数据特点
  • 读写快
  • 断电立即丢失
3、缓存解决了什么问题?
  • 提高网站吞吐量(请求响应)提高, 网站运行效率快
  • 缓存的存在是用来解决数据库访问压力
4、既然缓存能提高效率, 所有项目中都加缓存吗?
  • 使用缓存时一定是数据库中数据极少发生修改, 更多用于查询情况, 比如地址, 省, 市,
5、本地缓存分布式缓存的区别?

跳转到目录

  • 本地缓存 : 存在应用服务器内存中数据称之为本地缓存(Local Cache) — MyBatis中的一级/二级缓存就属于本地缓存
  • 分布式缓存: 存储在当前应用服务器内存之外的数据称之为分布式缓存(Distribute Cache) — Redis缓存输入分布式缓存, 因为它不局限于某个应用, 可以在多个应用之间使用

集群 : 将同一种服务的创建多个节点放在一起共同对系统提供服务的过程称之为集群分布式 : 有多个不同服务集群共同对系统提供服务这个系统称之为分布式系统

6、利用MyBatis自身本地缓存结合Redis实现分布式缓存

跳转到目录 MyBatis的缓存相关详细请参考 : MyBatis一级、二级缓存

  • mybatis中应用级缓存(二级缓存), SqlSessionFactory级别缓存, 所有会话共享
  • 如何开启(二级缓存), mapper.xml 中写标签, 在mybatis全局配置文件中开启缓存打开本地缓存
  • MyBatis中的一级/二级缓存的实现都是PerpetualCache, 查看Cache标签缓存实现org.apache.ibatis.cache.impl.PerpetualCache实现
  • 自定义RedisCache实现
    • 通过mybatis默认cache源码得知, 可以使用自定义Cache类 implements Cache接口, 并对里面方法进行实现 public class RedisCache implements Cache { }
    • 使用RedisCache实现

在这里插入图片描述

二、搭建SpringBoot和MyBatis整合测试

跳转到目录 注意: 这里连接的RedisMySQL服务都在远程服务器上

1、pom文件


    4.0.0

    
         org.springframework.boot
         spring-boot-starter-parent
         2.2.6.RELEASE
     

    com.zy
    red
    1.0-SNAPSHOT

    
        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.boot
            spring-boot-devtools
            runtime
            true
        

        
            org.projectlombok
            lombok
        

        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        



        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        


        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            2.1.3
        

        
        
            mysql
            mysql-connector-java
            5.1.38
        

        
        
            com.alibaba
            druid
            1.1.19
        

    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    


2、application.yml 全局配置文件
spring:
  redis:
    host: 192.168.80.131
    port: 6379
    database: 0

  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://192.168.80.131/redis_cache?characterEncoding=UTF-8
    username: root
    password: 1111
    
mybatis:
  mapper-locations: classpath:com/zy/mapper/*.xml
  type-aliases-package: com.zy.entity

logging:
  level:
    com:
      zy:
        dao: debug
3、entity, dao, service,启动类
@Data
@Accessors(chain = true) // 该注解的作用, user对象的set方法可以链式方式设置
public class User implements Serializable {

    private String id;
    private String name;
    private Integer age;
    private Date bir;
}

// ------------------------
public interface UserDao {
    List findAll();
}

// ------------------------
public interface UserService {
    List findAll();
}

@Service
@Transactional // 增删改要添加事务
public class UserServiceImpl implements UserService {

    @Resource
    private UserDao userDao;

    @Override
    public List findAll() {
        return userDao.findAll();
    }
}

// ------------------------
@SpringBootApplication
@MapperScan("com.zy.dao")
public class RedisCacheApplication {
    public static void main(String[] args) {
        SpringApplication.run(RedisCacheApplication.class, args);
    }
}
4、mapper文件
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


    
    
        SELECT id, name, age, bir FROM t_user
    
    

5、测试
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestUserService {

    @Resource
    private UserService userService;

    @Test
    public void test() {
        List users = userService.findAll();
        users.forEach(user -> System.out.println("user = " + user));

        System.out.println("------------------------------------");

        userService.findAll().forEach(System.out::println);
    }
}

在mybatis没有开启缓存, 每一次查询同样的sql语句, 都是从数据库查的 在这里插入图片描述 开启mybatis的二级缓存, 在mapper.xml文件中添加 且实体类要实现序列化标签即可



  • 此时第二次查询就是从缓存中获取了

在这里插入图片描述

注意:

  • mybatis虽然提供了这种缓存的方式, 但是这种方式只局限于当前的应用程序,当程序停止运行(JVM关闭)缓存就没了, 当再次启动程序后, 还是会去数据库中查询, 这种缓存属于本地缓存

MyBatis的缓存默认是实现public class PerpetualCache implements Cache来实现的, 底层使用的是一个HashMap;

  • mybatis的缓存是基于[namespace:sql语句:参数]来进行缓存的,意思就是,SqlSession的HashMap存储缓存数据时
  • 使用[namespace:sql:参数]作为key查询返回的语句作为value保存的。 在这里插入图片描述
三、自定义RedisCache缓存

跳转到目录

1、首先在mapper.xml文件中将的类型改为自定义RedisCache
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


    
	

    
    

    
    
        SELECT id, name, age, bir FROM t_user
    


2、RedisCache

因为RedisCache的实例化是由mybatis来操作的, 并不是Spring容器, 所以不能直接注入RedisTemplate; 此时的问题是如何拿到RedisTemplate, 往putObject / getObject方法中 放/取 值呢?

/**
 * Description: 用来获取SpringBoot创建好的工厂
 *
 * @author 
 * @date Created on 2020/7/27 17:12
 */
@Configuration
public class ApplicationContextUtils implements ApplicationContextAware {

    // 此时就是内部创建好的工厂
    private static ApplicationContext applicationContext;

    // 将创建好的工厂以参数的形式传递给这个类
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    // 提供在工厂中获取对象的方法
    public static Object getBean(String beanName) {
        return applicationContext.getBean(beanName);
    }
}

自定义RedisCache

/**
 * Description: 自定义Redis缓存实现
 *
 * @author 
 * @date Created on 2020/7/27 16:55
 */
public class RedisCache implements Cache {

    private final String id;

    // 必须存在构造方法
    public RedisCache(String id) {
        // id 就是当前放入缓存的mapper的namespace ---> com.zy.dao.UserDao
        System.out.println("id =============> " + id);
        this.id = id;
    }

    // 返回cache的唯一标识
    @Override
    public String getId() {
        return this.id;
    }

    // 往缓存中放值 --> 使用 RedisTemplate往缓存中放值
    // key ---> -983043073:3242099914:com.zy.dao.UserDao.findAll:0:2147483647:SELECT id, name, age, bir FROM t_user:SqlSessionFactoryBean
    @Override
    public void putObject(Object key, Object value) {
        System.out.println("key = " + key);
        // 通过ApplicationContextUtils来获取redisTemplate
        RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());

        // 使用hash类型作为缓存存储模型   ==> 
        redisTemplate.opsForHash().put(id, key.toString(), value);

    }

    // 往缓存中取值, 这个key
    // key ---> -983043073:3242099914:com.zy.dao.UserDao.findAll:0:2147483647:SELECT id, name, age, bir FROM t_user:SqlSessionFactoryBean
    @Override
    public Object getObject(Object key) {
        System.out.println("key = " + key);
        // 通过ApplicationContextUtils来获取redisTemplate
        RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());

        // 根据key从redis的hash类型中key获取数据
        return redisTemplate.opsForHash().get(id, key.toString());
    }

    @Override
    public Object removeObject(Object o) {
        return null;
    }

    @Override
    public void clear() {

    }

    @Override
    public int getSize() {
        return 0;
    }
}

此时运行程序就会发现, 第一次用来查询数据库, 然后将查询的语句存到Redis中, 第二次就从Redis中获取了;

在这里插入图片描述 在这里插入图片描述 此时当程序结束, 再次启动程序后, 两次查询都会丛Redis中获取了 在这里插入图片描述 我们再测试一个查询, 根据指定id来查询, 一条信息 在这里插入图片描述

四、关于增删改的RedisCache的操作

跳转到目录

  • 当执行增删改会执行RedisCache的clear方法, 清除Redis中的缓存; 防止缓存中的数据是没有更新过的脏数据 !
    // 当执行增删改会调用这个方法
    @Override
    public void clear() {
        System.out.println("清空缓存");
    }
  • 所以我们要对这个方法进行清空redis中的缓存, 否则, 执行完删除操作后, 数据库中的数据被删除了, 但是之前查询的操作还在redis中; 仍然可以查询出来

RedisCache类

/**
 * Description: 自定义Redis缓存实现
 *
 * @author 
 * @date Created on 2020/7/27 16:55
 */
public class RedisCache implements Cache {

    private final String id;

    // 必须存在构造方法
    public RedisCache(String id) {
        // id 就是当前放入缓存的mapper的namespace ---> com.zy.dao.UserDao
        System.out.println("id =============> " + id);
        this.id = id;
    }

    // 返回cache的唯一标识
    @Override
    public String getId() {
        return this.id;
    }

    // 往缓存中放值 --> 使用 RedisTemplate往缓存中放值
    // key ---> -983043073:3242099914:com.zy.dao.UserDao.findAll:0:2147483647:SELECT id, name, age, bir FROM t_user:SqlSessionFactoryBean
    @Override
    public void putObject(Object key, Object value) {
        // 使用hash类型作为缓存存储模型   ==> 
        getRedisTemplate().opsForHash().put(id, key.toString(), value);

    }

    // 往缓存中取值, 这个key
    // key ---> -983043073:3242099914:com.zy.dao.UserDao.findAll:0:2147483647:SELECT id, name, age, bir FROM t_user:SqlSessionFactoryBean
    @Override
    public Object getObject(Object key) {
        System.out.println("key = " + key);
        // 根据key从redis的hash类型中key获取数据
        return getRedisTemplate().opsForHash().get(id, key.toString());
    }

    // 为mybatis的保留方法, 默认没有实现
    @Override
    public Object removeObject(Object o) {
        System.out.println("根据指定key删除缓存");
        return null;
    }

    // 当执行增删改会调用这个方法
    @Override
    public void clear() {
        System.out.println("清空缓存");
        getRedisTemplate().delete(id); // 清空缓存
    }

    // 用来计算缓存数量
    @Override
    public int getSize() {
        // 获取hash中key value的数量
        return getRedisTemplate().opsForHash().size(id).intValue();
    }

    //封装redisTemplate
    private RedisTemplate getRedisTemplate(){
        //通过application工具类获取redisTemplate
        RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}
关注
打赏
1661428283
查看更多评论
立即登录/注册

微信扫码登录

0.0393s