第一步:创建SpringBoot项目
maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
有的文章说需要再加入依赖spring-boot-starter-cache
,实际测试spring-boot-starter-web中已经包含了spring cache相关的依赖。
application.yml
spring:
redis:
database: 0
host: 127.0.0.1
port: 6379
# password: 1234
timeout: 10000ms # 超时时间
lettuce:
pool:
max-idle: 8 # 最大空闲连接数,默认值为8
max-wait: -1ms # 最大连接阻塞等待时间,默认值-1
min-idle: 2 # 最小空闲连接数
max-active: 20 #最大连接数
cache:
type: redis #缓存类型
redis:
cache-null-values: false #不缓存null数据
time-to-live: 50000ms #超时时间
use-key-prefix: false #不使用前缀
第二步:Cache配置文件
//启用Redis缓存,这个注释必须加上,即使单元测试也得加上
@EnableCaching
@Configuration
public class CacheRedisConfig {
/**
* 过期时间7天
*/
private final Duration timeToLive = Duration.ofDays(7);
private final StringRedisSerializer keySerializer = new StringRedisSerializer();
/**
* 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
*/
private final Jackson2JsonRedisSerializer valueSerializer = new Jackson2JsonRedisSerializer(Object.class);
/**
* 代码块,会优先执行
* 用来设置Jackson2JsonRedisSerializer
*/ {
ObjectMapper objectMapper = new ObjectMapper();
//设置所有访问权限以及所有的实际类型都可序列化和反序列化
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
//下面两行解决Java8新日期API序列化问题
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.registerModule(new JavaTimeModule());
valueSerializer.setObjectMapper(objectMapper);
}
/**
* 由于原生的redis自动装配,在存储key和value时,没有设置序列化方式,故自己创建redisTemplate实例
*
* @param factory
* @return
*/
@Bean(name = "redisTemplate")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// key采用String的序列化方式
template.setKeySerializer(keySerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(keySerializer);
// value序列化方式采用jackson
template.setValueSerializer(valueSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(valueSerializer);
template.afterPropertiesSet();
return template;
}
@Bean(name = "cacheManager")
public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
// 配置序列化(解决乱码的问题),通过config对象对缓存进行自定义配置
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
// 设置缓存的默认过期时间
.entryTtl(timeToLive)
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer))
// 不缓存空值
.disableCachingNullValues();
//根据redis缓存配置和reid连接工厂生成redis缓存管理器
RedisCacheManager redisCacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.transactionAware()
.build();
return redisCacheManager;
}
/**
* 配置事务管理器
*
* @param dataSource
* @return
*/
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
在SpringBoot2.0之后,spring容器自动的生成了StringRedisTemplate和RedisTemplate<Object,Object>,可以直接注入。但是在实际使用中,大多情况下不会直接使用RedisTemplate<Object,Object>,而是会对key和value进行序列化,所以我们还需要新增一个配置类。
第三步:Redis工具类
第四步:实体类:
@Getter
@Setter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Province implements Serializable {
private static final long serialVersionUID = -7311767494745575164L;
/**
* 编号
*/
@ApiModelProperty(value = "编号")
private Integer id;
/**
* 省名
*/
@ApiModelProperty(value = "省名")
private String name;
/**
* 所属地区
*/
@ApiModelProperty(value = "所属地区")
private String area;
/**
* 显示优先级
*/
@ApiModelProperty(value = "显示优先级")
private Integer priority;
/**
* 状态 1正常 0删除
*/
@ApiModelProperty(value = "状态 1正常 0删除")
private Integer status;
}
第五步:Service 接口及实现类:
使用MyBatisPlus实现
DeptService.java
public interface ProvinceService extends IService<Province> {
/**
* 添加省份
*
* @param province
* @return 返回修改后的省份
*/
Province insertProvince(Province province, Boolean flag);
/**
* 根据id查找省份
*
* @param id
* @return
*/
Province findProvinceById(Integer id, Boolean flag);
/**
* 从Redis中查找所有的省份
*
* @return
*/
List<Province> findAllProvince(String key, Boolean flag);
/**
* 修改省份
*
* @param province
* @return 返回修改后的省份
*/
Province updateProvince(Province province, Boolean flag);
/**
* 删除id指定的省份
*
* @param id
* @return
*/
Province deleteProvinceById(Integer id, Boolean flag);
}
DeptServiceImpl.java
@Service
@CacheConfig(cacheManager = "cacheManager")
public class ProvinceServiceImpl extends ServiceImpl<ProvinceMapper, Province> implements ProvinceService {
@Resource
private RedisUtil<List<Province>> provinceListRedisUtil;
@Override
@Cacheable(value = RedisConst.PROVINCE, key = "#key", condition = "#flag==true")
public List<Province> findAllProvince(String key, Boolean flag) {
List<Province> provinceList = baseMapper.selectList(null);
return provinceList;
}
@Override
@CachePut(value = RedisConst.PROVINCE, key = "#province.id", condition = "#flag==true")
public Province insertProvince(Province province, Boolean flag) {
int insertRes = baseMapper.insert(province);
if (insertRes == 1) {
//更新Province列表
List<Province> provinceList = provinceListRedisUtil.get(RedisConst.PROVINCE + "::all");
provinceList.add(province);
//排序
provinceList.sort((item1, item2) -> {
int t1 = item1.getPriority() == null ? 0 : item1.getPriority();
int t2 = item2.getPriority() == null ? 0 : item2.getPriority();
return t2 - t1;
});
provinceListRedisUtil.set(RedisConst.PROVINCE + "::all", provinceList);
}
return province;
}
@Override
@Cacheable(value = RedisConst.PROVINCE, key = "#id", condition = "#flag==true")
public Province findProvinceById(Integer id, Boolean flag) {
Province province = baseMapper.selectById(id);
return province;
}
@Override
@CachePut(value = RedisConst.PROVINCE, key = "#province.id", condition = "#flag==true")
public Province updateProvince(Province province, Boolean flag) {
int updateRes = baseMapper.updateById(province);
if (updateRes == 1) {
province = baseMapper.selectById(province.getId());
//目标城市下的所有Province
List<Province> provinceList = provinceListRedisUtil.get(RedisConst.PROVINCE + "::all");
for (int i = 0; i < provinceList.size(); i++) {
Province item = provinceList.get(i);
//如果参数country在目标城市中,即没有换城市
if (item.getId().equals(province.getId())) {
provinceList.set(i, province);
break;
}
}
provinceListRedisUtil.set(RedisConst.PROVINCE + "::all", provinceList);
}
return province;
}
@Override
@CacheEvict(value = RedisConst.PROVINCE, key = "#id", condition = "#flag==true")
public Province deleteProvinceById(Integer id, Boolean flag) {
int deleteRes = baseMapper.deleteById(id);
if (deleteRes == 1) {
//目标城市下的所有Province
List<Province> provinceList = provinceListRedisUtil.get(RedisConst.PROVINCE + "::all");
for (int i = 0; i < provinceList.size(); i++) {
Province item = provinceList.get(i);
//如果参数country在目标城市中,即没有换城市
if (item.getId().equals(id)) {
provinceList.remove(i);
break;
}
}
provinceListRedisUtil.set(RedisConst.PROVINCE + "::all", provinceList);
}
return new Province();
}
}
第六步:测试代码
@SpringBootTest
class ProvinceServiceImplTest {
@Resource
private ProvinceService provinceService;
@Test
void findAllProvince() {
List<Province> provinceList = provinceService.findAllProvince("all", true);
provinceList.forEach(System.out::println);
}
@Test
void insertProvince() {
Province province = Province.builder()
.name("haha")
.priority(79)
.build();
provinceService.insertProvince(province, true);
}
@Test
void findProvinceById() {
Province province = provinceService.findProvinceById(22, true);
System.out.println(province);
}
@Test
void updateProvince() {
Province province = Province.builder().id(47).name("america").build();
provinceService.updateProvince(province, true);
}
@Test
void deleteProvinceById() {
Province province = provinceService.deleteProvinceById(47, true);
System.out.println(province);
}
}