springboot+spring data cache + redis框架搭建

redis

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets)与范围查询, bitmaps, hyperloglogs和 地理空间(geospatial)索引半径查询。
Redis是当 ,在web开发中,我们常用它来缓存数据库的查询结果。

redis常用命令

set key value
设置 key 的值

get key
获取 key 的值

exists key
查看此 key 是否存在

keys *
查看所有的 key

flushall
消除所有的 key

redis用途

将介绍如何使用SpringBoot快速搭建一个Web应用,并且采用Mybatis作为我们的ORM框架。为了提升性能,我们将Redis作为Mybatis的二级缓存。为了提升访问速度,一般会将经常查询且不会经常发生改变的数据存入缓存,然后从缓存中查询数据,提升查询速度。在大流量场景下,需要将一些经常展现和不会频繁变更的数据,存放在存取速率更快的地方,在技术选型中,常使用Redis作为缓存数据库。
应用场景:例如常见的电商场景,根据商品ID获取商品信息,这是就可以把店铺信息和商品详细信息写入redis,减少了去数据库查询的次数。
在这里插入图片描述

redis和mysql选择

redis和mysql都可以作为持久层,对数据进行保存,区别是:
​redis数据是key-value的数据结构,value可以是list,set,hash,sortedset和String,并且保存在内存中,速度非常快,提高性能的时候可以使用,但同时因为保存在内存,需要更多资源。目前redis主要是用在缓存,消息队列和session共享上。
​ mysql的数据是储存在磁盘上,虽然性能没有redis好,但是不需要更多的资金投入维护。
​ 因此mysql适合作为主要的数据库,redis作为辅助数据库,帮助mysql储存缓存信息,减轻后台对于数据库频繁操作的压力。

在java中如何使用Redis

用 Java 操作 Redis 的方案很多,常用的Redis Java客户端有:Jedis(是目前较为流行的一种方案)、Lettuce、Redison、Spring Data Redis。

Spring-data-redis是Spring-Data项目的一个子项目,Spring框架集成Redis操作的一个子框架,封装了Redis的很多命令,可以很方便的使用Spring操作Redis数据库,通过Spring-data-redis工具,使得操作Redis以更加面向对象的方式。

Spring-data-redis是spring的一部分,提供了在srping应用中通过简单的配置访问redis服务,对reids底层开发包(Jedis, JRedis, and RJC)进行了高度封装,RedisTemplate提供了redis各种操作、异常处理及序列化,支持发布订阅,并对spring 3.1 cache进行了实现。SpringCache并非某一种Cache实现的技术,SpringCache是一种缓存实现的通用技术,基于Spring提供的Cache框架,让开发者更容易将自己的缓存实现高效便捷的嵌入到自己的项目中。当然,SpringCache也提供了本身的简单实现NoOpCacheManager、ConcurrentMapCacheManager 等。通过SpringCache,可以快速嵌入自己的Cache实现。

在 Spring Boot 中,默认集成的 Redis 就是 Spring Data Redis,默认底层的连接池使用了 lettuce。

1、springboot中使用mybatis框架+spring data redis做缓存

1.新建项目:

在这里插入图片描述

2.在 Spring Boot 中,默认集成的 Redis 就是 Spring Data Redis,默认底层的连接池使用了 lettuce,所以在pom.xml中也必须集成连接池:commons-pool2

3.参考https://github.com/Fengxiangmei/springboot2先spring boot + mybatis + jsp构建成功,在此基础上增加spring data redis 代码

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.8.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.fxm. study</groupId>
    <artifactId>redis</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>redis</name>
    <description>Demo project for Spring Boot</description>

    <properties>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.0</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
        <version>2.4.2</version>
    </dependency>
   <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
    <!-- servlet依赖.为jsp添加以下依赖 -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
        <version>8.5.20</version>
    </dependency>
</dependencies>

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <!-- 此配置不可缺,否则mybatis的Mapper.xml将会丢失 -->
            <includes>
                <include>**/*.xml</include>
            </includes>
        </resource>
        <!--指定资源的位置-->
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.yml</include>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
                <include>**/*.*</include>
            </includes>
        </resource>
    </resources>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
</project>

4.在application.properties文件中简单配置一下Redis,Redis的基本信息,另外,这里要用到Cache,因此还需要稍微配置一下Cache:

	#静态资源访问路径
	spring.mvc.static-path-pattern=/**
	
	#静态资源映射路径
	spring.resources.static-locations=classpath:/META-INF/resources/,\
	  classpath:/resources/,classpath:/static/,\
	  classpath:/public/,\
	  classpath:/assets/
	
	#数据库配置
	spring.datasource.url=jdbc:mysql://127.0.0.1:3306/hr?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=false
	spring.datasource.driver-class-name=com.mysql.jdbc.Driver
	spring.datasource.username=root
	spring.datasource.password=123456
	
	##jsp
	spring.mvc.view.prefix=/WEB-INF/jsp/
	spring.mvc.view.suffix=.jsp
	spring.thymeleaf.cache = false
	spring.thymeleaf.enabled=false
	#slf4j
	
	#启用日志颜色
	spring.output.ansi.enabled=always
	#mapper接口所在的包设置为debug
	logging.level.cn.bdqn.personnels.personnel.Dao=DEBUG
	#设置日志的级别
	logging.level.root=INFO
	#在当前项目下生成日志文件
	logging.file=./logs/mylog.log
	logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %clr(%logger){cyan} %clr(%msg%n){green}
	logging.pattern.file=%d{yyyy-MM-dd HH:mm} [%thread] %-5level %msg%n
	
	##redis and cache
	spring.redis.host=localhost
	spring.redis.port=6379

5.在配置类上添加如下代码,表示开启缓存,Spring Boot就会自动帮我们在后台配置一个RedisCacheManager,相关的配置是在org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration类中完成的。

开启基于注解的缓存 @EnableCaching

package cn.fxm.study.redis;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@MapperScan("cn.fxm.study.redis.dao")
@EnableCaching //程序入口开启使用缓存@EnableCaching 等价于 <cache:annotation-driven/> 。能够在服务类方法上标注@Cacheable  
public class RedisApplication {


public static void main(String[] args) {
    SpringApplication.run(RedisApplication.class, args);
}
}

6.序列化实体化类 implements Serializable
package cn.fxm.study.redis.pojo;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.io.Serializable;

@Getter
@Setter
@ToString
public class Emp implements Serializable {
	private Integer empno;

	private String ename;

	private String job;

 private Integer deptno;
}

7.标注缓存注解即可,关于更多的缓存注解请看:https://www.cnblogs.com/yueshutong/p/9381540.html
@CacheConfig
    @Cachable
    @CachePut
    @CacheEvict
    
@EnableCaching: 程序入口开启使用缓存@EnableCaching 等价于 cache:annotation-driven/ 。能够在服务类方法上标注@Cacheable

@CacheConfig : 这个注解在类上使用,用来描述该类中所有方法使用的缓存名称,当然也可以不使用该注解,直接在具体的缓存注解上配置名称

@Cachable: 将方法的运行结果进行缓存;以后再要相同的数据,直接从缓存中获取,不用调用方法;默认情况下,缓存的key就是方法的参数,缓存的value就是方法的返回值。

@CachePut: 既调用方法,又更新缓存数据:同步更新缓存。修改了数据库的某个数据,同时更新缓存。没有指定key时,默认用传参做key,查询result做value。需要要指定和@Cacheable用相同的key,更新的key和查询的key达到一致。

@CacheEvict: 这个注解一般加在删除方法上,当数据库中的数据删除后,相关的缓存数据也要自动清除,该注解在使用的时候也可以配置按照某种条件删除(condition属性)或者或者配置清除所有缓存(allEntries属性)

package cn.fxm.study.redis.service;

import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import cn.fxm.study.redis.pojo.Emp;
import cn.fxm.study.redis.dao.EmpMapper;
import org.springframework.transaction.annotation.Transactional;

@Transactional
@Service
@CacheConfig(cacheNames = "my")   //使用 Redis 缓存时,没有指定缓存名,将会报错:no cache could be resolved for at least one cache should be provided per cache operation
public class EmpServiceImpl implements EmpService{

    @Resource
private EmpMapper empMapper;
/*删除方法*/
@Override
@CacheEvict(key = "#p0")
public int deleteByPrimaryKey(Integer empno) {
    return empMapper.deleteByPrimaryKey(empno);
}

@Override
public Emp insert(Emp record) {
     empMapper.insert(record);
   Emp emp =  empMapper.selectByPrimaryKey(record.getEmpno());
   return  emp;
}

@Override
public int insertSelective(Emp record) {
    return empMapper.insertSelective(record);
}
/*查询方法*/
@Override
@Cacheable(key="#empno",unless = "#result==null")
public Emp selectByPrimaryKey(Integer empno) {
    return empMapper.selectByPrimaryKey(empno);
}

/*更新方法*/
@Override
@CachePut(key = "#record.empno",unless = "#result==null")
public Emp updateByPrimaryKeySelective(Emp record) {
    empMapper.updateByPrimaryKeySelective(record);
    Emp emp =  empMapper.selectByPrimaryKey(record.getEmpno());
    return emp;
}

@Override
public int updateByPrimaryKey(Emp record) {
    return 0;
}

8.controller类内容

package cn.fxm.study.redis.controller;
import cn.fxm.study.redis.pojo.Emp;
import cn.fxm.study.redis.service.EmpService;
//import org.apache.log4j.Logger;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/test")
public class EmpController {
    @Autowired
    EmpService empService;

    private  static Logger logger = Logger.getLogger(EmpController.class);

@RequestMapping("/get.html")
public String index(Model model,Integer id){
    long start = System.currentTimeMillis();
    Emp e = empService.selectByPrimaryKey(id);
    model.addAttribute("hello",e.getEname());
    long end = System.currentTimeMillis();
    long time = end-start;
    logger.error("------------"+e.getEname()+ ",取出数据用时:" +time + "------------------------");
    return "index";
}

@RequestMapping("/update.html")
public String update(Model model,Integer id){
    long start = System.currentTimeMillis();
    Emp newEmp = new Emp();
    newEmp.setEmpno(id);
    newEmp.setJob("倒水的");
    int i  = empService.updateByPrimaryKey(newEmp);
    long end = System.currentTimeMillis();
    long time = end-start;
    if(i>0){
        logger.error("------------"+"更新数据成功,用时:" +time + "------------------------");
    }else{
        logger.error("------------------更新数据失败------------------------------");
    }
    return "index";
}
@RequestMapping("/delete.html")
public String delete(Model model,Integer id){
    long start = System.currentTimeMillis();
    int i =  empService.deleteByPrimaryKey(id);
    if(i>0){
        logger.error("-------------------删除数据成功------------------------");
    }else{
        logger.error("-------------------删除数据失败------------------------");
    }
    long end = System.currentTimeMillis();
    long time = end-start;
    return "index";
}

}

ps:当在rdm软件中查看数据时,发现缓存的数据乱码了,需要在项目中加一个config包,并在该包下添加类SpringCacheRedisConfig(和启动器并列)

package cn.fxm.study.redis;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
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.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

@Configuration
public class SpringCacheRedisConfig  extends CachingConfigurerSupport {
    private Duration timeToLive = Duration.ZERO;
    public void setTimeToLive(Duration timeToLive) {
        this.timeToLive = timeToLive;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
    RedisSerializer<String> redisSerializer = new StringRedisSerializer();
    Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

    //解决查询缓存转换异常的问题
    ObjectMapper om = new ObjectMapper();
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    jackson2JsonRedisSerializer.setObjectMapper(om);

    // 配置序列化(解决乱码的问题)
    RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(timeToLive)
            .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
            .disableCachingNullValues();

    RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
            .cacheDefaults(config)
            .build();
    return cacheManager;
}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用SpringBoot结合Redis实现缓存功能的步骤如下: 1. 在pom.xml文件中添加Redis依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` 2. 配置Redis连接信息,在application.properties文件中添加以下配置: ``` # Redis连接信息 spring.redis.host=<redis服务器IP> spring.redis.port=<redis服务器端口> spring.redis.password=<redis密码> ``` 3. 创建一个Redis配置类,用于将RedisTemplate注入到Spring容器中: ``` @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(lettuceConnectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); return redisTemplate; } } ``` 4. 编写Cacheable注解,用于对需要缓存的方法进行标注: ``` @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Cacheable { // 缓存key的前缀 String prefix() default ""; // 缓存时间,默认为30分钟 long expireTime() default 1800L; } ``` 5. 编写缓存切面,对被Cacheable注解标注的方法进行缓存: ``` @Aspect @Component public class CacheAspect { @Autowired private RedisTemplate<String, Object> redisTemplate; @Around("@annotation(com.example.demo.annotation.Cacheable)") public Object cache(ProceedingJoinPoint joinPoint) throws Throwable { // 获取方法参数 Object[] args = joinPoint.getArgs(); // 获取方法名 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); String methodName = method.getName(); // 获取注解信息 Cacheable cacheable = method.getAnnotation(Cacheable.class); String prefix = cacheable.prefix(); long expireTime = cacheable.expireTime(); // 构造缓存key StringBuilder keyBuilder = new StringBuilder(); keyBuilder.append(prefix); for (Object arg : args) { keyBuilder.append(":").append(arg); } String key = keyBuilder.toString(); // 从缓存中获取数据 Object value = redisTemplate.opsForValue().get(key); if (value != null) { return value; } // 缓存中不存在则调用方法,将返回值存入缓存 Object result = joinPoint.proceed(args); redisTemplate.opsForValue().set(key, result, expireTime, TimeUnit.SECONDS); return result; } } ``` 6. 在需要进行缓存的方法上加上Cacheable注解,即可实现缓存功能: ``` @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override @Cacheable(prefix = "user", expireTime = 3600L) public User getUserById(Integer id) { return userDao.getUserById(id); } } ``` 这样,在调用getUserById方法时,如果缓存中已经存在数据,则直接返回缓存中的数据;否则调用方法,将返回值存入缓存,并返回结果。这样可以有效地减少数据库的访问次数,提高系统的性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值