springboot集成redis (使用注解)
注意:一般开发中小型快速应用,适合redis注解开发。但是想要合理点的设置缓存,建议还是手动配置
为什么要使用注解版?
- 注解版使用方便
- 注解版功能多样化,适合多种环境
哪种不适合缓存
- insert插入数据库后,返回一个int值,这个值有必要缓存???
没必要。因为一般情况下我不会从缓存中取出int值,例如我插入了一个数据,缓存一个int值,在再插入一个数据,这种缓存一般不会被使用。
而且插入一条数据后,就应该让该命名空间下的所有key全部移除,所以一般写的是@CacheEvict(value allEntries=true)
- @EnableCache 开启基于注解的缓存功能
@Cacheable注解 :先从redis数据库中 按照当前key查找,有没有。如果redis中有,是不会走当前该方法的,如果没有再调用方法返回结果,如果结果不为null将其缓存到数据库中(一般用于find)
- 属性:
- value:key的一部分(前缀),主要是指明数据放在那个key范围
- key:key的主体,#p0:指明取出第一个参数 #p1:指明取出第二个参数。。。依此类推
- unless:结果为true,将当前的数据结果不保存到redis,#result:指明取出数据库中返回的结果
- condition 结果如果为true,将当前数据保存到redis
实例
@Cacheable(value = RedisConfig.REDIS_DATABASE_KEY, key = "'user-'+#p0", unless = "#result==null") @Override public User getUserByName(String name) { UserExample example = new UserExample(); example.createCriteria().andNameEqualTo(name); List<User> users = userMapper.selectByExample(example); if(users.size()==0){ return null; } return users.get(0); }
- @CachePut: 主要用于向数据库中插入数据,向数据中插入数据的时候,会将返回的int类型,放入redis中缓存,当然是有选择性的(一般用于insert)
- 属性:
- value:key的一部分,命名空间
- key:指定key的名称
- unless:满足条件,则不将返回的结果放入redis
- condition: 满足条件,则将返回的结果放入redis
实例
@CachePut(value = RedisConfig.REDIS_DATABASE_KEY,key = "'user-insert-'+#p0.id",unless = "#result==0") @Override public int insert(User record) { return userMapper.insert(record); }
@CacheEvict:满足条件则移除当前key在redis中的数据(一般用于update/delete)
- 属性:
- value: 同理命名空间
- key: key名称
- condition:满足什么条件从缓存中移除指定的key
- AllEntries:true/false 是否移除命名空间下的所有key
实例
@CacheEvict(value = RedisConfig.REDIS_DATABASE_KEY,key = "'user-'+#p0.id",condition = "#result==1") @Override public int updateByPrimaryKey(User record) { return userMapper.updateByPrimaryKey(record); }
问题
我们知道如果进行查询的时候,会将数据缓存到redis中。一旦进行增删改,那么原本数据库中的数据可能会发生变化,那么增删改成功后,应该要指定的移除指定key的缓存。但是我们通过@CaheEvict移除指定key的数据,发现并不能行的通。
为什么?
例如:我们修改了一个product信息,那么应该移除product-id 这种key的数据,但是如果这个product数据关联,product-list等等这种key(即list中有当前修改前的数据),按理说我们应该移除这些相关联的key
怎么做
- 第一种方法
@CaheEvict(value=“nameSpace”,allEntries=true),移除这个命名空间下的所有数据
@CaheEvict(value=“product”,allEntries=true),移除product命名空间下的所有keys
- 第二种
通过redisTemplate手动移除相关联的key的数据(相对来说麻烦点)
- 第一种方法
完整配置
pom.xml
<!--springboot中的redis依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- lettuce pool 缓存连接池--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
redisConfig
//开启基于注解的配置 @EnableCaching @Configuration public class RedisConfig { /** * redis数据库自定义key的命名空间 */ public static final String REDIS_DATABASE_KEY="tmall_springboot"; public static final String REDIS_CATEGORY_KEY="category"; public static final String REDIS_ORDER_ITEM_KEY="orderItem"; public static final String REDIS_ORDER_KEY="order"; public static final String REDIS_PRODUCT_KEY="product"; public static final String REDIS_PROPERTY_KEY="property"; public static final String REDIS_PROPERTY_VALUE_KEY="propertyValue"; public static final String REDIS_REVIEW_KEY="review"; public static final String REDIS_USER_KEY="user"; public static final String REDIS_PRODUCT_IMAGE_KEY="productImage"; /** * 自动配置的redisTemplate,存在序列化问题,会导致存入redis数据库中的数据,不容易看清所以需要自己配置 */ /** * 配置自定义redisTemplate * @return */ @Bean RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory,RedisSerializer redisSerializer) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); // 设置键(key)的序列化采用StringRedisSerializer。 redisTemplate.setKeySerializer(new StringRedisSerializer()); // 设置值(value)的序列化采用Jackson2JsonRedisSerializer。 redisTemplate.setValueSerializer(redisSerializer); // 设置hashKey的序列化 redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(redisSerializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } /** * 配置json序列化器(我们使用jackson的序列化器) */ @Bean public RedisSerializer redisSerializer(){ Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(objectMapper); return jackson2JsonRedisSerializer; } /** * 配置redis缓存管理器,管理注解版的缓存 */ @Bean public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory,RedisSerializer redisSerializer) { RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory); //设置Redis缓存有效期为10分钟 RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) //ttl .entryTtl(Duration.ofHours(2)); return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration); } }
application.yml
# 配置redis redis: host: 115.29.111.155 port: 6379 database: 0 #默认使用0号数据库 timeout: 2000 #设置连接超时时间 lettuce: pool: max-idle: 10
service层
@Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @CacheEvict(value = RedisConfig.REDIS_USER_KEY,allEntries = true,condition = "#result==1") @Override public int deleteByPrimaryKey(Integer id) { return userMapper.deleteByPrimaryKey(id); } /** * @CachePut: 主要用户向数据库中插入数据,向数据中插入数据的时候,会将返回的int类型,放入redis中缓存,当然是有选择性的 * 属性: * value:key的一部分,命名空间 * key:指定key的名称 * unless:满足条件,则不将返回的结果放入redis * condition: 满足条件,则将返回的结果放入redis */ // @CachePut(value = RedisConfig.REDIS_USER_KEY,key = "'insert-'+#p0.id",unless = "#result==0") @CacheEvict(value = RedisConfig.REDIS_USER_KEY,allEntries = true,condition = "#result==1") @Override public int insert(User record) { return userMapper.insert(record); } // @CachePut(value = RedisConfig.REDIS_USER_KEY,key = "'insert-'+#p0.id",unless = "#result==0") @CacheEvict(value = RedisConfig.REDIS_USER_KEY,allEntries = true,condition = "#result==1") @Override public int insertSelective(User record) { return userMapper.insertSelective(record); } @Cacheable(value = RedisConfig.REDIS_USER_KEY,key = "'list'",unless = "#result==null") @Override public List<User> selectByExample() { UserExample example = new UserExample(); example.setOrderByClause("id desc"); List<User> users = userMapper.selectByExample(example); return users; } /** * 查找数据库是否存在与该名称相同的用户,如果存在true else false * * 我们这样规定,对于key而言,返回boolean的加一级 exist * */ @Cacheable(value = RedisConfig.REDIS_USER_KEY , key = "'exist-'+#p0") @Override public boolean findUserByName(String name) { UserExample example = new UserExample(); example.createCriteria().andNameEqualTo(name); List<User> users = userMapper.selectByExample(example); if(users.size()==0){ return false; } return true; } /** * 通过username获取用户,username是唯一的 * redis中使用缓存缓存数据。 * * @Cacheable 开启基于注解的缓存功能 * * @Cacheable注解 :先从redis数据库中 按照当前key查找,有没有。如果没有再调用方法返回结果,如果结果不为null将其缓存到数据库中 * 属性: * value:key的一部分(前缀),主要是指明数据放在那个key范围 * key:key的主体,#p0:指明取出第一个参数 #p1:指明取出第二个参数。。。依此类推 * unless:结果为true,将当前的数据结果不保存到redis,#result:指明取出数据库中返回的结果 * condition 结果如果为true,将当前数据保存到redis * 此为生成的key,中文再redis中显示是这样的 * "tmall_springboot::user-\xe5\xbc\xa0\xe4\xb8\x89" */ @Cacheable(value = RedisConfig.REDIS_USER_KEY, key = "'one-name-'+#p0", unless = "#result==null") @Override public User getUserByName(String name) { // System.out.println("走该方法,数据应该缓冲"); UserExample example = new UserExample(); example.createCriteria().andNameEqualTo(name); List<User> users = userMapper.selectByExample(example); if(users.size()==0){ return null; } return users.get(0); } /** * 通过userName,password,查询是否有此用户 */ @Cacheable(value = RedisConfig.REDIS_USER_KEY,key = "'one-'+#p0.id",unless = "#result==null") @Override public User findOneByNameAndPassword(User user){ UserExample example = new UserExample(); example.createCriteria().andNameEqualTo(user.getName()).andPasswordEqualTo(user.getPassword()); List<User> users = userMapper.selectByExample(example); if(users.size()==1){ return users.get(0); } return null; } @Cacheable(value = RedisConfig.REDIS_USER_KEY , key = "'one-'+#p0",unless = "#result==null") @Override public User selectByPrimaryKey(Integer id) { return userMapper.selectByPrimaryKey(id); } /** * @CacheEvict:满足条件则移除当前key在redis中的数据 * 属性: * value: 同理命名空间 * key: key名称 * condition:满足什么条件缓存到redis中 * allEntries: 移除value(命名空间)下,全部缓存 */ @CacheEvict(value = RedisConfig.REDIS_USER_KEY,allEntries = true,condition = "#result==1") @Override public int updateByPrimaryKeySelective(User record) { return userMapper.updateByPrimaryKeySelective(record); } @CacheEvict(value = RedisConfig.REDIS_USER_KEY,allEntries = true,condition = "#result==1") @Override public int updateByPrimaryKey(User record) { return userMapper.updateByPrimaryKey(record); } }