• 中文
    • English
  • 注册
  • 查看作者
  • Redis的简单使用

    一. 缓存数据到电脑内存

    使用Redis前,我们先新建一个SpringBoot项目,演示一下如何使用Spring的缓存将数据缓存在电脑的内存中

    首先做以下数据准备。

    1.  添加相关依赖

     <dependencies>
            <!--JDBC API-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
            <!--Mybatis Framework-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.0</version>
            </dependency>
            <!--MysqlDriver-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.47</version>
                <scope>runtime</scope>
            </dependency>
            <!-- mybatis-plus -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.1.2</version>
            </dependency>
            
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>

    2. 配置yml(接下来的项目中,我们将会使用Mybatis-plus,所以还需要配置Mybatis-plus)

    spring:
      datasource:
        url: jdbc:mysql://localhost:3306/demo2?useUnicode=true&characterEncoding=utf-8
        driver-class-name: com.mysql.jdbc.Driver
        username: root
        password: zhangjia
      jackson:
        date-format: yyyy-MM-dd HH:mm:ss
        time-zone: GMT+8
    mybatis:
      mapper-locations: classpath:mapper/*.xml
      type-aliases-package: io.zhangjia.redis.entity
      configuration:
        map-underscore-to-camel-case: true
    mybatis-plus:
      mapper-locations: classpath:mapper/*.xml
      type-aliases-package: io.zhangjia.redis.entity
      configuration:
        map-underscore-to-camel-case: true
    server:
      port: 8888
    logging:
      level:
        io.zhangjia.redis:  trace
      file: E:\logs\zhangjia.log

    3.  新建数据表,并随便插入几条数据

    create table book
    (
        id          int(6) auto_increment primary key,
        name        varchar(100),
        author      varchar(100),
        price       double(6, 2),
        status      int       default 1,
        create_time timestamp default CURRENT_TIMESTAMP
    );

    2.  新建Book表对应的实体类

    package io.zhangjia.redis.entity;
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    
    import java.sql.Timestamp;
    
    public class Book{
      @TableId(value = "id",type = IdType.AUTO)
      private Integer id;
      private String name;
      private String author;
      private Double price;
      private Integer status;
      private Timestamp createTime;
    
      //省略getter和setter
    }

    3.  新建mapper

    package io.zhangjia.redis.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import io.zhangjia.redis.entity.Book;
    import org.springframework.stereotype.Component;
    
    @Component
    public interface BookMapper extends BaseMapper<Book> {
    
    }

    这里我们使用Mybatis-plus内置的通用Mapper,所以只需要创建上面的接口即可,mapper.xml无需创建。

    4.  新建Service

    因为使用Redis需要给Service中的方法添加注解,所以该项目的Service不能再使用内置的通用Service,需要我们手动编写。

    package io.zhangjia.redis.service;
    import io.zhangjia.redis.entity.Book;
    import java.util.List;
    
    public interface BookService {
        List<Book> getBookList();
        Book getOne(Integer id);
        Book editBook(Book book);
        boolean delBook(Integer id);
    }
    
    //BookService的实现类
    package io.zhangjia.redis.service.impl;
    import com.baomidou.mybatisplus.core.toolkit.Wrappers;
    import io.zhangjia.redis.entity.Book;
    import io.zhangjia.redis.mapper.BookMapper;
    import io.zhangjia.redis.service.BookService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cache.annotation.CacheEvict;
    import org.springframework.cache.annotation.CachePut;
    import org.springframework.cache.annotation.Cacheable;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    @Service("bookService")
    public class BookServiceImpl implements BookService {
        @Autowired
        private BookMapper bookMapper;
        @Override
        public List<Book> getBookList() {
            return bookMapper.selectList(null);
        }
    
    
        @Override
        public Book getOne(Integer id) {
            return bookMapper.selectById(id);
        }
    
        @Override
        public Book editBook(Book book) {
            int i = bookMapper.updateById(book);
            return bookMapper.selectById(book.getId());
        }
    
        @Override
        public boolean delBook(Integer id) {
            return bookMapper.deleteById(id) == 1;
        }
    }

    5.  新建Controller

    package io.zhangjia.redis.controller;
    
    import io.zhangjia.redis.entity.Book;
    import io.zhangjia.redis.service.BookService;
    import io.zhangjia.redis.util.Result;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.List;
    
    @RestController
    public class BookController {
        @Autowired
        private BookService bookService;
    
    
        @GetMapping("/books")
        @ResponseBody
        public List<Book> books() {
            return bookService.getBookList();
        }
    
    
    
        @GetMapping("/book/{id}")
        public Book getBook(@PathVariable Integer id) {
            System.out.println("id = " + id);
            return bookService.getOne(id);
        }
    
        @DeleteMapping("/book/{id}")
        public Result delBook(@PathVariable Integer id) {
            System.out.println("id = " + id);
            boolean b = bookService.delBook(id);
            return new Result(b?"success":"error");
        }
    
        @PutMapping("/book")
        public Book editBook(Book book) {
            System.out.println("book = " + book);
            return bookService.editBook(book);
        }
    }

    到这里我们的准备功能就全部完成了,上述代码的功能就是对Book表进行一些简单的增删改查。以查询全部某一本书为例,我们在postman中访问http://localhost:8888/book/5,结果如下:

    Redis的简单使用

    打开IDEA的控制台,也能看到相关的日志信息,因为此时还没有做任何缓存操作,所以当我们多次访问http://localhost:8888/book/5时,每一次都会执行查询语句:

    Redis的简单使用

    那么如何开启缓存呢?

    二.  开启缓存

    首先在RedisApplication.java中添加@EnableCaching注解

    package io.zhangjia.redis;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cache.annotation.EnableCaching;
    
    @SpringBootApplication
    @MapperScan("io.zhangjia.redis.mapper")
    @EnableCaching
    public class RedisApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(RedisApplication.class, args);
        }
    
    }

    当你在配置类(@Configuration)上使用@EnableCaching注解时,会触发一个post processor,这会扫描每一个spring bean,查看是否已经存在注解对应的缓存。[1] 

    接下来在BookService中,为getOne方法添加@Cacheable注解:

    @Override
    //Cacheable当缓存中已经存在该数据, 那么直接从缓存中读取数据,如果缓存中不存在,那么将返回结果写入缓存
    @Cacheable(cacheNames = "book",key = "'book-' + #id")
    public Book getOne(Integer id) {
        return bookMapper.selectById(id);
    }

    其中cacheNames 意为,而key,因为相同的key会覆盖,所以这里我们以book-为前缀,以每本书的id为后缀来唯一区分一本书。

    此时重启项目,再次访问http://localhost:8888/book/5,无论访问多少次,只有第一次会执行SELECT语句,缓存已经生效

    Redis的简单使用

    虽然说缓存成功,但是也会产生新的问题:

    假如我们现在对id为5的这本书执行了修改或者删除操作,虽然说数据库里的数据修改成功了,但是因为我们之前对该书进行了缓存,所以当我们访问http://localhost:8888/book/5的时候,显示的还是未修改或者未删除前的数据,为了解决该问题,我们需要为修改和删除的方法分别添加对应的注解:

    package io.zhangjia.redis.service.impl;
    
    import com.baomidou.mybatisplus.core.toolkit.Wrappers;
    import io.zhangjia.redis.entity.Book;
    import io.zhangjia.redis.mapper.BookMapper;
    import io.zhangjia.redis.service.BookService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cache.annotation.CacheEvict;
    import org.springframework.cache.annotation.CachePut;
    import org.springframework.cache.annotation.Cacheable;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    @Service("bookService")
    public class BookServiceImpl implements BookService {
        @Autowired
        private BookMapper bookMapper;
        @Override
        //list一般不缓存
        public List<Book> getBookList() {
            return bookMapper.selectList(null);
        }
    
    
        @Override
        //Cacheable当缓存中已经存在该数据, 那么直接从缓存中读取数据,如果缓存中不存在,那么将返回结果写入缓存
            //unless:不进行缓存的条件
            @Cacheable(cacheNames = "book",key = "'book-' + #id",unless = "#result == null")
        public Book getOne(Integer id) {
            return bookMapper.selectById(id);
        }
    
        @Override
        //CachePut始终会将方法的返回值写入缓存
        @CachePut(cacheNames = "book",key = "'book-' + #book.id")
        public Book editBook(Book book) {
            int i = bookMapper.updateById(book);
            return bookMapper.selectById(book.getId());
        }
    
        @Override
        @CacheEvict(cacheNames = "book",key = "'book-' + #id")
        public boolean delBook(Integer id) {
            return bookMapper.deleteById(id) == 1;
        }
    }

    三.  使用Redis进行缓存

    在上一段中,我们的数据都是缓存在电脑的内存中,项目一重启就都没了,我们可以使用redis缓存我们的数据。

    在SpringBoot中使用Redis非常简单,只需要两个操作即可:

    1.  添加依赖

    以SpringBoot项目为例,使用Redis需要先在pom.xml文件中添加以下依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    2.  配置Redis

    在application.yml中配置Redis的host和端口号

    spring:
      redis:
        host: 192.168.112.129
        port: 6379 #其实不加这个也可以,默认就是6379
        database: 0 #其实不加这个也可以,默认就是存入第0个

    此时再次访问http://localhost:8888/book/5,缓存的数据已经存入Redis,即使重启项目,缓存的数据也不会消失

    Redis的简单使用

    java自带的序列化器将缓存数据序列化成了上图红框中的格式,但是这种格式并不利于我们查看,我们可以自定义Redis的序列化容器来将缓存的数据序列化成json的格式,新建对应的配置类即可:

    package io.zhangjia.redis.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.cache.RedisCacheConfiguration;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializationContext;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    import java.time.Duration;
    
    @Configuration
    public class SerializableConfig {
        @Bean
        public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
            //准备key的序列化器
            RedisSerializationContext.SerializationPair<String> keySerializer = RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer());
            //准备value的序列化器
            RedisSerializationContext.SerializationPair<Object> valueSerializer = RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer());
            RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                    .serializeKeysWith(keySerializer) //设置key的序列化器
                    .serializeValuesWith(valueSerializer) //设置value的序列化器
                    .entryTtl(Duration.ofDays(1)); //设置有效期为1天
                   // .disableCachingNullValues(); //不允许缓存空值当null值进行缓存时,会出现异常
            RedisCacheManager cacheManager = RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(redisConnectionFactory)
                    .cacheDefaults(cacheConfig).build();
            return cacheManager;
        }
    }

    此时将Redis中的数据清空(一定要记得清空,否则会报JsonParseException异常),再次访问http://localhost:8888/book/5,可以看到缓存中的数据已经是json格式:

    Redis的简单使用

    在SSM项目中,如果使用的不是jackson,而是fastjson,那么需要这样配置:

    先添加fastjson依赖:

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.59</version>
    </dependency>

    接下来将配置类的value的序列化器修改为以下内容即可:

    //jackson,
     RedisSerializationContext.SerializationPair<Object> valueSerializer = RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer());
    //将上面的内容修改为:
     //fastjson:
    //RedisSerializationContext.SerializationPair<Object> valueSerializer = RedisSerializationContext.SerializationPair.fromSerializer(new GenericFastJsonRedisSerializer());

    三.  Redis测试类

    最后给出redis的一个测试类,里面包含了几个常用的方法,了解即可。

    package io.zhangjia.redis;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.redis.connection.RedisConnection;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.test.context.junit4.SpringRunner;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class RedisApplicationTests {
    
        @Autowired
        private RedisTemplate redisTemplate; //注入redisTemplate
    
        @ Test   
        public void contextLoads() {
            RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
            String ping = connection.ping();
            System.out.println("ping = " + ping); //如果结果是PONG,说明redis正常工作
    
            String key = "name";
            String value = "zhangjia";
            
            //存储数据到redis
            Boolean set = connection.set(key.getBytes(), value.getBytes());
            System.out.println("存入是否成功?" + (set ? "是" : "否"));
            
            //根据key获取数据库中的数据
            byte[] name = connection.get(key.getBytes());
            System.out.println("name = " + new String(name));
            /*
                     * 输出:
                     * ping = PONG
                     * 存入是否成功?是
                     * name = zhangjia
                     * */
    
        }
        
      @ Test
            public void contextLoads2() {
            //给单个数据设置过期时间的两种方法
        //        ValueOperations<String, String> stringValueOperations = stringRedisTemplate.opsForValue();
        //        stringValueOperations.set("code","7896", Duration.ofSeconds(10)); //60秒后过期
                redisTemplate.setKeySerializer(new StringRedisSerializer());
                redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
                ValueOperations valueOperations = redisTemplate.opsForValue();
                valueOperations.set("codes", "7895", Duration.ofSeconds(20));
            }
    
    }

    参考资料

    [1] Spring的@EnableCaching注解

  • 0
  • 0
  • 0
  • 1.8k
  • 请登录之后再进行评论

    登录
    单栏布局 侧栏位置: