1.作为mybits的缓存整合
1)用户第一次访问的时候获取数据库的值,再次访问时直接从缓存中获取数据
2)设置缓存过期时间
3)项目8080端口是对外端口(向外部暴露的端口),区别于内部进程号,查内部端口用ps -ef|grep port,查外部端口用lsof -i:port
判断进程是否正常启动
启动会有一段时间datasource
结果:
先从缓存中拿数据,找不到再从DB中刷数据
2.作为mybits的二级缓存的整合
(1)springboot cache的使用:可以结合redis、ehcache等缓存
之前的操作;1)先到redis缓存中去取值
2)如果找不到到db中去取值
3)将db中的值刷到缓存
这3个步骤都是固定的所以就有了cache
一级缓存是:sqlSession,sql建立连接到关闭连接的数据缓存
二级缓存是:全局
@CacheConfig(cacheNames="userInfoCache") 在同个redis里面必须唯一
@Cacheable(查) :
来划分可缓存的方法 - 即,结果存储在缓存中的方法,以便在后续调用(具有相同的参数)时,返回缓存中的值而不必实际执行该方法
@CachePut(修改、增加) :
当需要更新缓存而不干扰方法执行时,可以使用@CachePut注释。也就是说,始终执行该方法并将其结果放入缓存中(根据@CachePut选项)
@CacheEvict(删除) :
对于从缓存中删除陈旧或未使用的数据非常有用,指示缓存范围内的驱逐是否需要执行而不仅仅是一个条目驱逐
(2)springboot cache的整合步骤:
1)引入pom.xml依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</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
如果不设置序列化的化可能会造成乱码等格式的问题
(3)springboot cache自定义项
1)自定义KeyGenerator
2)自定义cacheManager,设置缓存过期时间
3)自定义序列化方式,Jackson
使用方法:(前两个)
@Configuration
@EnableCaching
public class RedisConfig {
public RedisTemplate<String,String> redisTemplate(RedisConnectionFactory factory){
RedisTemplate<String, String> 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<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
redisCacheConfigurationMap.put("UserInfoList", this.getRedisCacheConfigurationWithTtl(100));
redisCacheConfigurationMap.put("UserInfoListAnother", this.getRedisCacheConfigurationWithTtl(18000));
return redisCacheConfigurationMap;
}
/**
*
* 自定义序列化的方式
*/
private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
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;
}
}