目录
1、简介
- 缓存就是数据交换的缓冲区(Cache),有一些需要经常被访问数据并且比较少更新的数据,就可以缓存起来。当用户查询数据,先从缓存中查找,如果找到了则直接返回,找不到则去数据库中查找。
- 缓存的本质就是减少数据库IO,减轻服务器压力,减少网络延迟,加快页面打开速度。
2、整合Redis实现注解缓存
2.1、Redis缓存测试环境搭建
1、创建一个 Spring Boot 项目
2、pom 依赖
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<!-- 版本按自己需要选择也可以 -->
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
3、创建测试数据库
- 创建 cache_test 数据库
- 创建测试表和测试数据
drop table sys_commodity; create table sys_commodity ( `commodity_id` int(11) not null auto_increment primary key, `commodity_name` varchar(50), `commodity_type` varchar(50), `commodity_price` varchar(50), `commodity_add_date_time` datetime ); # 创建测试数据 insert into sys_commodity(`commodity_name`, `commodity_type`, `commodity_price`, `commodity_add_date_time`) values ('鸭嘴帽', 'A', 29, now()), ('小米10手机', 'A', 3999, now()), ('苹果', 'A', 8, now()), ('鼠标', 'A', 199, now()), ('杯子', 'A', 9, now()), ('软甲呀', 'A', 999999999, now()) select * from sys_commodity;
4、编写商品URUD
Commodity
/**
* 商品实体模型(用于缓存测试)
*
* @author cms
* @version 1.0.0.0
* @Date: 2022/5/24 14:59
*/
@Data
@TableName(value = "sys_commodity")
public class Commodity implements Serializable
{
/** 商品编号 */
@TableId(value = "commodity_id", type = IdType.AUTO)
private Integer commodityId;
/** 商品名称 */
@NotBlank(message = "商品名不能为空!")
@TableField(value = "commodity_name")
private String commodityName;
/** 商品类型 */
@NotBlank(message = "商品类型不能为空!")
@TableField(value = "commodity_type")
private String commodityType;
/** 商品价格 */
@NotBlank(message = "商品价格不能为空!")
@TableField(value = "commodity_price")
private String commodityPrice;
/** 商品添加时间 */
@TableField(value = "commodity_add_date_time")
private Date commodityAddDateTime;
}
ICommodity
/**
* @author cms
* @version 1.0.0.0
* @Date: 2022/5/24 15:25
*/
@Repository
public interface ICommodityMapper extends BaseMapper<Commodity> //使用mybatis-plus快速完成CRUD
{
}
ICommodityService
/**
* @author cms
* @version 1.0.0.0
* @Date: 2022/5/24 15:26
*/
public interface ICommodityService extends IService<Commodity>
{
/**
* 添加商品
* @param commodity 商品实体
* @return 结果
*/
Boolean saveCommodity(Commodity commodity);
/**
* 删除商品
* @param commodity 商品实体
* @return 结果
*/
Boolean deleteCommodity(Commodity commodity);
/**
* 修改商品
* @param commodity 商品实体
* @return 结果
*/
Boolean updateCommodity(Commodity commodity);
/**
* 查询商品列表
* @return 结果
*/
List<Commodity> queryCommodityList();
/**
* 查询商品信息(按ID)
* @param commodityId 商品ID
* @return 结果
*/
Commodity queryByIdCommodity(Integer commodityId);
}
CommodityServiceImpl
/**
* @author cms
* @version 1.0.0.0
* @Date: 2022/5/24 15:29
*/
@Service
public class CommodityServiceImpl extends ServiceImpl<ICommodityMapper, Commodity> implements ICommodityService
{
@Autowired
private ICommodityMapper commodityMapper;
@Override
public Boolean saveCommodity(Commodity commodity)
{
commodity.setCommodityAddDateTime(new Date());
return commodityMapper.insert(commodity) > 0;
}
@Override
public Boolean deleteCommodity(Commodity commodity)
{
return commodityMapper.deleteById(commodity.getCommodityId()) > 0;
}
@Override
public Boolean updateCommodity(Commodity commodity)
{
return commodityMapper.updateById(commodity) > 0;
}
@Override
public List<Commodity> queryCommodityList() {
return commodityMapper.selectList(null);
}
@Override
public Commodity queryByIdCommodity(Integer commodityId)
{
return commodityMapper.selectById(commodityId);
}
}
5、编写 Controller
/**
* @author cms
* @version 1.0.0.0
* @Date: 2022/5/24 16:17
*/
@RestController
@RequestMapping(value = "/commodity")
public class CommodityController
{
@Autowired
private ICommodityService commodityService;
/**
* 添加商品
* @param commodity 商品实体
* @return 添加结果
*/
@PostMapping
public Object addCommodity(@RequestBody Commodity commodity)
{
return commodityService.saveCommodity(commodity) ? "添加商品成功" : "添加商品失败";
}
/**
* 删除商品
* @param commodity 商品实体
* @return 删除结果
*/
@DeleteMapping
public Object deleteCommodity(@RequestBody Commodity commodity)
{
return commodityService.deleteCommodity(commodity) ? "删除商品成功" : "删除商品失败";
}
/**
* 更新商品信息
* @param commodity 商品实体
* @return 更新结果
*/
@PutMapping
public Object updateCommodity(@RequestBody Commodity commodity)
{
return commodityService.updateCommodity(commodity) ? "更新商品成功" : "更新商品失败";
}
/**
* 查询商品列表
* @return 商品列表
*/
@GetMapping(value = "/list")
public Object listCommodity()
{
return commodityService.queryCommodityList();
}
/**
* 查询单个商品
* @param commodityId 商品编号
* @return 商品信息
*/
@GetMapping(value = "/query/{commodityId}")
public Object getCommodityOne(@PathVariable("commodityId") Integer commodityId)
{
return commodityService.queryByIdCommodity(commodityId);
}
}
6、测试环境是否有问题
环境测试成功 ~ 其他接口自行测试!
2.2、引入Redis缓存依赖
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.3、yml配置redis连接
server:
port: 8080
# 应用名称
spring:
application:
name: springboot-day05-cache
# mysql
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/cache_test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8
username: root
password:
# redis配置
redis:
# 地址
host: localhost
# 端口号
port: 6379
# 密码没有不填
password:
# 数据源默认0
database: 0
# mybatis plus基本配置
mybatis-plus:
# 配置实体别名
type-aliases-package: com.ruanjia.entity
# 配置mapper映射位置
mapper-locations: classpath*:mappers/**/*Mapper.xml
# 日志配置
logging:
level:
com.ruanjia: debug
2.4、Redis缓存序列化以及自定义缓存管理器配置
/**
* Redis 配置
*
* @author cms
* @version 1.0.0.0
* @Date: 2022/5/24 18:19
*/
@Configuration
public class RedisConfig extends CachingConfigurerSupport
{
/**
* 自定义缓存key生成策略:方法名:参数列表
* @return
*/
/*@Override
public KeyGenerator keyGenerator()
{
return new KeyGenerator()
{
@Override
public Object generate(Object target, Method method, Object... params)
{
return method.getName() + ":" + Arrays.asList(params);//例如:query:123
}
};
}*/
/**
* Redis缓存模板
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<Object,Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
/* new StringRedisSerializer() */
redisTemplate.setKeySerializer(stringRedisSerializer());
redisTemplate.setHashKeySerializer(stringRedisSerializer());
/* new Jackson2JsonRedisSerializer<>() */
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer());
return redisTemplate;
}
/* key序列化 */
private RedisSerializer<String> stringRedisSerializer()
{
return new StringRedisSerializer();
}
/* value序列化 */
private RedisSerializer<Object> jackson2JsonRedisSerializer()
{
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(mapper);
return jackson2JsonRedisSerializer;
}
/**
* 使用自定义缓存管理器
* @param connectionFactory
* @return
*/
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory)
{
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofDays(1))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer()))
.disableCachingNullValues();
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.transactionAware()
.build();
}
}
2.5、开启注解缓存
开启方法:直接在Spring Boot启动类添加 @EnableCaching 开启注解缓存
@MapperScan("com.ruanjia.mapper")
@EnableCaching //开启注解缓存
@SpringBootApplication
public class SpringbootDay05CacheApplication
{
public static void main(String[] args)
{
SpringApplication.run(SpringbootDay05CacheApplication.class, args);
}
}
2.6、Spring 缓存管理器
- 缓存管理器(CacheManager),管理着多个《缓存组件》
- 每个缓存组件都是唯一,拥有自己唯一的名字
2.7、Spring提供的几个重要缓存注解
- @EnableCaching 开启注解缓存
- @Caacheable 根据方法请求参数经行结果缓存
- @CacheEvict 清空缓存
- @CachePut 保证方法被调用,又希望被缓存(更新缓存)
2.8、@EnableCaching
作用:开启注解形式缓存
@MapperScan("com.ruanjia.mapper")
@EnableCaching //开启注解缓存
@SpringBootApplication
public class SpringbootDay05CacheApplication
{
public static void main(String[] args)
{
SpringApplication.run(SpringbootDay05CacheApplication.class, args);
}
}
2.9、@Caacheable
作用:根据方法请求参数进行方法结果缓存(可以指定缓存条件)
/**
* @Cacheable 符合条件就缓存
* 注解属性作用:
* 1、value/cacheName:缓存组件名(可以指定多个缓存)
* 2、key:缓存数据的key(默认使用方法参数的值)
* 3、常用的缓存SpEL(使用 # 取值)
* 获取当前调用方法名:#root.methodName
* 获取当前被调用的方法:#root.method.name
* 获取当前被调用目标对象:#root.target
* 获取当前被调用目标对象类:#root.targetClass
* 获取被调用方法参数列表:#root.args[0]
* 获取方法参数的值(可以直接#参数名):#commodityId
* 获取方法执行后的返回值:#result
* 4、keyGenerator:指定key生成策略(key/keyGenerator: 只能选择其中一个生效)
* 5、cacheManager:指定缓存管理器
* 6、condition:符合条件才经行缓存
* 7、unless:unless为true就不缓存这个方法
*
*
* @param commodityId 商品ID
* @return 结果
*/
@Cacheable(cacheNames = {"commodity"})
@Override
public Commodity queryByIdCommodity(Integer commodityId)
{
return commodityMapper.selectById(commodityId);
}
2.10、@CacheEvict
作用:根据缓存的key清除缓存,亦可以清楚指定缓存组件下所有缓存
/**
* @CacheEvict:清除缓存
* allEntries = true:清除指定缓存组件下所有缓存
* beforeInvocation = true:方法调用前清除缓存
* cacheNames = {"commodity"}, key = "#commodity.getCommodityId()":更具指定key清除缓存
*
* @param commodity 商品实体
* @return
*/
//@CacheEvict(cacheNames = {"commodity"}, allEntries = true, beforeInvocation = true) //清楚指定缓存组件下所有缓存
@CacheEvict(cacheNames = {"commodity"}, key = "#commodity.getCommodityId()") //根据指定缓存的key清楚缓存
@Override
public Boolean deleteCommodity(Commodity commodity)
{
return commodityMapper.deleteById(commodity.getCommodityId()) > 0;
}
2.11、@CachePut
作用:调用方法,更新指定缓存的数据
注意:如果没有找到指定缓存就会把方法结果缓存为新的缓存
/**
* @CachePut 保证方法被调用,同时根据缓存中的key更新缓存数据
* 注意:如果缓存中没有找到对应的key就会缓存一条新的数据
*
* @param commodity 商品实体
* @return
*/
@CachePut(cacheNames = {"commodity"}, key = "#commodity.getCommodityId()") // 根据缓存中的key更新缓存,例如:commodity::1
@Override
public Commodity updateCommodity(Commodity commodity)
{
if (commodityMapper.updateById(commodity) > 0)
{
return commodityMapper.selectById(commodity.getCommodityId());
}
throw new RuanjiaExceptionHandler(ErrorEnum.S_CATCH_AUTH, "商品更新失败!");
}
摆烂!不写了 ~