• 中文
    • 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.7k
    • 请登录之后再进行评论

      登录

      赞助本站

      • 支付宝
      • 微信
      • QQ

      感谢一直支持本站的所有人!

      单栏布局 侧栏位置: