How to implement the like function in springboot

Keywords: Programming Redis Database Spring

In information projects, we often encounter the function implementation of article comment and comment. However, these functions appear frequently in projects. If you operate the database directly, you will have too much pressure on the database. How to solve this problem?

redis cache

The first thing we think about is to add a caching mechanism. Users' likes can be placed in Redis and then persisted to the database.

Liking and canceling liking are high-frequency operations. If the database is read and written every time, a large number of operations will affect the performance of the database, so caching is needed

actual combat

Knowing the solution, we're going to test the results.

There is no need to introduce the redis installation. Everyone can install and run it. Let's talk about how spring boot solves this problem.

Import dependency

Add cache annotation @ EnableCaching to startup class

Next, we need to configure redis

@Configuration
public class RedisConfig {
 
    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<String, Object> redisTemplate(
            RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
 
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
 
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(redisConnectionFactory);
        template.setKeySerializer(jackson2JsonRedisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashKeySerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
 
 
    @Bean
    @ConditionalOnMissingBean(StringRedisTemplate.class)
    public StringRedisTemplate stringRedisTemplate(
            RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

After the basic environment is built, we need to do the redis tool class.

redis storage format

Now we know the redis cache, but how can we store likes in redis. Redis supports a lot of data formats. We need to record the likes and dislikes, and the likes status.

Because of the need to record the likes and the likes, as well as the likes status (likes and cancels likes), it is also necessary to take out all likes data in Redis at a fixed time interval, and analyze the best Hash data format in Redis.

We have determined the storage mode of hash, and then we will imitate the user's likes to store data.

If you click like, the stored key is: likedUserId::likedPostId, and the corresponding value is 1. Cancel liking. The stored key is: likedUserId::likedPostId. The corresponding value is 0. When fetching data, you can cut the key with:: to get two IDS, which is also very convenient.

Now the main implementation method is to periodically persist from the cache to the database from redis.

@Override
    public List<UserLike> getLikedDataFromRedis() {
        Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_KEY_USER_LIKED, ScanOptions.NONE);
        List<UserLike> list = new ArrayList<>();
        while (cursor.hasNext()){
            Map.Entry<Object, Object> entry = cursor.next();
            String key = (String) entry.getKey();
            //Separate likedUserId, likedPostId
            String[] split = key.split("::");
            String likedUserId = split[0];
            String likedPostId = split[1];
            Integer value = (Integer) entry.getValue();
 
            //Assemble as UserLike object
            UserLike userLike = new UserLike(likedUserId, likedPostId, value);
            list.add(userLike);
 
            //Delete from Redis after saving to list
            redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED, key);
        }
 
        return list;
    }
 
    @Override
    public List<LikedCountDTO> getLikedCountFromRedis() {
        Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, ScanOptions.NONE);
        List<LikedCountDTO> list = new ArrayList<>();
        while (cursor.hasNext()){
            Map.Entry<Object, Object> map = cursor.next();
            //Store likes in LikedCountDT
            String key = (String)map.getKey();
            LikedCountDTO dto = new LikedCountDTO(key, (Integer) map.getValue());
            list.add(dto);
            //Delete this record from Redis
            redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, key);
        }
        return list;
    }

These two methods can be used to record the likes and count the likes in the database. In fact, redis is mainly to realize the cache function of praise and praise times.

But there is a problem. If the customer refreshes the page after clicking like and redis is not persisted in the database, the user will feel that clicking like is invalid and continue to click like. It can lead to a bad experience. Because of the influence of timer, it will bring this kind of trouble. We have better solutions to share.

All of the above are my opinions. Welcome to put forward criticism and guidance. Let's discuss together!

Posted by djanim8 on Mon, 06 Jan 2020 10:42:02 -0800