1)用户第一次访问的时候获取数据库的值,再次访问时直接从缓存中获取数据
2)设置缓存过期时间
3)项目8080端口是对外端口(向外部暴露的端口),区别于内部进程号,查内部端口用ps -ef|grep port,查外部端口用lsof -i:port
判断进程是否正常启动

启动会有一段时间datasource
![]()
结果:

先从缓存中拿数据,找不到再从DB中刷数据

之前的操作;1)先到redis缓存中去取值
2)如果找不到到db中去取值
3)将db中的值刷到缓存
这3个步骤都是固定的所以就有了cache
一级缓存是:sqlSession,sql建立连接到关闭连接的数据缓存
二级缓存是:全局
@CacheConfig(cacheNames="userInfoCache") 在同个redis里面必须唯一
@Cacheable(查) :
来划分可缓存的方法 - 即,结果存储在缓存中的方法,以便在后续调用(具有相同的参数)时,返回缓存中的值而不必实际执行该方法
@CachePut(修改、增加) :
当需要更新缓存而不干扰方法执行时,可以使用@CachePut注释。也就是说,始终执行该方法并将其结果放入缓存中(根据@CachePut选项)
@CacheEvict(删除) :
对于从缓存中删除陈旧或未使用的数据非常有用,指示缓存范围内的驱逐是否需要执行而不仅仅是一个条目驱逐
1)引入pom.xml依赖:
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-cacheartifactId>
- dependency>
2)开启缓存注解: @EnableCaching(RedisConfig类 配置类)

3)在方法上面加入SpEL
@CacheConfig(cacheNames="userInfoCache") 在同个redis里面必须唯一 指定cache的名称
@Cacheable(查) :
来划分可缓存的方法 -
即,结果存储在缓存中的方法,以便在后续调用(具有相同的参数)时,返回缓存中的值而不必实际执行该方法

@CachePut(修改、增加) :
当需要更新缓存而不干扰方法执行时,可以使用@CachePut注释。
也就是说,始终执行该方法并将其结果放入缓存中(根据@CachePut选项)

@CacheEvict(删除) :
对于从缓存中删除陈旧或未使用的数据非常有用,指示缓存范围内的驱逐是否需要执行而不仅仅是一个条目驱逐


结果:


- @Service
- @CacheConfig(cacheNames="userInfoCache") // 本类内方法指定使用缓存时,默认的名称就是userInfoCache
- @Transactional(propagation=Propagation.REQUIRED,readOnly=false,rollbackFor=Exception.class)
- public class UserService {
-
- @Autowired
- private UserMapper userMapper;
-
- // 因为必须要有返回值,才能保存到数据库中,如果保存的对象的某些字段是需要数据库生成的,
- //那保存对象进数据库的时候,就没必要放到缓存了
- @CachePut(key="#p0.id") //#p0表示第一个参数
- //必须要有返回值,否则没数据放到缓存中
- public User insertUser(User u){
- this.userMapper.insert(u);
- //u对象中可能只有只几个有效字段,其他字段值靠数据库生成,比如id
- return this.userMapper.find(u.getId());
- }
-
-
- @CachePut(key="#p0.id")
- public User updateUser(User u){
- this.userMapper.update(u);
- //可能只是更新某几个字段而已,所以查次数据库把数据全部拿出来全部
- return this.userMapper.find(u.getId());
- }
-
- @Nullable
- @Cacheable(key="#p0") // @Cacheable 会先查询缓存,如果缓存中存在,则不执行方法
- public User findById(String id){
- System.err.println("根据id=" + id +"获取用户对象,从数据库中获取");
- Assert.notNull(id,"id不用为空");
- return this.userMapper.find(id);
- }
-
-
-
- @CacheEvict(key="#p0") //删除缓存名称为userInfoCache,key等于指定的id对应的缓存
- public void deleteById(String id){
- this.userMapper.delete(id);
- }
-
- //清空缓存名称为userInfoCache(看类名上的注解)下的所有缓存
- //如果数据失败了,缓存时不会清除的
- @CacheEvict(allEntries = true)
- public void deleteAll(){
- this.userMapper.deleteAll();
- }
-
-
- @Nullable
- @Cacheable(value = "UserInfoList", keyGenerator = "simpleKeyGenerator") // @Cacheable 会先查询缓存,如果缓存中存在,则不执行方法
- public User findByIdTtl(String id){
- System.err.println("根据id=" + id +"获取用户对象,从数据库中获取");
- Assert.notNull(id,"id不用为空");
- return this.userMapper.find(id);
- }
-
- }
提问:springboot cache 存在什么问题,
第一,生成key过于简单,容易冲突 userinfoCache::3 (同一个redis只能有一个名称 ) key+参数
第二,无法设置过期时间,默认过期时间为永久不过期 (上面的3中方法)
第三,配置序列化方式,默认的是序列化JDKSerialazable jackson/json
如果不设置序列化的化可能会造成乱码等格式的问题
1)自定义KeyGenerator

2)自定义cacheManager,设置缓存过期时间

3)自定义序列化方式,Jackson

使用方法:(前两个)




- @Configuration
- @EnableCaching
- public class RedisConfig {
- public RedisTemplate
redisTemplate(RedisConnectionFactory factory){ - RedisTemplate
redisTemplate = new RedisTemplate<>(); - redisTemplate.setConnectionFactory(factory);
- return redisTemplate;
- }
- @Bean
- public KeyGenerator simpleKeyGenerator() {
- return (o, method, objects) -> {
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append(o.getClass().getSimpleName());
- stringBuilder.append(".");
- stringBuilder.append(method.getName());
- stringBuilder.append("[");
- for (Object obj : objects) {
- stringBuilder.append(obj.toString());
- }
- stringBuilder.append("]");
-
- return stringBuilder.toString();
- };
- }
-
- @Bean
- public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
- return new RedisCacheManager(
- RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
- this.getRedisCacheConfigurationWithTtl(600), // 默认策略,未配置的 key 会使用这个
- this.getRedisCacheConfigurationMap() // 指定 key 策略
- );
- }
- /**
- *
- * 自定义某个key的过期时间
- */
- private Map
getRedisCacheConfigurationMap() { - Map
redisCacheConfigurationMap = new HashMap<>(); - redisCacheConfigurationMap.put("UserInfoList", this.getRedisCacheConfigurationWithTtl(100));
- redisCacheConfigurationMap.put("UserInfoListAnother", this.getRedisCacheConfigurationWithTtl(18000));
- return redisCacheConfigurationMap;
- }
- /**
- *
- * 自定义序列化的方式
- */
- private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
- Jackson2JsonRedisSerializer
- ObjectMapper om = new ObjectMapper();
- om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
- om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
- jackson2JsonRedisSerializer.setObjectMapper(om);
-
- RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
- redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
- RedisSerializationContext
- .SerializationPair
- .fromSerializer(jackson2JsonRedisSerializer)
- ).entryTtl(Duration.ofSeconds(seconds));
-
- return redisCacheConfiguration;
- }
- }