学习笔记@某Spring学习视频
第九天
RedisTemplate与RedisRepository
两者的应用场景的区别,
比如,你按ID存了一个数据到Redis里,但又希望能按别的维度来查询时,一种方式就是自己维护二级索引,而RedisRepository可以帮你做这个二级索引,让你根据自己需要来查询。
完全自己用Jedis或者Lettuce客户端什么都做,也就是你说的最灵活的。但如果你就想缓存一些方法的执行结果,Spring的缓存抽象都帮你写好了,直接用就行,这时你就不用自己再操心怎么写了
简单来说,就是RedisTemplate就是封装好的,直接操作就可以用的;RedisRepository要求对细节上有更多的控制
RedisTemplate
引入依赖后
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
就在CoffeeService类中、注入RedisTemplate、写一个findOneCoffee方法
@Autowired
private RedisTemplate<String, Coffee> redisTemplate;
public Optional<Coffee> findOneCoffee(String name) {
//omit
}
在Optional findOneCoffee(String name)方法中
//redis缓存名称
private static final String CACHE="springbucks-coffee";
//findOneCoffee方法
public Optional<Coffee> findOneCoffee(String name) {
HashOperations<String, String, Coffee> hashOperations = redisTemplate.opsForHash();
if (redisTemplate.hasKey(CACHE) && hashOperations.hasKey(CACHE, name)) {
log.info("Get coffee {} from Redis.", name);
return Optional.of(hashOperations.get(CACHE, name));
}
ExampleMatcher exampleMatcher=ExampleMatcher.matching().withMatcher("name",exact().ignoreCase());
Optional<Coffee> coffee=coffeeRepository.findOne(Example.of(Coffee.builder().name(name).build(),exampleMatcher));
log.info("Coffee Found: {}", coffee);
if(coffee.isPresent()){
log.info("Put coffee {} to Redis.", name);
hashOperations.put(CACHE,name,coffee.get());
//设置过期时间
redisTemplate.expire(CACHE,1, TimeUnit.MINUTES);
}
return coffee;
}
使用的入口类开启事务注解,注入CoffeeService的Bean,就可以简单通过RedisTemplate为方法findOneCoffee使用缓存
···
@EnableTransactionManagement
public class RedisApplication implements ApplicationRunner {
@Autowired
private CoffeeService coffeeService;
···
@Bean
public RedisTemplate<String, Coffee>redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String,Coffee> template=new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
···
@Override
public void run(ApplicationArguments args) throws Exception{
Optional<Coffee> c = coffeeService.findOneCoffee("mocha");
log.info("Coffee {}", c);
for(int i=0;i<5;i++){
c=coffeeService.findOneCoffee("mocha");
}
log.info("Value from Redis: {}", c);
}
RedisRepository
先新建一个CoffeeCache类
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@RedisHash(value = "springbucks-coffee",timeToLive = 60)//指定redis命名及ttl时间
public class CoffeeCache {
@Id
private Long id;
@Indexed
private String name;
private Money price;
}
而后
public interface CoffeeCacheRepository extends CrudRepository<CoffeeCache, Long> {
Optional<CoffeeCache> findOneByName(String name);
}
在CoffeeService中注入repository
@Autowired
private CoffeeCacheRepository coffeeCacheRepository;
public Optional<Coffee> findSimpleCoffeeFromCache(String name) {
Optional<CoffeeCache> cached = coffeeCacheRepository.findOneByName(name);
if (cached.isPresent()) {
CoffeeCache coffeeCache = cached.get();
Coffee coffee = Coffee.builder().name(coffeeCache.getName()).price(coffeeCache.getPrice()).build();
log.info("Coffee {} found in cache.", coffeeCache);
return Optional.of(coffee);
}
else{
Optional<Coffee> raw=findOneCoffee(name);
raw.ifPresent(c->{
CoffeeCache coffeeCache=CoffeeCache.builder().id(c.getId()).name(c.getName()).price(c.getPrice()).build();
log.info("Save Coffee {} to cache.", coffeeCache);
coffeeCacheRepository.save(coffeeCache);
});
return raw;
}
在application类中注入
@Autowired
private CoffeeService coffeeService;
直接调用
findSimpleCoffeeFromCache
方法即可
扩展内容
@Indexed
用于标识对应属性在Redis数据库中生成二级索引。使用该注解后会在Redis数据库中生成属性对应的二级索引,索引名称就是属性名,可以方便地进行数据条件查询
主键索引和非主键索引的区别是:非主键索引的叶子节点存放的是主键的值,而主键索引的叶子节点存放的是整行数据,其中非主键索引也被称为二级索引,而主键索引也被称为聚簇索引。
根据这两种结构我们来进行下查询,看看他们在查询上有什么区别。
1、如果查询语句是 select * from table where ID = 100,即主键查询的方式,则只需要搜索 ID 这棵 B+树。
2、如果查询语句是 select * from table where k = 1,即非主键的查询方式,则先搜索k索引树,得到ID=100,再到ID索引树搜索一次,这个过程也被称为回表。