Simple CRUD using Servlet 3.0, Redis/Jedis and CDI – Part 1
In this post we will build a simple user crud. The data will be stored in Redis. To interact with Redis we will use Jedis library. CDI for Depedency Injection and Servlet 3.0 for the view.
Let’s start with the Redis/Jedis part. You can find some overview on Redis and Jedis in these posts.
Let’s start with the User class, we can see that below:
public class User { private String firstName; private String lastName; private String email; private String gender; private long id; }
Now let’s define the keys we will use to store the user information on Redis. In our example we will use three keys:
- user:ids – That will be used to generate the user ids by using the INCR command.
- user:all – A Redis List to store all user ids
- user:<id>:data – There will be one key with this pattern for each user in the system. Those keys will be hashes;
When we are going to add a new user to the system, we will handle three keys, as we can see in the steps below:
- First we get a new user id by incrementing the user:ids key: INCR user:ids
- Then we add it to the user:all list: lpush user:all returnedId
- And add the user information in its own hash: HMSET user:<returnedId>:data field value ..
We can see this code in action the method UserDAO.addUser:
public User addUser(User user){ long userId = jedis.incr(Keys.USER_IDS.key()); user.setId(userId); //Getting the Pipeline Pipeline pipeline = jedis.pipelined(); //add to users list pipeline.lpush(Keys.USER_ALL.key(), String.valueOf(userId)); //add to the hash pipeline.hmset(Keys.USER_DATA.formated(String.valueOf(userId)), BeanUtil.toMap(user)); pipeline.sync(); return user; }
Explainning the code above, firstly we get the new user id. The jedis variable is an attribute of the UserDAO class, it is an instance of Jedis class. To avoid three network callings to the redis server we use the concept of Pipeline, so, in one calling to the redis server we will add the user id in the user:all list and the user info in the user:<id>:data hash.
The commands performed by the pipeline instance will be performed in the redis server after the calling of pipeline.sync(). We created an util class to convert the user object in a Map<String, String> to be stored in the redis hash.
To see the user’s detail we have a method in the DAO to get the user, that we can see below:
public User getUser(long userId){ String userInfoKey = Keys.USER_DATA.formated(String.valueOf(userId)); Map<String, String> properties = jedis.hgetAll(userInfoKey); return BeanUtil.populate(properties, new User()); }
As we can see it is a simple method, basically we call the command HGETALL to retrieve all fields from a Hash. The Jedis api returns that as Map so we can simply fill the user properties from the Map.
To remove the user we created the method below:
public boolean remove(long userId){ String userInfoKey = Keys.USER_DATA.formated(String.valueOf(userId)); Pipeline pipeline = jedis.pipelined(); Response<Long> responseDel = pipeline.del(userInfoKey); Response<Long> responseLrem = pipeline.lrem(Keys.USER_ALL.key(), 0, String.valueOf(userId)); pipeline.sync(); return responseDel.get() > 0 && responseLrem.get() > 0; }
The method below we also use the concept of pipelining once we need to remove the Hash key and the user id from the user:all list. The LREM command removes the value from the list, the zero indicates to remove all occurrences of this value in the list. In this method we also use the returned values from the commands by using the Response objects returned by each command. We can only use those objects after calling the sync method.
The update method is very simple, we can see it below:
public User update(User user){ String userInfoKey = Keys.USER_DATA.formated(String.valueOf(user.getId())); jedis.hmset(userInfoKey ,BeanUtil.toMap(user)); return user; }
It is just a calling of HMSET passing a Map with all user attributes to be updated in the redis hash.
To list the users we would need also to use pipeline. Redis doesn’t provide a HMGETALL command, so, to retrieve all users with one network connection we will do that via pipeline.
The list method can be seen below:
public List<User> list(){ List<User> users = new ArrayList<User>(); //Get all user ids from the redis list using LRANGE List<String> allUserIds = jedis.lrange(Keys.USER_ALL.key(), 0, -1); if(allUserIds != null && !allUserIds.isEmpty()){ List<Response<Map<String,String>>> responseList = new ArrayList<Response<Map<String,String>>>(); Pipeline pipeline = jedis.pipelined(); for(String userId : allUserIds){ //call HGETALL for each user id responseList.add(pipeline.hgetAll(Keys.USER_DATA.formated(userId))); } pipeline.sync(); //iterate over the pipelined results for(Response<Map<String, String>> properties : responseList){ users.add(BeanUtil.populate(properties.get(), new User())); } } return users; }
In this method we first get all the user ids from the list user:all by using the command LRANGE. After that we do a “HMGETALL” via pipeline, we call the HGETALL command for each user, then we build the user objects from the Map instances returned.
In this first post we saw how to interact with the redis server using the Jedis api to store and retrieve the user’s information. We saw the concept and use of Pipeline. In the next post, we will show how to use CDI for Dependency Injection and Servlet 3.0 for the view.