RedisTemplate 实现缓存(通常用这个)
1.修改CoffeeService
public class CoffeeService {
private static final String CACHE = "springbucks-coffee";
@Autowired
private CoffeeRepository coffeeRepository;
@Autowired
private RedisTemplate<String, Coffee> redisTemplate;
public List<Coffee> findAllCoffee() {
return coffeeRepository.findAll();
}
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 matcher = ExampleMatcher.matching()
.withMatcher("name", exact().ignoreCase());
Optional<Coffee> coffee = coffeeRepository.findOne(
Example.of(Coffee.builder().name(name).build(), matcher));
log.info("Coffee Found: {}", coffee);
if (coffee.isPresent()) {
log.info("Put coffee {} to Redis.", name);
hashOperations.put(CACHE, name, coffee.get());//coffee.get() 如果Optional有值则将其返回,否则抛出NoSuchElementException
redisTemplate.expire(CACHE, 1, TimeUnit.MINUTES); //设置一分钟的过期时间
}
return coffee;
}
}
2.执行SpringBucksApplication
@Slf4j
@EnableTransactionManagement
@SpringBootApplication
@EnableJpaRepositories
public class SpringBucksApplication implements ApplicationRunner {
@Autowired
private CoffeeService coffeeService;
public static void main(String[] args) {
SpringApplication.run(SpringBucksApplication.class, args);
}
@Bean
public RedisTemplate<String, Coffee> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Coffee> template = new RedisTemplate<>(); //因为本来的泛型为object 所以需要我们重新写个
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
public LettuceClientConfigurationBuilderCustomizer customizer() {
return builder -> builder.readFrom(ReadFrom.MASTER_PREFERRED); //优先读主节点
}
@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);
}
}
Redis Repository 实现缓存(需要二级索引的时候,用这个)
实体注解
- @RedisHash
@RedisHash(value = “springbucks-coffee”, timeToLive = 60)
对应了@entity,value为名字,ttl为存在周期 - @Id
主键 - @Indexed
此注解为某个字段申请索引 如果需要二级索引,加这个
代码实现
1.将money转换类改为这两个类
@ReadingConverter //读Redis数据时候使用这个
public class BytesToMoneyConverter implements Converter<byte[], Money> {
@Override
public Money convert(byte[] source) {
String value = new String(source, StandardCharsets.UTF_8);
return Money.ofMinor(CurrencyUnit.of("CNY"), Long.parseLong(value));
}
}
@WritingConverter ////写入Redis数据时候使用这个
public class MoneyToBytesConverter implements Converter<Money, byte[]> {
@Override
public byte[] convert(Money source) {
String value = Long.toString(source.getAmountMinorLong());
return value.getBytes(StandardCharsets.UTF_8);
}
}
2.加入我们需要缓存的Redis实体
@RedisHash(value = "springbucks-coffee", timeToLive = 60) //对应了entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CoffeeCache {
@Id
private Long id;
@Indexed //此注解为某个字段申请索引 二级索引
private String name;
private Money price;
}
3.加入缓存接口
public interface CoffeeCacheRepository extends CrudRepository<CoffeeCache, Long> {
Optional<CoffeeCache> findOneByName(String name);
}
4.修改CoffeeService类
@Slf4j
@Service
public class CoffeeService {
@Autowired
private CoffeeRepository coffeeRepository;
@Autowired
private CoffeeCacheRepository cacheRepository;
public List<Coffee> findAllCoffee() {
return coffeeRepository.findAll();
}
public Optional<Coffee> findSimpleCoffeeFromCache(String name) {
Optional<CoffeeCache> cached = cacheRepository.findOneByName(name); //Optional类的Javadoc描述如下:这是一个可以为null的容器对象.如果值存在则isPresent()方法会返回true,
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 -> { //如果Optional实例有值则为其调用consumer,否则不做处理 如果Optional实例有值,调用ifPresent()可以接受接口段或lambda表达式。
CoffeeCache coffeeCache = CoffeeCache.builder()
.id(c.getId())
.name(c.getName())
.price(c.getPrice())
.build();
log.info("Save Coffee {} to cache.", coffeeCache);
cacheRepository.save(coffeeCache);
});
return raw;
}
}
public Optional<Coffee> findOneCoffee(String name) {
ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("name", exact().ignoreCase());
Optional<Coffee> coffee = coffeeRepository.findOne(
Example.of(Coffee.builder().name(name).build(), matcher));
log.info("Coffee Found: {}", coffee);
return coffee;
}
}
5.执行SpringBucksApplication
@Slf4j
@EnableTransactionManagement
@SpringBootApplication
@EnableJpaRepositories
@EnableRedisRepositories
public class SpringBucksApplication implements ApplicationRunner {
@Autowired
private CoffeeService coffeeService;
public static void main(String[] args) {
SpringApplication.run(SpringBucksApplication.class, args);
}
@Bean
public LettuceClientConfigurationBuilderCustomizer customizer() {
return builder -> builder.readFrom(ReadFrom.MASTER_PREFERRED); //从主节点开始读
}
@Bean
public RedisCustomConversions redisCustomConversions() {
return new RedisCustomConversions(
Arrays.asList(new MoneyToBytesConverter(), new BytesToMoneyConverter()));//加入对于金额的处理
}
@Override
public void run(ApplicationArguments args) throws Exception {
Optional<Coffee> c = coffeeService.findSimpleCoffeeFromCache("mocha");
log.info("Coffee {}", c);
for (int i = 0; i < 5; i++) {
c = coffeeService.findSimpleCoffeeFromCache("mocha");
}
log.info("Value from Redis: {}", c);
}
}
用Redis实现缓存的方法
-
用spring的配置文件
spring.cache.type=redis
spring.cache.cache-names=coffee
spring.cache.redis.time-to-live=5000
#缓存的生命周期
spring.cache.redis.cache-null-values=false -
RedisTemplate 实现缓存
-
Redis Repository 实现缓存(需要二级索引使用这个)
RedisTemplate是万能的,各种情况下都能用,不过是手工操作的;@Cachable用在把某个方法的返回值缓存的情况,可以不用自己写,Spring替你做了,这个就会比较方便。Repository这个就比较少用到,像操作数据库的Repository一样来操作Redis,如果针对一些数据有二级索引的需求,Repository替我们做了主键以外的索引。