Drop RedisTemplate and Use JPA Repository Instead
I have worked for many years and have used both methods, but it still depends on the usage scenario to decide which one to use. Overall, JPA Repository will be more convenient to use.
RedisTemplate
RedisTemplate is the best way to understand. We first define a POJO class, a very simple BOOK class, with three fields: id, name, and author. Then a RedisTemplate Bean, and then define a Service class that uses this RedisTemplate.
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Book implements Serializable {
private Long id;
private String name;
private String author;
}
@Bean
public RedisTemplate<String, Book> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Book> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
public Optional<Book> findOneBook(String name) {
HashOperations<String, String, Book> hashOperations = redisTemplate.opsForHash();
if (redisTemplate.hasKey(CACHE) && hashOperations.hasKey(CACHE, name)) {
log.info("Get book {} from Redis.", name);
return Optional.of(hashOperations.get(CACHE, name));
}
Optional<Book> book = bookRepository.getBook(name);
log.info("Book Found: {}", book);
if (book.isPresent()) {
log.info("Put book {} to Redis.", name);
hashOperations.put(CACHE, name, book.get());
redisTemplate.expire(CACHE, 10, TimeUnit.MINUTES);
}
return book;
}Use Hash to store this Book information. In the above method, check whether the title of the book exists in Redis. If it exists, it will return directly. It will go to the persistent storage to find it if it does not exist. If it is found, it will write it to Redis through Template. This is a common practice for caching. It feels very convenient to use. For the sake of simplicity, we did not use persistent storage here, so we hard-coded a few pieces of data.
@Repository
public class BookRepository {
Map<String, Book> bookMap = new HashMap<>();
public BookRepository(){
bookMap.put("apache kafka", Book.builder()
.name("apache kafka").id(1L).author("zhangsan")
.build());
bookMap.put("python", Book.builder()
.name("python").id(2L).author("lisi")
.build());
}
public Optional<Book> getBook(String name){
if(bookMap.containsKey(name)){
return Optional.of(bookMap.get(name));
}
else{
return Optional.empty();
}
}
}Call bookService.findOneBook(“python”) and bookService.findOneBook(“apache kafka”) to write data into the cache
127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x04book"
127.0.0.1:6379> type "\xac\xed\x00\x05t\x00\x04book"
hash
127.0.0.1:6379> hgetall "\xac\xed\x00\x05t\x00\x04book"
1) "\xac\xed\x00\x05t\x00\x06python"
2) "\xac\xed\x00\x05sr\x00&com.ken.redistemplatesample.model.Book=\x19\x96\xfb\x7f\x7f\xda\xbe\x02\x00\x03L\x00\x06authort\x00\x12Ljava/lang/String;L\x00\x02idt\x00\x10Ljava/lang/Long;L\x00\x04nameq\x00~\x00\x01xpt\x00\x04lisisr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x00\x00\x00\x00\x00\x02t\x00\x06python"
3) "\xac\xed\x00\x05t\x00\x0capache kafka"
4) "\xac\xed\x00\x05sr\x00&com.ken.redistemplatesample.model.Book=\x19\x96\xfb\x7f\x7f\xda\xbe\x02\x00\x03L\x00\x06authort\x00\x12Ljava/lang/String;L\x00\x02idt\x00\x10Ljava/lang/Long;L\x00\x04nameq\x00~\x00\x01xpt\x00\bzhangsansr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x00\x00\x00\x00\x00\x01t\x00\x0capache kafka"It can be seen that the data is stored in a Hash table whose key is “\xac\xed\x00\x05t\x00\x04book”, and there are two records in the Hash. Did you find a problem? That is, the key is not the “book” key we imagined, but a string of hexadecimal codes.
This is because RedisTemplate uses the default JdkSerializationRedisSerializer to serialize our key and value. If everyone uses the Java language That’s no problem. If someone writes in Java and someone reads in another language, there will be a problem, just like when I used the hgetall “book” at the beginning, I couldn’t get the data. RedisTemplate also provides StringRedisTemplate to facilitate the use of String to serialize the data in redis
@Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
{
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
public Optional<String> getBookString(String name){
HashOperations<String, String, String> hashOperations = stringRedisTemplate.opsForHash();
if (stringRedisTemplate.hasKey(STRINGCACHE) && hashOperations.hasKey(STRINGCACHE, name)) {
log.info("Get book {} from Redis.", name);
return Optional.of(hashOperations.get(STRINGCACHE, name));
}
Optional<Book> book = bookRepository.getBook(name);
log.info("Book Found: {}", book);
if (book.isPresent()) {
log.info("Put book {} to Redis.", name);
hashOperations.put(STRINGCACHE, name, book.get().getAuthor());
stringRedisTemplate.expire(STRINGCACHE, 10, TimeUnit.MINUTES);
return Optional.of(book.get().getAuthor());
}
return Optional.empty();
}127.0.0.1:6379> keys *
1) "string_book"
127.0.0.1:6379> hgetall string_book
1) "python"
2) "lisi"
3) "apache kafka"
4) "zhangsan"JPA Repository
When using JPA Repository to access DataBase, operations such as adding, deleting, modifying, and checking can be easily realized. It is to define an interface without writing code. Spring will help us complete most of the work, so is it possible to access Redis? how about this? The answer is yes, let’s look at the code First we still define a POJO.
@RedisHash(value = "cache-book", timeToLive = 600)
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CacheBook implements Serializable {
@Id
private Long userId;
@Indexed
private String name;
private String author;
}The difference between this class and the class above our template is that we added two annotations, @RedisHash(value = “cache-book”, timeToLive = 600) at the beginning of the class, and @Id and @Indexed definition on the field A Repository interface.
Then define a service like the template in the above example. If there is one in the cache, get it from the cache. If not, find it in the persistent storage and write it into the cache.
public interface CacheBookRepository extends CrudRepository<CacheBook, Long> {
Optional<CacheBook> findOneByName(String name);
}
@Slf4j
@Service
public class BookService {
private static final String CACHE = "repository-book";
@Autowired
private CacheBookRepository cacheRepository;
@Autowired
private BookRepository bookRepository;
public Optional<CacheBook> findOneBook(String name) {
Optional<CacheBook> optionalCacheBook = cacheRepository.findOneByName(name);
if(!optionalCacheBook.isPresent())
{
Optional<CacheBook> book = bookRepository.getBook(name);
log.info("Book Found: {}", book);
if (book.isPresent()) {
log.info("Put book {} to Redis.", name);
cacheRepository.save(book.get());
}
return book;
}
return optionalCacheBook;
}
}127.0.0.1:6379> keys *
1) "repository-book:2"
2) "repository-book:2:idx"
3) "repository-book"
4) "repository-book:name:apache kafka"
5) "repository-book:name:python"
6) "repository-book:1:idx"
7) "repository-book:1"
127.0.0.1:6379> smembers repository-book:1:idx
1) "repository-book:name:apache kafka"
127.0.0.1:6379> smembers "repository-book:name:apache kafka"
1) "1"Cache
If there is something in the cache, return it from the cache, if not, find it from the persistent storage, and then write it into the cache. This function can be implemented using Cache
@Slf4j
@Service
@CacheConfig(cacheNames = "cache-book")
public class BookService {
@Autowired
private BookRepository bookRepository;
@Cacheable
public List<CacheBook> findAllCoffee() {
return bookRepository.findAll();
}
@CacheEvict
public void reloadCoffee() {
}
}Annotation @CacheConfig(cacheNames = “cache-book”) is added to the class. Cacheable and CacheEvict are added to the method. The method of Cacheable is used to implement logic. If there is one, it will be taken from the cache. If not, it will be taken from the database, CacheEvict clears the cache when calling this method. Then add the annotation @EnableJpaRepositories @EnableCaching(proxyTargetClass = true) where the entry program is started, and add it to the configuration file application.properties
Finally
Thanks for reading. I am looking forward to your following and reading more high-quality articles.




