Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
docker pull redis
docker run -d -p 6379:6379 --name myredis redis
redis的端口号为6379,
如果之前启动过redis,可以通过docker start id进行启动
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
spring: datasource: # 配置监控服务器 type: com.alibaba.druid.pool.DruidDataSource druid: # druid 数据源其他配置 当添加了如下配置,我们需要在程序中增加configuration配置来将这些配置映射到系统默认配置参数上去。 driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root url: jdbc:mysql://192.168.100.158:3306/spring_cache?characterEncoding=utf-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useSSL=false initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,‘wall‘用于防火墙 filter: config: enabled: true stat: enabled: true db-type: mysql wall: enabled: true db-type: mysql maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 stat-view-servlet: login-username: admin login-password: admin reset-enable: false url-pattern: /druid/* web-stat-filter: url-pattern: /* exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" redis: host: 192.168.100.158 mybatis: configuration: # 开启驼峰命名法 map-underscore-to-camel-case: true logging: level: # 设置包下日志输出级别 com.seegot.mapper: debug debug: true
/* Navicat MySQL Data Transfer ? Source Server : vm Source Server Version : 50729 Source Host : 192.168.100.158:3306 Source Database : spring_cache ? Target Server Type : MYSQL Target Server Version : 50729 File Encoding : 65001 ? Date: 2020-05-18 13:48:39 */ ? SET FOREIGN_KEY_CHECKS=0; ? -- ---------------------------- -- Table structure for department -- ---------------------------- DROP TABLE IF EXISTS `department`; CREATE TABLE `department` ( `id` int(11) NOT NULL AUTO_INCREMENT, `departmentName` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; ? -- ---------------------------- -- Records of department -- ---------------------------- INSERT INTO `department` VALUES (‘1‘, ‘研发部‘); INSERT INTO `department` VALUES (‘2‘, ‘测试部‘); INSERT INTO `department` VALUES (‘3‘, ‘财务部‘); ? -- ---------------------------- -- Table structure for employee -- ---------------------------- DROP TABLE IF EXISTS `employee`; CREATE TABLE `employee` ( `id` int(11) NOT NULL AUTO_INCREMENT, `lastName` varchar(255) DEFAULT NULL, `email` varchar(255) DEFAULT NULL, `gender` int(2) DEFAULT NULL, `d_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; ? -- ---------------------------- -- Records of employee -- ---------------------------- INSERT INTO `employee` VALUES (‘1‘, ‘Tom‘, ‘1‘, ‘0‘, ‘1‘); INSERT INTO `employee` VALUES (‘2‘, ‘Jerry‘, ‘Jerry@seegot.com‘, ‘2‘, ‘2‘); ?
Department部门 和 Employee员工
package com.seegot.bean; ? import lombok.Data; ? /** * @program: spring-boot-01-cache * @description: * @author: PP Zhang * @create: 2020-04-27 10:46 */ @Data public class Department { private Integer id; private String departmentName; } package com.seegot.bean; ? import lombok.Data; ? import java.io.Serializable; ? /** * @program: spring-boot-01-cache * @description: * @author: PP Zhang * @create: 2020-04-27 10:47 */ @Data public class Employee { private Integer id; private String lastName; private String email; private Integer gender; private Integer dId; }
package com.seegot.mapper; ? import com.seegot.bean.Department; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; ? /** * @program: spring-boot-01-cache * @description: * @author: PP Zhang * @create: 2020-04-27 11:06 */ @Mapper public interface DepartmentMapper { @Select("select * from department where id = #{id}") Department getDeptById(Integer id); } package com.seegot.mapper; ? import com.seegot.bean.Employee; import org.apache.ibatis.annotations.*; ? /** * @program: spring-boot-01-cache * @description: * @author: PP Zhang * @create: 2020-04-27 11:06 */ @Mapper public interface EmployeeMapper { @Select("select * from employee where id=#{id}") public Employee getEmpById(Integer id); @Update("update employee set lastName=#{lastName},email=#{email},gender=#{gender},d_id=#{dId} where id=#{id}") public void updateEmp(Employee emp); @Delete("delete from employee where id=#{id}") public void deleteEmpById(Integer id); @Insert("insert into employee(lastName,email,gender,d_id) values (#{lastName}),#{email},#{gender},#{dId}") public void insertEmp(Employee emp); @Select("select * from employee where lastName = #{name}") public Employee getEmpName(String name); }
package com.seegot.service; ? import com.seegot.bean.Department; import com.seegot.mapper.DepartmentMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; ? /** * @program: spring-boot-01-cache * @description: * @author: PP Zhang * @create: 2020-05-18 10:37 */ @Service public class DeptService { @Autowired DepartmentMapper departmentMapper; @Cacheable(cacheNames = "dept") public Department getDeptById(Integer id){ System.out.println("查询部门:"+id); Department dept = departmentMapper.getDeptById(id); return dept; } } package com.seegot.service; ? import com.seegot.bean.Employee; import com.seegot.mapper.EmployeeMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.*; import org.springframework.stereotype.Service; ? /** * @program: spring-boot-01-cache * @description: * @author: PP Zhang * @create: 2020-04-27 11:15 */ @CacheConfig(cacheNames = "emp") // 抽取公共的缓存部分, @Service public class EmployeeService { @Autowired EmployeeMapper employeeMapper; /** * @Description: * 将方法运行的结果进行缓存,以后再要相同的数据,直接从缓存中获取,不用调用方法去数据库查询。 * CacheManager管理多个Cache组件的,对缓存真正的CRUD操作在Cache组件中,每个缓存组件有自己唯一的名字; * @Cacheable几个属性:(key-value) * cacheNames/values:指定缓存组件的名字。讲方法的反馈结果放在哪个缓存中,而且可以使数组的方式,可以指定多个缓存 * key:缓存数据时用的key;可以用它来指定,如果不指定Key默认是使用方法参数的值; * 编写SpEL: * #id 是参数id的值。 * keyGenerator:key的生成器,可以自定义key的生成器组件id * 指定了Key 就不要再指定keyGenerator 二选一 * cacheManager:指定缓存管理器;或者cacheResolver指定获取解析器 * condition:指定符合条件的情况下才会缓存。@Cacheable(cacheNames = "emp",condition = "#id>0") * unless:否定缓存,当unless指定的条件为true,方法返回值就不被缓存。可以获取到结果进行判断。unless = "#result==null * sync:是否开启异步模式 * * 原理: * 1.自动配置类:CacheAutoConfiguration * 2.缓存配置类 * * 3.哪些配置类生效? * SimpleCacheConfiguration * * 运行流程: * @Cacheable * 1.方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取; * (CacheManager先获取相应的缓存),第一次获取缓存,如果没有Cache组件会自动创建, * 2.去Cache中查找缓存的内容,按使用一个Key,默认就是方法的参数; * key是按照某种策略生成的。默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成。 * SimpleKeyGenerator生成Key的默认策略: * 如果没有参数;key=new SimpleKey(); * 如果有一个参数:key=参数的值() * 如果有多个参数:key=new SimpleKey(params); * 3.没有查到缓存就调用目标方法; * 4.将目标方法返回的结果,放进缓存中 * * @Cacheable标注的方法执行之前下来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存, * 如果没有就运行方法并将结果放到缓存;以后在来调用,就可以直接使用缓存中的数据; * * * 核心: * 1)使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurrentMapCache】组件 * 2)key使用keyGenerator生成的,默认是SimpleKeyGenerator生成的。 * @Param: * @return: * @Author: PP Zhang * @Date: 2020/4/27 */ // @Cacheable(value="emp",keyGenerator=myKeyGenerator) // @Cacheable(cacheNames = "emp",key = "#root.methodName+‘[‘+#id+‘]‘",condition = "#a0>1 and #root.nethodName eq[‘aaa‘]") // @Cacheable(cacheNames = "emp",key = "#id") 当不在类上进行@CacheConfig()的时候,用它 @Cacheable(/*cacheNames = "emp",*/key = "#id") public Employee getEmp(Integer id){ System.out.println("查询"+id+"号的员工信息"); Employee employee = employeeMapper.getEmpById(id); return employee; } /** * @CachePut:即调用方法,又更新缓存数据; * 修改了数据库的某个数据,同时更新缓存; * 运行时机: * 1.先调用目标方法 * 2.将目标方法结果缓存起来 * * 测试步骤: * 1.查询1号员工;查到的结果会放到缓存中。 * key:1 value:lastName:Lilei * 2.以后查询还是之前的结果。 * 3.更新1号员工信息。【lastName:zhangsan;gender=1】 * 使用 @CachePut(value = "emp") 其实也将该条结果存入了缓存, * 由于没有指定缓存名称,所以默认是参数也就是 employee * key:employee value:返回的employee对象。 * @Cacheable是不可能用#result的。 * 4.查询1号员工 * 应该是更新的员工 * key = "#employee.id" 使用传入的参数员工id; * key="#result.id" 是返回后的Id * * 为什么是没有更新前的? * */ @CachePut(/*value = "emp",*/key = "#result.id") public Employee updateEmp(Employee employee){ System.out.println("updateEmp"+employee); employeeMapper.updateEmp(employee); return employee; } /** * @CacheEvict:清除缓存 * key 指定要清除的Key * * allEntries = true 删除指定缓存中的所有数据 * * beforeInvocation = false 缓存的清除是否在方法之前执行? * 默认缓存清除是在方法执行之后执行的,如果方法出现异常,缓存就不会被清除。 * * beforeInvocation = true * 代表清除缓存操作是在方法运行之前执行的,无论方法是否出现异常,缓存都被清除。 * */ @CacheEvict(/*value = "emp",*//*key = "#id",*//*allEntries = true*/beforeInvocation = true) public void deleteEmp(Integer id){ System.out.println("deleteEmp"+id); // employeeMapper.deleteEmpById(id); int i = 10/0; } ? // 定义复杂的注解 @Caching( cacheable = { @Cacheable(/*value="emp",*/key = "#name") }, put = { @CachePut(/*value="emp",*/key = "#result.id"), @CachePut(/*value = "emp",*/key = "#result.email") } ) public Employee getEmpByName(String name){ Employee employee = employeeMapper.getEmpName(name); return employee; } }
package com.seegot.config; ? import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import org.springframework.cache.CacheManager; 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.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; ? /** * @program: spring-boot-01-cache * @description: * @author: PP Zhang * @create: 2020-05-15 13:56 */ @Configuration public class MyRedisConfig { ? @Bean public RedisTemplate<String,Object>redisTemplate(RedisConnectionFactory redisConnectionFactory){ RedisTemplate<String,Object>template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(valueSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(valueSerializer()); return template; } @Bean public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory){ RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory); RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer())); redisCacheConfiguration.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())); return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration); } private RedisSerializer<Object> valueSerializer() { Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<Object>(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); //因为上面那句代码已经被标记成作废,因此用下面这个方法代替,仅仅测试了一下,不知道是否完全正确 objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL); serializer.setObjectMapper(objectMapper); return serializer; } }
原文:https://www.cnblogs.com/pengpengzhang/p/12910180.html