实战篇之基于二进制思想的用户标签系统(Mysql+SpringBoot2

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
注册配置 MSE Nacos/ZooKeeper,118元/月
云原生网关 MSE Higress,422元/月
简介: 实战篇之基于二进制思想的用户标签系统(Mysql+SpringBoot2

四:一起动手实现用户标签系统 - 底层标签读写组件的实现
4.1: 建立用户标签表SQL
``CREATE TABLEt_user_tag(user_idbigint NOT NULL DEFAULT -1 COMMENT '用户 id',tag_info_01bigint NOT NULL DEFAULT '0' COMMENT '标签记录字段',tag_info_02bigint NOT NULL DEFAULT '0' COMMENT '标签记录字段',tag_info_03bigint NOT NULL DEFAULT '0' COMMENT '标签记录字段',create_timedatetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时 间',update_timedatetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin
COMMENT='用户标签记录';

  ###4.2:service层接口
  ```package com.laoyang.provider.service;

import com.laoyang.constants.UserTagsEnum;

/**
 * @author:Kevin
 * @create: 2023-08-01 09:53
 * @Description:
 */

public interface IUserTagService {

    /**
     * 设置标签 只能设置成功一次
     *
     * @param userId
     * @param userTagsEnum
     * @return
     */
    boolean setTag(Long userId, UserTagsEnum userTagsEnum);
    /**
     * 取消标签
     *
     * @param userId
     * @param userTagsEnum
     * @return
     */
    boolean cancelTag(Long userId, UserTagsEnum userTagsEnum);
    /**
     * 是否包含某个标签
     *
     * @param userId
     * @param userTagsEnum
     * @return
     */
    boolean containTag(Long userId,UserTagsEnum userTagsEnum);
}

```package com.laoyang.provider.service.impl;

import com.laoyang.common.utils.ConvertBeanUtils;
import com.laoyang.constants.UserTagFieldNameConstants;
import com.laoyang.constants.UserTagsEnum;
import com.laoyang.dto.UserTagDTO;
import com.laoyang.framework.redis.key.UserProviderCacheKeyBuilder;
import com.laoyang.provider.dao.mapper.IUserTagMapper;
import com.laoyang.provider.dao.po.UserTagPO;
import com.laoyang.provider.service.IUserTagService;
import com.laoyang.usils.TagInfoUtils;
import jakarta.annotation.Resource;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.nio.charset.StandardCharsets;

/**

  • @author:Kevin
  • @create: 2023-08-01 09:54
  • @Description:
    */

public class UserTagServiceImpl implements IUserTagService {

@Resource
private IUserTagMapper userTagMapper;
@Resource
private RedisTemplate<String, String> redisTemplate;

private RedisTemplate<String, UserTagDTO> userTagDTORedisTemplate;

@Resource
private UserProviderCacheKeyBuilder cacheKeyBuilder;

@Override
public boolean setTag(Long userId, UserTagsEnum userTagsEnum) {
    boolean updateStatus = userTagMapper.setTag(userId,userTagsEnum.getFieldName(),userTagsEnum.getTag()) > 0;
    if (updateStatus){
        String redisKey = cacheKeyBuilder.buildtagInfoKey(userId);
        userTagDTORedisTemplate.delete(redisKey);
        return  true;
    }
    String key = cacheKeyBuilder.buildTagLockKey(userId);
    //TODO 分布式锁 实现多个线程之间对同一个资源的互斥访问,保证同一时间只有一个线程能够获取到锁并执行相应的操作
    String setNxResult = redisTemplate.execute(new RedisCallback<String>() {
        @Override
        public String doInRedis(RedisConnection connection) throws DataAccessException {
            RedisSerializer keySerializer = redisTemplate.getKeySerializer();
            RedisSerializer valueSerializer = redisTemplate.getValueSerializer();
            String result = (String) connection.execute("set", keySerializer.serialize(key),
                    valueSerializer.serialize("-1"),
                    "NX".getBytes(StandardCharsets.UTF_8),
                    "EX".getBytes(StandardCharsets.UTF_8),
                    "3".getBytes(StandardCharsets.UTF_8));
            return result;
        }
    });

    if (!"OK".equals(setNxResult)){
        return false;
    }

    UserTagPO userTagPO = userTagMapper.selectById(userId);
    if (userTagPO!=null){
        return false;
    }
    userTagPO = new UserTagPO();
    userTagPO.setUserId(userId);
    userTagMapper.insert(userTagPO);
    updateStatus = userTagMapper.setTag(userId,userTagsEnum.getFieldName(),userTagsEnum.getTag()) > 0;
    redisTemplate.delete(key);
    return updateStatus;
}

@Override
public boolean cancelTag(Long userId, UserTagsEnum userTagsEnum) {
    boolean cancleStatus = userTagMapper.cancelTag(userId,userTagsEnum.getFieldName(),userTagsEnum.getTag()) > 0;
    if (cancleStatus){
        return false;
    }
    String redisKey = cacheKeyBuilder.buildtagInfoKey(userId);
    userTagDTORedisTemplate.delete(redisKey);
    return true;
}

@Override
public boolean containTag(Long userId, UserTagsEnum userTagsEnum) {
    UserTagDTO userTagDTO = this.queryByUserId(userId);
    if (userTagDTO == null) {
        return false;
    }
    String queryFieldName = userTagsEnum.getFieldName();
    if
    (UserTagFieldNameConstants.TAG_INFO_01.equals(queryFieldName)) {
        return
                TagInfoUtils.isContain(userTagDTO.getTagInfo01(),
                        userTagsEnum.getTag());
    } else if
    (UserTagFieldNameConstants.TAG_INFO_02.equals(queryFieldName)) {
        return
                TagInfoUtils.isContain(userTagDTO.getTagInfo02(),
                        userTagsEnum.getTag());
    } else if
    (UserTagFieldNameConstants.TAG_INFO_03.equals(queryFieldName)) {
        return
                TagInfoUtils.isContain(userTagDTO.getTagInfo03(),
                        userTagsEnum.getTag());
    }
    return false;
}

/**
 * 从redis查询用户标签
 * @param userId
 * @return
 */
private UserTagDTO queryByUserId(Long userId){
    String redisKey = cacheKeyBuilder.buildtagInfoKey(userId);
    UserTagDTO userTagDTO = userTagDTORedisTemplate.opsForValue().get(redisKey);
    if (userTagDTO != null){
        return userTagDTO;
    }
    UserTagPO userTagPO = userTagMapper.selectById(userId);
    if (userTagPO == null){
        return null;
    }
    userTagDTO = ConvertBeanUtils.convert(userTagPO,UserTagDTO.class);
    userTagDTORedisTemplate.opsForValue().set(redisKey, userTagDTO);

    return userTagDTO;

}

}
```
说明:我们使用了redis作为缓存,mybatisplus, 并自行创建了redis业务主键生成工具类等等,会放在最后,先把核心代码呈现。这里说明下使用到了redis分布式实现

这段代码是使用RedisTemplate执行一个"set"命令,并设置了一些选项参数。下面对代码进行解释:

首先,通过redisTemplate.getKeySerializer()获取key的序列化器,通过redisTemplate.getValueSerializer()获取value的序列化器。
在RedisCallback的doInRedis方法中,通过RedisConnection的execute方法执行"set"命令。
参数中,keySerializer.serialize(key)将key序列化为字节数组,valueSerializer.serialize("-1")将value序列化为字节数组。
"NX".getBytes(StandardCharsets.UTF_8)表示设置NX选项,即只有在key不存在时才进行set操作。
"EX".getBytes(StandardCharsets.UTF_8)表示设置EX选项,即设置key的过期时间为3秒。
"3".getBytes(StandardCharsets.UTF_8)表示设置key的过期时间为3秒。
connection.execute方法返回的是一个Object类型的结果,需要将其转换为String类型并返回。 总体来说,这段代码的作用是在Redis中执行一个set命令,将key和value存储到Redis中,并设置了过期时间和NX选项,确保只有在key不存在时才进行set操作。

    当多个节点同时尝试执行set操作来设置同一个key时,只有一个节点能够成功设置,因为Redis中的set命令默认具有原子性。如果设置了NX选项,即只有在key不存在时才进行set操作,那么只有第一个节点能够成功设置该key,其他节点将无法设置。 通过利用这个特性,可以将某个共享资源对应的key作为锁的名称,多个节点试图通过set操作来竞争该锁。只有一个节点能够成功设置该锁的key,即获得了分布式锁。其他节点则在设置失败后,可以选择等待或者进行其他处理。 同时,为了避免因为某个节点获得锁后发生故障而导致锁一直无法释放,还可以为锁设置过期时间。当锁的持有者在一定时间后未能释放锁,锁将自动过期并被其他节点获取。 综上所述,通过使用Redis的set操作和一些选项参数,可以实现简单的分布式锁。多个节点可以通过竞争设置同一个key来获得锁,并通过设置过期时间来避免因为锁的持有者发生故障而导致锁一直无法释放。

————————————————

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
25天前
|
安全 关系型数据库 MySQL
如何将数据从MySQL同步到其他系统
【10月更文挑战第17天】如何将数据从MySQL同步到其他系统
143 0
|
9天前
|
分布式计算 关系型数据库 MySQL
SpringBoot项目中mysql字段映射使用JSONObject和JSONArray类型
SpringBoot项目中mysql字段映射使用JSONObject和JSONArray类型 图像处理 光通信 分布式计算 算法语言 信息技术 计算机应用
30 8
|
10天前
|
SQL 前端开发 关系型数据库
SpringBoot使用mysql查询昨天、今天、过去一周、过去半年、过去一年数据
SpringBoot使用mysql查询昨天、今天、过去一周、过去半年、过去一年数据
41 9
|
17天前
|
关系型数据库 MySQL Linux
Linux系统如何设置自启动服务在MySQL数据库启动后执行?
【10月更文挑战第25天】Linux系统如何设置自启动服务在MySQL数据库启动后执行?
63 3
|
24天前
|
自然语言处理 Java API
Spring Boot 接入大模型实战:通义千问赋能智能应用快速构建
【10月更文挑战第23天】在人工智能(AI)技术飞速发展的今天,大模型如通义千问(阿里云推出的生成式对话引擎)等已成为推动智能应用创新的重要力量。然而,对于许多开发者而言,如何高效、便捷地接入这些大模型并构建出功能丰富的智能应用仍是一个挑战。
94 6
|
1月前
|
存储 关系型数据库 MySQL
PACS系统 中 dicom 文件在mysql 8.0 数据库中的 存储和读取(pydicom 库使用)
PACS系统 中 dicom 文件在mysql 8.0 数据库中的 存储和读取(pydicom 库使用)
27 2
|
1月前
|
Ubuntu 关系型数据库 MySQL
Linux系统MySQL安装
【10月更文挑战第19天】本文介绍了在 Linux 系统上安装 MySQL 的步骤,包括安装前准备、安装 MySQL、启动 MySQL 服务、配置 MySQL 以及验证安装。适用于 Ubuntu/Debian 和 CentOS/Fedora 系统,提供了详细的命令示例。
149 1
|
1月前
|
Java 关系型数据库 MySQL
springboot学习五:springboot整合Mybatis 连接 mysql数据库
这篇文章是关于如何使用Spring Boot整合MyBatis来连接MySQL数据库,并进行基本的增删改查操作的教程。
64 0
springboot学习五:springboot整合Mybatis 连接 mysql数据库
|
18天前
|
关系型数据库 MySQL Java
SpringBoot项目中mysql字段映射使用JSONObject和JSONArray类型
SpringBoot项目中mysql字段映射使用JSONObject和JSONArray类型
22 0
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
143 1