Статьи

Простой CRUD с использованием Servlet 3.0, Redis / Jedis и CDI — Часть 1

В этом посте мы создадим простой пользовательский crud. Данные будут сохранены в Redis. Для взаимодействия с Redis мы будем использовать библиотеку Jedis. CDI для Depedency Injection и Servlet 3.0 для представления.

Давайте начнем с части Redis / Jedis. Вы можете найти обзор Redis и Jedis в этих постах .

Давайте начнем с класса User, мы можем видеть это ниже:

01
02
03
04
05
06
07
08
09
10
11
12
public class User {
 
    private String firstName;
 
    private String lastName;
 
    private String email;
 
    private String gender;
 
    private long id;
}

Теперь давайте определим ключи, которые мы будем использовать для хранения пользовательской информации в Redis. В нашем примере мы будем использовать три ключа:

  • user: ids — Это будет использоваться для генерации идентификаторов пользователя с помощью команды INCR.
  • user: all — Redis List для хранения всех идентификаторов пользователей
  • user: <id>: data — будет один ключ с этим шаблоном для каждого пользователя в системе. Эти ключи будут хешами;

Когда мы собираемся добавить нового пользователя в систему, мы будем обрабатывать три ключа, как мы видим на следующих шагах:

  1. Сначала мы получаем новый идентификатор пользователя, увеличивая значение user: ids key: INCR user: ids
  2. Затем мы добавляем его пользователю: весь список: lpush пользователь: все возвращенный идентификатор
  3. И добавьте информацию о пользователе в свой собственный хеш: HMSET user: <returnId>: значение поля данных.

Мы можем увидеть этот код в действии методом UserDAO.addUser:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
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;
    }

Объясняя приведенный выше код, сначала мы получаем новый идентификатор пользователя. Переменная jedis является атрибутом класса UserDAO, это экземпляр класса Jedis. Чтобы избежать трех сетевых обращений к серверу redis, мы используем концепцию Pipeline, поэтому при одном обращении к серверу redis мы добавим идентификатор пользователя в user: all list и информацию о пользователе в user: <id>: data хэш.

Команды, выполняемые экземпляром конвейера, будут выполняться на сервере redis после вызова pipe.sync () . Мы создали класс util для преобразования пользовательского объекта в Map <String, String> для хранения в хэше redis.

Чтобы увидеть детали пользователя, у нас есть метод в DAO для получения пользователя, который мы можем увидеть ниже:

1
2
3
4
5
6
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());
    }

Как мы видим, это простой метод, в основном мы вызываем команду HGETALL для извлечения всех полей из хэша. API-интерфейс Jedis возвращает его в виде карты, поэтому мы можем просто заполнить пользовательские свойства карты.

Для удаления пользователя мы создали метод ниже:

1
2
3
4
5
6
7
8
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;
    }

В методе ниже мы также используем концепцию конвейерной обработки, когда нам нужно удалить ключ Hash и идентификатор пользователя из списка user: all. Команда LREM удаляет значение из списка, ноль указывает на удаление всех вхождений этого значения в списке. В этом методе мы также используем возвращаемые значения команд, используя объекты Response, возвращаемые каждой командой. Мы можем использовать эти объекты только после вызова метода синхронизации .

Метод обновления очень прост, мы можем увидеть его ниже:

1
2
3
4
5
public User update(User user){
        String userInfoKey = Keys.USER_DATA.formated(String.valueOf(user.getId()));
        jedis.hmset(userInfoKey ,BeanUtil.toMap(user));
        return user;
    }

Это просто вызов HMSET, передающий карту со всеми атрибутами пользователя, которые должны быть обновлены в хэше redis.

Для составления списка пользователей нам также необходимо использовать конвейер. Redis не предоставляет команду HMGETALL, поэтому, чтобы получить всех пользователей с одним сетевым подключением, мы сделаем это по конвейеру.

Метод списка можно увидеть ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
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;
    }

В этом методе мы сначала получаем все идентификаторы пользователя из списка user: все с помощью команды LRANGE. После этого мы выполняем «HMGETALL» через конвейер, вызываем команду HGETALL для каждого пользователя, затем строим пользовательские объекты из возвращенных экземпляров Map.

В этом первом посте мы увидели, как взаимодействовать с сервером Redis с помощью API-интерфейса Jedis для хранения и получения информации о пользователе. Мы увидели концепцию и использование Pipeline . В следующем посте мы покажем, как использовать CDI для Dependency Injection и Servlet 3.0 для представления.