今天,我们继续讲SpringBoot整合Redis ,也就缓存,它将与我们的Springboot整合

Redis 简介

Redis 是当前比较热门的NOSQL系统之一,它是一个开源的使用ANSI c语言编写的

key-value 存储系统(区别于MySQL的二维表格的形式存储。)。

Memcache类似,但很大程度补偿了Memcache的不足。和Memcache一样,Redis数据都是缓存在计算机内存中,不同的是,Memcache只能将数据缓存到内存中,无法自动定期写入硬盘,这就表示,一断电或重启,内存清空,数据丢失。所以Memcache的应用场景适用于缓存无需持久化的数据。而Redis不同的是它会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,实现数据的持久化

特点

  • Redis读取的速度是110000次/s,写的速度是81000次/s

    原子 。
  • Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。

    支持多种数据结构:string(字符串);list(列表);hash(哈希),set(集合);zset(有序集合)
  • 持久化,主从复制(集群)
  • 支持过期时间,支持事务,消息订阅。
  • 官方不支持window,但是又第三方版本。
好了,现在知道他是干什么来的 ,我开动吧,相信你也等急了

准备工作

本文使用 Linux 版本的 redis ,当然你也可以使用 Windows ,Windows的话,只需要点击



当然我们要复杂一点,不然写博客太没意思了,就以Linux版本为例

  • 1.Linux系统

  • 2.安装redis(也可以安装docker,然后再docker中装redis,本文章就直接用Linux安装redis做演示)

redis下载地址:http://download.redis.io/releases/redis-4.0.14.tar.gz

修改Redis,开启远程访问

默认Redis是不支持远程访问的 ,我们需要手动开启

找到redis中的redis.conf文件并编辑(在安装路径中找到)

vim ./redis.conf

找到bind 127.0.0.1并注释掉

  1. 默认127.0.0.1只能本地访问,注释掉即可ip访问

  2. 修改 protected-mode 属性值为no,注释掉并把保护模式禁用以后可以IP访问

  3. 修改daemonize属性将no 改为yes,将daemonize设置为yes即启动后台运行

  4. 开放6379端口

/sbin/iptables -I INPUT -p tcp --dport 6379 -j ACCEPT
  1. 最后一步,开启Redis
redis-server /myconf/redis.conf
  1. 测试连接 redis-server默认在/usr/local/bin路径下,redis.conf在redis的安装路径下
redis-cli -h 192.168.126.129 -p 6379

redis-cli -h redis服务器IP -p 6379 -a 密码(没有设置redis密码不要写空,否则报错)

Quick Start

第一步 : 加依赖

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <!-- mybatis 与 spring boot 2.x的整合包 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency> <!--mysql JDBC驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.39</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
</dependencies>

application.yml

下面是springboot的配置文件application.yml,配置redis(里面都有注释解释)

server:
port: 8081 #数据库连接
spring:
datasource:
url: jdbc:mysql://localhost:3306/mytest_springboot_cache?useUnicode=true
driver-class-name: com.mysql.jdbc.Driver
username: root
password: root ## Redis 配置
redis:
## Redis数据库索引(默认为0)
database: 0
## Redis服务器地址
host: 192.168.126.129
## Redis服务器连接端口
port: 6379
## Redis服务器连接密码(默认为空)
password:
jedis:
pool:
## 连接池最大连接数(使用负值表示没有限制)
#spring.redis.pool.max-active=8
max-active: 8
## 连接池最大阻塞等待时间(使用负值表示没有限制)
#spring.redis.pool.max-wait=-1
max-wait: -1
## 连接池中的最大空闲连接
#spring.redis.pool.max-idle=8
max-idle: 8
## 连接池中的最小空闲连接
#spring.redis.pool.min-idle=0
min-idle: 0
## 连接超时时间(毫秒)
timeout: 1200 #将themilef的默认缓存禁用,热加载生效
thymeleaf:
cache: false #mybatis的下划线转驼峰配置
configuration:
map-underscore-to-camel-case: true #另外一种打印语句的方式
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql时的语句
logging:
level:
com:
acong:
dao: debug
file: d:/logs/bsbdj.log

接着是实体类,这个比较简单就不多说了

package com.spiritmark.springbootstudytest.bean;

import java.io.Serializable;

/**
* @author spiritmark
* create 2019-09-18-22:32
*/
public class User implements Serializable { private static final long serialVersionUID = 1L;
private int uid;
private String userName;
private String passWord;
private int salary;
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
public User(int uid, String userName, String passWord, int salary) {
super();
this.uid = uid;
this.userName = userName;
this.passWord = passWord;
this.salary = salary;
}
public User() {
super();
}
} 
Controller
package com.spiritmark.springbootstudytest.controller;

import com.spiritmark.springbootstudytest.bean.User;
import com.spiritmark.springbootstudytest.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController; import java.util.HashMap;
import java.util.List;
import java.util.Map; /**
* @author spiritmark
* create 2019-09-18-22:36
*/
@RestController
public class TestController { @Autowired
private UserService userService; @RequestMapping("/queryAll")
public List<User> queryAll(){
List<User> lists = userService.queryAll();
return lists;
} @RequestMapping("/findUserById")
public Map<String, Object> findUserById(@RequestParam int id){
User user = userService.findUserById(id);
Map<String, Object> result = new HashMap<>();
result.put("uid", user.getUid());
result.put("uname", user.getUserName());
result.put("pass", user.getPassWord());
result.put("salary", user.getSalary());
return result;
} @RequestMapping("/updateUser")
public String updateUser(){
User user = new User();
user.setUid(1);
user.setUserName("cat");
user.setPassWord("miaomiao");
user.setSalary(4000); int result = userService.updateUser(user); if(result != 0){
return "update user success";
} return "fail";
} @RequestMapping("/deleteUserById")
public String deleteUserById(@RequestParam int id){
int result = userService.deleteUserById(id);
if(result != 0){
return "delete success";
}
return "delete fail";
}
}

之前都是学过的内容,下面是新增的 也就是Redis配置redistemplate序列化

package com.spiritmark.springbootstudytest.config;

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.cache.annotation.EnableCaching;
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.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; /**
* @author spiritmark
* create 2019-09-24-15:07
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport { /**
* 选择redis作为默认缓存工具
* @param redisConnectionFactory
* @return
*/
/*@Bean
//springboot 1.xx
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
return rcm;
}*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1)); // 设置缓存有效期一小时
return RedisCacheManager
.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
.cacheDefaults(redisCacheConfiguration).build();
} /**
* retemplate相关配置
* @param factory
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>();
// 配置连接工厂
template.setConnectionFactory(factory); //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jacksonSeial.setObjectMapper(om); // 值采用json序列化
template.setValueSerializer(jacksonSeial);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer()); // 设置hash key 和value序列化模式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(jacksonSeial);
template.afterPropertiesSet(); return template;
} /**
* 对hash类型的数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForHash();
} /**
* 对redis字符串类型数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForValue();
} /**
* 对链表类型的数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForList();
} /**
* 对无序集合类型的数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForSet();
} /**
* 对有序集合类型的数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForZSet();
}
}
数据访问层

接着是Mapper持久层Dao,这里主要用注解写比较方便,也可以使用mybatis的xml配置文件写sql语句

package com.spiritmark.springbootstudytest.mapper;

import com.spiritmark.springbootstudytest.bean.User;
import org.apache.ibatis.annotations.*; import java.util.List; /**
* @author SpiritMark
* create 2019-09-18-22:32
*/
@Mapper
public interface UserDao { @Select("select * from user")
List<User> queryAll(); @Select("select * from user where uid = #{id}")
User findUserById(int id); @Update("UPDATE USER SET username = CASE WHEN (#{userName} != NULL) AND (#{userName} != '') THEN #{userName},PASSWORD = CASE WHEN (#{passWord} != NULL) AND (#{passWord} != '') THEN #{passWord},salary = CASE WHEN (#{salary} != 0) THEN #{salary} WHERE uid = #{uid}")
int updateUser(@Param("user") User user); @Delete("delete from user where uid = #{id}")
int deleteUserById(int id); }

service层,这里主要是使用redis模板来写

业务逻辑层,就是重点讲 RedisTemplate

package com.spiritmark.springbootstudytest.service;

import com.spiritmark.springbootstudytest.bean.User;
import com.spiritmark.springbootstudytest.mapper.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service; import java.util.List;
import java.util.concurrent.TimeUnit; /**
* @author spiritmark
* create 2019-09-18-22:33
*/
@Service
public class UserService { @Autowired
private UserDao userDao; @Autowired
private RedisTemplate redisTemplate; public List<User> queryAll() {
return userDao.queryAll();
} /**
* 获取用户策略:先从缓存中获取用户,没有则取数据表中 数据,再将数据写入缓存
*/
public User findUserById(int id) {
String key = "user_" + id; ValueOperations<String, User> operations = redisTemplate.opsForValue(); //判断redis中是否有键为key的缓存
boolean hasKey = redisTemplate.hasKey(key); if (hasKey) {
User user = operations.get(key);
System.out.println("从缓存中获得数据:"+user.getUserName());
System.out.println("------------------------------------");
return user;
} else {
User user = userDao.findUserById(id);
System.out.println("查询数据库获得数据:"+user.getUserName());
System.out.println("------------------------------------"); // 写入缓存
operations.set(key, user, 5, TimeUnit.HOURS);
return user;
}
} /**
* 更新用户策略:先更新数据表,成功之后,删除原来的缓存,再更新缓存
*/
public int updateUser(User user) {
ValueOperations<String, User> operations = redisTemplate.opsForValue();
int result = userDao.updateUser(user);
if (result != 0) {
String key = "user_" + user.getUid();
boolean haskey = redisTemplate.hasKey(key);
if (haskey) {
redisTemplate.delete(key);
System.out.println("删除缓存中的key-----------> " + key);
}
// 再将更新后的数据加入缓存
User userNew = userDao.findUserById(user.getUid());
if (userNew != null) {
operations.set(key, userNew, 3, TimeUnit.HOURS);
}
}
return result;
} /**
* 删除用户策略:删除数据表中数据,然后删除缓存
*/
public int deleteUserById(int id) {
int result = userDao.deleteUserById(id);
String key = "user_" + id;
if (result != 0) {
boolean hasKey = redisTemplate.hasKey(key);
if (hasKey) {
redisTemplate.delete(key);
System.out.println("删除了缓存中的key:" + key);
}
}
return result;
} } 

这里主要是使用RedisTemplate来对远程redis操作,每次访问controller暴露的接口,

  • 首先判断redis缓存中是否存在该数据
  • 若不存在就从数据库中读取数据,然后保存到redis缓存中
  • 当下次访问的时候,就直接从缓存中取出来。这样就不用每次都执行sql语句,能够提高访问速度。
  • 但是在保存数据到缓存中,通过设置键和值和超时删除,注意设置超时删除缓存时间不要太长,否则会给服务器带来压力。

执行spring boot的启动类,访问 http://localhost:8081/findUserById?id=1

再次访问 http://localhost:8081/findUserById?id=1就是从缓存中获取保存的数据

好了今天就写到这里,如果对您有帮助记得点个关注哦 !

SpringBoot从入门到精通教程(七)的更多相关文章

  1. SpringBoot从入门到精通教程(二)

    SpringBoot 是为了简化 Spring 应用的创建.运行.调试.部署等一系列问题而诞生的产物,自动装配的特性让我们可以更好的关注业务本身而不是外部的XML配置,我们只需遵循规范,引入相关的依赖 ...

  2. SpringBoot从入门到精通教程(六)

    之前学了,这么多东西 thyemeaf .MyBatis 还有 配置文件等等,今天我们就来做一个小案例 CRUD,程序员的必备 项目结构 pom.xml <!-- mybatis 相关依赖 -- ...

  3. SpringBoot从入门到精通教程(三)

    在上一篇中,我们已经讲了,SpringBoot 如何构建项目,和SpringBoot的HelloWorld, 那这一节我们继续讲 Thymeleaf Thymeleaf 官网: Thymeleaf T ...

  4. SpringBoot从入门到精通教程(一)

    写在前面的话: 在很早之前,记笔记时候,我就一直在思考一个问题,我记笔记是为了什么,我一直想不明白 ,后面发现技术跟新迭代的速度实在太快了,笔记刚纪完,技术又跟新了,于是我想了想干脆边写博客,边记笔记 ...

  5. SpringBoot从入门到精通教程(八)

    本主要介绍ElasticSearch 和 SpringBoot 的整合 ,对您有帮助的话,点个关注哦 ElastSearch 介绍 ElasticSearch是一个基于Lucene的搜索服务器.它提供 ...

  6. SpringBoot从入门到精通教程(五)

    上节,我们讲了 SpringBoot 如何使用MyBatis 今天我们讲讲 Springboot Logo自定义的问题, 我们在启动 SpringBoot 时,控制台会打印 SpringBoot Lo ...

  7. SpringBoot从入门到精通教程(四)

    前端时间整合SSM ,发现了一个现象,在整合的时候 配置文件过于复杂. 1.建工程,建目录,导入jar包. 2.配置 数据源 映射信息 等等 ... 3. 还有 各种 拦截器,控制器 ,头都大了... ...

  8. 深入浅出!springboot从入门到精通,实战开发全套教程!

    前言 之前一直有粉丝想让我出一套springboot实战开发的教程,我这边总结了很久资料和经验,在最近总算把这套教程的大纲和内容初步总结完毕了,这份教程从springboot的入门到精通全部涵盖在内, ...

  9. Spring Boot从入门到精通(七)集成Redis实现Session共享

    单点登录(SSO)是指在多个应用系统中,登录用户只需要登录验证一次就可以访问所有相互信任的应用系统,Redis Session共享是实现单点登录的一种方式.本文是通过Spring Boot框架集成Re ...

随机推荐

  1. 用Camtasia设计微课视频封面,让课程更加高大上

    在网络时代,尤其现在疫情影响只能线上学习,微课的应用前景已经越来越广了.但是想把微课做好,只有内容与录制精细是不够的,还需要一个精美的封面来吸引学生.接下来,小编就用微课制作软件Camtasia 20 ...

  2. 网络拓扑实例之交换机基于接口地址池作为DHCP服务器(六)

    组网图形 DHCP服务器简介 通常用户希望网络中的每台终端能够动态获取IP地址.DNS服务器的IP地址.路由信息.网关信息等网络参数,不需要手动配置终端的IP地址等网络参数:另外,针对一些移动终端(手 ...

  3. day102:MoFang:后端完成对短信验证码的校验&基于celery完成异步短信发送&flask_jwt_extended&用户登录的API接口

    目录 1.用户注册 1.后端完成对短信验证码的校验 2.基于celery实现短信异步发送 2.用户登录 1.jwt登录验证:flask_jwt_extended 2.服务端提供用户登录的API接口 1 ...

  4. moviepy音视频剪辑:mask clip遮罩剪辑、遮片、蒙版的作用以及其包含的构成内容

    ☞ ░ 前往老猿Python博文目录 ░ 在阅读moviepy的类ImageClip的构造方法代码时,对于其中涉及遮罩的处理没有理解,到处查找遮罩的资料没有查到,最后到moviepy的官网上尝试了一下 ...

  5. PyQt专题结题感言

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 今天写完PyQt专题显示部件的最后一篇文章,长长的 ...

  6. PyQt(Python+Qt)学习随笔:QScrollArea的widgetResizable属性

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 滚动区域的widgetResizable属性用于控制滚动区域的内容部署层是否应跟随滚动区域的大小变化 ...

  7. PyQt(Python+Qt)学习随笔:QTableWidget表格部件中行高和列宽的计算方式

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 QTableWidget表格部件中行高和列宽的计算在Qt提供的资料中内容介绍比较泛,细节说得不清楚, ...

  8. Python中str类型的字符串写入二进制文件时报TypeError错的处理方式

    在用二进制模式打开文件情况下,写入一个str对象时报错:TypeError: a bytes-like object is required, not 'str' 出现该问题是因为Python严格区分 ...

  9. Thread interrupt() 线程中断的详细说明

    GitHub源码地址 原创声明:作者:Arnold.zhao 博客园地址:https://www.cnblogs.com/zh94 一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止 ...

  10. 使用T4模板动态生成NPoco实体类

    这是一个妥妥的NPoco类,这是我们在工作开发中,手动去写这个实体类,属实非常心累,字段少无所谓一次两次,数量多了,字段多了,就心态裂开