Spring Data with Redis
In this tutorial, we will have a look at Redis, the key-value database or store. We’ll look at a couple of template implementations for storing data to and from a Redis database.
Spring framework is an open source Java platform that provides MVC infrastructure support for developing robust Java applications very easily and very rapidly using recommended MVC pattern.
Redis
Redis is driven by a key-store based data structure to persist the data and can be used as a database, cache and a message broker as well. To look at the main features:
- It’s a NoSQL key-value data store.
- Acts like a data structure server.
- Redis can be used as a real database instead of just volatile cache.
The data types found in Redis are even more important. Key values can be simple strings, like we can do in memcached, but they can also be more complex types like Hashes, Lists (ordered collection, makes a great queue), Sets (unordered collection of non-repeating values), or Sorted Sets (ordered/ranked collection of non-repeating values).
Comparison with Memcache
Persistence to disk means you can use Redis as a real database instead of just a volatile cache. The data won't disappear when you restart, like with memcached.
Redis is a fantastic choice if we want a highly scalable data store shared by multiple processes, multiple applications, or multiple servers. As just an inter-process communication mechanism it is tough to beat. The fact that we can communicate cross-platform, cross-server, or cross-application just as easily makes it a pretty great choice for many many use cases. Its speed also makes it great as a caching layer.
Maven Dependencies
To start, let’s add suitable maven dependency for Redis in our project:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.1.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.2.RELEASE</version>
</dependency>
Clearly, we added Spring dependencies as well.
Configuring Redis
To start talking to a Redis Database, we need to create a connection using a Redis connection factory. There are, not one but four available options here:
- JedisConnectionFactory
- JredisConnectionFactory
- LettuceConnectionFactory
- SrpConnectionFactory
Good thing about having four options is we can go for any option but the configuration remains surprisingly similar. For example, to define a bean for JedisConnectionFactory, we can do:
@Bean
public RedisConnectionFactory redisCF() {
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
jedisConnectionFactory.setHostName("redis-server");
jedisConnectionFactory.setPort(7379);
jedisConnectionFactory.setPassword("qwerty");
return jedisConnectionFactory;
}
Each Redis connection factory have setHostName, setPort and setPassword methods.
Working with RedisTemplate
Redis connection factory produce connection to a Redis key-value store. Using RedisConnection, we can store and read data. Using the jedisConnectionFactory, a RedisTemplate is defined, which will then be used for querying data with a custom repository.
Let’s move on to define a RedisTemplate @Bean:
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(redisCF());
return template;
}
As with other Spring data projects, Spring Data Redis offers a high-level data access option with RedisTemplate, offering two templates:
- RedisTemplate
- StringRedisTemplate
Constructing and Using RedisTemplate
We already saw how to construct a RedisTemplate bean. Something to notice is that it is parameterized with two types:
- String: This signifies that the key of the data is a String.
- Object: Well, this can be any type of Object needed to be stored against a String key.
If both types turn out to be a String, we better use StringRedisTemplate. Once we have a RedisTemplate (or StringRedisTemplate), we can start saving, fetching, and deleting key-value entries.
Working with Simple values
Suppose that we want to save a User to a RedisTemplate<String, User> where the key is the value of the id property. The following snippet of code will do that via opsForValue():
redis.opsForValue().set(user.getId(), user);
Similarly, if we wanted to fetch a user whose id is 23, we could use this snippet:
User user = redis.opsForValue().get("23");
If no entry is found with the given key, null will be returned.
Working with Lists
Working with list values is simple via opsForList(). For example, we can add a value to the end of a list entry like this:
redis.opsForList().rightPush("users", user);
This adds a User to the end of the list stored at the key users.
>>> If a list doesn't already exist at that key, one will be created.
Whereas the rightPush() method adds an element to the end of a list entry, left- Push() inserts a value at the beginning:
redis.opsForList().leftPush("users", user);
There are a number of ways we can fetch an item from a list. We can pop an entry off of either end using leftPop() or rightPop():
User first = redis.opsForList().leftPop("users");
User last = redis.opsForList().rightPop("users");
Aside from fetching a value from the list, these two pop methods have the side effect of removing the popped items from the list. If we’d rather simply retrieve the value (perhaps even from the middle of the list), we can use the range() method:
List<User> users = redis.opsForList().range("users", 2, 12);
The range() method doesn’t remove any values from the list entry, but it does retrieve one or more values given the key and a range of indexes. The above example retrieves eleven entries starting with the entry at index 2 and going through index 12 (inclusive). If the range exceeds the bounds of the list, then only the entries within those indexes will be returned. If no entries fall within the indexes, an empty list will be returned.
Enough simple types thugh. What about our own Repository implementations like other Spring Data projects?
Redis Repository
To begin, let’s start by creating an entity which we will follow along with:
public class User implements Serializable {
public enum Gender {
MALE, FEMALE
}
private String id;
private String name;
private Gender gender;
}
Spring Data Repository
As with other projects, let’s create our own UserRepository:
public interface UserRepository {
void saveUser(User user);
//Other methods
}
It is worth noticing, unlike other Spring Data Repository interfaces, this is just a standard interface to define a required method. It doesn’t indicate any Spring-related features.
This is certainly unusual for a Spring Data project as well. Other Spring Data projects are enabled for building repositories based on the common Spring Data interfaces.
For instance, Spring Data JPA provides several base repository interfaces that we can extend to get base features such as basic CRUD operations, the ability to generate queries based on method names, etc. In most cases, there’s no need to write an implementation of the repository interface at all.
Spring Data Repository Implementation
The UserRepositoryImpl implementation is using the redisTemplate defined in the Java configuration above.
Redis supports different data structures such as hashes, lists, sets, and sorted sets. In this example, we will use the opsForHash() function, which uses hash-related operations for data manipulation. We will use the string “User” as the name of the hash in which our User entities are stored.
@Repository
public class UserRepositoryImpl implements UserRepository {
private static final String KEY = "User";
private RedisTemplate<String, User> redisTemplate;
private HashOperations hashOps;
@Autowired
privateUserRepositoryImpl(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
@PostConstruct
private void init() {
hashOps = redisTemplate.opsForHash();
}
public void saveUser(User user) {
hashOps.put(KEY, user.getId(), user);
}
public void updateUser(User user) {
hashOps.put(KEY, user.getId(), user);
}
public User findUser(String id) {
return (User) hashOps.get(KEY, id);
}
public Map<Object, Object> findAllUsers() {
return hashOps.entries(KEY);
}
public void deleteUser(String id) {
hashOps.delete(KEY, id);
}
}
That was pretty straightforward. Now quickly, let’s look at each CRUD operation.
User user = new User("123", "Liran", User.Gender.MALE);
userRepository.saveUser(user);
Finding a user will be:
User retrievedUser = userRepository.findUser("123");
Let’s update a user now:
retrievedUser.setName(Hermoine");
userRepository.saveUser(retrievedUser);
Deleting a user will look like:
userRepository.deleteUser(user.getId());
Conclusion
In this tutorial, we went through the basics of Spring Data Redis.
No matter what kind of database we choose, fetching data from the database is a costly operation. In fact, database queries are often the biggest performance bottlenecks in any application.
Check out the homepage for more of the best SDK tools.
Recent Stories
Top DiscoverSDK Experts
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment