搭建Spring Cache - Redis
Core:Spring Boot
ORM:Spring Data JPA
其他省略.... 0.0
实体类
@Data
@RequiredArgsConstructor
@AllArgsConstructor
@Entity(name = "cx_user")
public class User {
@Id
private String id;
private String name;
private Integer sex;
private String phone;
private Date insertTime;//JPA自动对驼峰命名属性对应数据库下划线insert_time
}
@Data
@Entity(name = "cx_depart")
public class Depart {
@Id
private String id;
private String name;
private String info;
private Date insertTime;//JPA自动对驼峰命名属性对应数据库下划线insert_time
@OneToOne(fetch = FetchType.EAGER)//不使用懒加载
@JoinColumn(name = "uid")
private User user;
}
Repository
@CacheConfig(cacheNames = "users")//设置全局value = "users"
public interface UserRepository extends JpaRepository<User, String> {
@Cacheable(key = "#p0")
User findById(String id);
// @Cacheable(key = "#p0")//会导致脏数据
User findByName(String name);
@CachePut(key = "#p0.id")
User save(User user);
//此方法不会清空上方两方法的缓存,因此要将users的缓存全部清空
@CacheEvict(key = "#id")
void delete(String id);
}
@CacheConfig(cacheNames = "departs")//设置全局value = "users"
public interface DepartRepository extends JpaRepository<Depart, String> {
@Cacheable(key = "#p0")
Depart findById(String id);
// @Cacheable(key = "#p0")//会导致脏数据
Depart findByName(String name);
@CachePut(key = "#p0.id")
Depart save(Depart depart);
//此方法不会清空上方两方法的缓存,因此要将users的缓存全部清空
@CacheEvict(key = "#id")
void delete(String id);
}
Redis Configuration
@Configuration
@EnableCaching
//不继承CachingConfigurerSupport则@CachePut会抛出类型转换异常
public class RedisCacheConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//ObjectMapper 这段代码能够保证正常的反序列化。设置fastjson时,会报Object无法匹配实体类,反序列化有问题,因此只能使用jackson2
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
redisTemplate.setKeySerializer(redisTemplate.getStringSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// redisTemplate.setValueSerializer(new FastJsonRedisSerializer<>(Object.class));
redisTemplate.setConnectionFactory(factory);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
//设置缓存失效时长,秒级
redisCacheManager.setDefaultExpiration(60);
return redisCacheManager;
}
/**
* 自定义生成redis-key, 当@Cacheable指定key时,则不使用该生成方式
* @return
*/
@Bean
@Override
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... objects) {
StringBuilder sb = new StringBuilder();
sb.append(o.getClass().getName()).append(".");
for (Object obj : objects) {
sb.append(obj.toString());
}
System.out.println("keyGenerator=" + sb.toString());
return sb.toString();
}
};
}
}
核心测试
ps:不知道是我学习Spring Cache的深度不够还是Spring Cache的Cache Name真的无用
首先,我们先看一下,我们如果按照官方文档提供的方式进行设置,在redis中key是什么样的。以此为例:
@Cacheable(key = "#p0")
User findById(String id);
结果:
image.png
我们会发现,redis里存在了users-keys和user的id,这时我就在想,如果以id存入的时候,假如这时候来个id和他一样的Depart怎么办?以我对Spring的了解,肯定他内部有办法,我觉得应该和这个users-keys有关,那么现在让我们测试一下假如id和他一样的Depart会怎么处理。
结果:
image.png
看来是我想多了,users-keys和departs-keys并没有起到什么作用,而且确实出现了类型不匹配异常,大家可以看一下我专门先查询了一下id=1的depart,已证明id不同时是能够正常放入缓存的。
结论
因此,再次我个人不推荐使用spring cache默认的放入方式。最好还是通过自定义KeyGenerator来保证缓存的隔离。我个人是推荐以repository为space进行隔离。
ps:如果对我的测试有什么疑问或者有其他好的建议希望能够指出,在此谢过