如何使用位运算的数据字典

场景举例

日常有很多项目,数据库中都有表示“多选状态标识”的字段。在这里用我们项目中的一个例子进行说明一下:

  • 示例一:
    表示某个商家是否支持多种会员卡打折(如有金卡、银卡、其他卡等),项目中的以往的做法是:在每条商家记录中为每种会员卡建立一个标志位字段。如图:
    如何使用位运算的数据字典

用多字段来表示“多选标识”存在一定的缺点:首先这种设置方式很明显不符合数据库设计第一范式,增加了数据冗余和存储空间。再者,当业务发生变化时,不利于灵活调整。比如,增加了一种新的会员卡类型时,需要在数据表中增加一个新的字段,以适应需求的变化。

 - 改进设计:标签位flag设计
二进制的“位”本来就有表示状态的作用。可以用各个位来分别表示不同种类的会员卡打折支持:
如何使用位运算的数据字典
这样,“MEMBERCARD”字段仍采用整型。当某个商家支持金卡打折时,则保存“1(0001)”,支持银卡时,则保存“2(0010)”,两种都支持,则保存“3(0011)”。其他类似。表结构如图:
如何使用位运算的数据字典

我们在编写SQL语句时,只需要通过“位”的与运算,就能简单的查询出想要数据。通过这样的处理方式既节省存储空间,查询时又简单方便。

//查询支持金卡打折的商家信息:  
select * from factory where MEMBERCARD & b'0001';
// 或者:  
select * from factory where MEMBERCARD & 1;
  
// 查询支持银卡打折的商家信息:  
select * from factory where MEMBERCARD & b'0010';
// 或者:  
select * from factory where MEMBERCARD & 2;

二进制( 位运算)枚举

可以通过@Dict注解设置数据字典的bit属性或者实现BitEnum接口来标识该枚举值为2的次幂。二进制枚举最大的区别在于值的序列化和反序列化方式是不一样的。

位运算的枚举定义示例

import pro.shushi.pamirs.meta.annotation.Dict;
import pro.shushi.pamirs.meta.common.enmu.BitEnum;

@Dict(dictionary = ClientTypeEnum.DICTIONARY, displayName = "客户端类型枚举", summary = "客户端类型枚举")
public enum ClientTypeEnum implements BitEnum {

    PC(1L, "PC端", "PC端"),
    MOBILE(1L << 1, "移动端", "移动端"),
    ;

    public static final String DICTIONARY = "base.ClientTypeEnum";

    private final Long value;
    private final String displayName;
    private final String help;

    ClientTypeEnum(Long value, String displayName, String help) {
        this.value = value;
        this.displayName = displayName;
        this.help = help;
    }

    @Override
    public Long value() {
        return value;
    }

    @Override
    public String displayName() {
        return displayName;
    }

    @Override
    public String help() {
        return help;
    }
}

使用方法示例

  • API: addTo 和 removeFrom

    List<ClientTypeEnum> clientTypes = module.getClientTypes();
    // addTo
    ClientTypeEnum.PC.addTo(clientTypes);
    // removeFrom
    ClientTypeEnum.PC.removeFrom(clientTypes);
  • 在查询条件中的使用

    List<Menu> moduleMenus = new Menu().queryListByWrapper(menuPage, LoaderUtils.authQuery(wrapper).eq(Menu::getClientTypes, ClientTypeEnum.PC));

Oinone社区 作者:nation原创文章,如若转载,请注明出处:https://doc.oinone.top/backend/4757.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

(0)
nation的头像nation数式员工
上一篇 2023年11月24日 pm3:45
下一篇 2023年11月27日 am11:14

相关推荐

  • 蓝绿发布

    背景 应用程序升级面临最大挑战是新旧业务切换,将软件从测试的最后阶段带到生产环境,同时要保证系统不间断提供服务。长期以来,业务升级渐渐形成了几个发布策略:蓝绿发布、灰度发布和滚动发布,目的是尽可能避免因发布导致的流量丢失或服务不可用问题。 本文主要介绍Oinone平台如何实现蓝绿发布。蓝绿发布:项目逻辑上分为AB组,在项目系统时,首先把A组从负载均衡中摘除,进行新版本的部署。B组仍然继续提供服务。 需求 统一权限统一登录信息不同业务数据 实现方案 首先需要两个环境并统一流量入口,这里使用Nginx配置负载均衡:nginx如何配置后端服务的负载均衡 统一权限配置 在蓝绿环境添加不同的redis前缀 spring: redis: prefix: xxx 在蓝绿环境的修改AuthRedisTemplate Bean,利用setKeySerializer去掉redis前缀。 可以使用@Profile注解指定仅线上环境生效。 @Configuration // @Profile("prod") public class AuthRedisTemplateConfig { @Bean(AuthConstants.REDIS_TEMPLATE_BEAN_NAME) public AuthRedisTemplate<?> authRedisTemplate( RedisConnectionFactory redisConnectionFactory, PamirsStringRedisSerializer pamirsStringRedisSerializer ) { AuthRedisTemplate<Object> template = new AuthRedisTemplate<Object>(redisConnectionFactory, pamirsStringRedisSerializer) { @Override public void afterPropertiesSet() { // 重写 key serializer,去掉前缀隔离 this.setKeySerializer(new PamirsStringRedisSerializer(null)); super.afterPropertiesSet(); } }; return template; } } 统一登录 在蓝绿环境自定义实现pro.shushi.pamirs.user.api.spi.UserCacheApi SPI,去除redis前缀 package pro.shushi.pamirs.top.core.spi; import com.alibaba.fastjson.JSON; import com.google.common.collect.Sets; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.Order; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.core.SessionCallback; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import pro.shushi.pamirs.auth.api.cache.redis.AuthRedisTemplate; import pro.shushi.pamirs.meta.api.dto.model.PamirsUserDTO; import pro.shushi.pamirs.meta.api.dto.protocol.PamirsRequestVariables; import pro.shushi.pamirs.meta.api.session.PamirsSession; import pro.shushi.pamirs.meta.common.spi.SPI; import pro.shushi.pamirs.user.api.cache.UserCache; import pro.shushi.pamirs.user.api.configure.UserConfigure; import pro.shushi.pamirs.user.api.spi.UserCacheApi; import java.net.URI; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; @Order(1) @Component @SPI.Service("MyUserCache") public class MyUserCache implements UserCacheApi { private static final Set<String> DEFAULT_FILTER_URIS = Collections.singleton("/pamirs/message"); @Autowired private AuthRedisTemplate redisTemplate; @Override public PamirsUserDTO getSessionUser(String key) { String objectValue = getUserCacheAndRenewed(key); if (StringUtils.isNotBlank(objectValue)) { return JSON.parseObject(objectValue, PamirsUserDTO.class); } return null; } @Override public void setSessionUser(String key, PamirsUserDTO user, Integer expire) { user.setPassword(null); expire = getExpire(expire); redisTemplate.opsForValue().set(key.replace("'", " "), JSON.toJSONString(user), expire, TimeUnit.SECONDS); // 当前的实现是一个user可以在多个客户端登录,需要在管理端修改user权限后强制清除掉该用户已登录的session,所以需要记录uid对应所有已登录的sessionId String userRelSessionKey = UserCache.createUserRelSessionKey(user.getUserId()); redisTemplate.opsForSet().add(userRelSessionKey, key); redisTemplate.expire(userRelSessionKey,…

    2025年9月18日
    71400
  • Oinone远程调用链路源码分析

    前提 源码分析版本是 5.1.x版本 概要 在服务启动时,获取注解REMOTE的函数,通过dubbo的泛化调用发布。在调用函数时,通过dubbo泛化调用获取结果。 注册服务者 在spring 启动方法installOrLoad中初始化 寻找定义REMOTE的方法 组装dubbo的服务配置 组装服务对象实现引用,内容如下,用于注册 调用前置处理 放信息到SessionApi 函数调用链追踪,放到本地TransmittableThreadLocal 从redis中获取到的数据进行反序列化并存在到本地的线程里 Trace信息,放一份在sessionApi中 和ThreadLocal 调用函数执行 返回数据转成特定格式 通过线程组调用dubbo的ServiceConfig.export 服务发布 时序图 源码分析 根据条件判断,确定向dubbo进行服务发布RemoteServiceLoader public void publishService(List<FunctionDefinition> functionList,Map<String,Runnable> isPublished) { // 因为泛化接口只能控制到namespace,控制粒度不能到fun级别,这里进行去重处理 Map<String, Function> genericNamespaceMap = new HashMap<>(); for (FunctionDefinition functionDefinition : functionList) { Function function = new Function(functionDefinition) try { //定义REMOTE, 才给予远程调用 if (FunctionOpenEnum.REMOTE.in(function.getOpen()) && !ClassUtils.isInterface(function.getClazz())) { genericNamespaceMap.putIfAbsent(RegistryUtils.getRegistryInterface(function), function); } } catch (PamirsException e) { } } // 发布远程服务 for (String namespace : genericNamespaceMap.keySet()) { Function function = genericNamespaceMap.get(namespace); if(isPublished.get(RegistryUtils.getRegistryInterface(function)) == null){ // 发布,注册远程函数服务,底层使用dubbo的泛化调用 Runnable registryTask = () -> remoteRegistry.registryService(function); isPublished.put(RegistryUtils.getRegistryInterface(function),registryTask); }else{ } } } 构造ServiceConfig方法,设置成泛化调用,进行发布export()DefaultRemoteRegistryComponent public void registryGenericService(String interfaceName, List<MethodConfig> methods, String group, String version, Integer timeout, Integer retries) { …. try { ServiceConfig<GenericService> service = new ServiceConfig<>(); // 服务接口名 service.setInterface(interfaceName); // 服务对象实现引用 service.setRef(genericService(interfaceName)); if (null != methods) { service.setMethods(methods); } // 声明为泛化接口 service.setGeneric(Boolean.TRUE.toString()); // 基础元数据 constructService(group, version, timeout, retries, service); service.export(); } catch (Exception e) { ….. } } // 服务对象实现引用 private GenericService genericService(String interfaceName) { return (method, parameterTypes, args) -> { PamirsSession.clear(); Function function = Objects.requireNonNull(PamirsSession.getContext()).getFunction(RegistryUtils.getFunctionNamespace(method), RegistryUtils.getFunctionFun(method)); if (log.isDebugEnabled()) { log.debug("interfaceName: " + interfaceName + ",…

    2024年9月4日
    1.5K00
  • 【后端】项目开发后端知识要点地图

    大类 明细 文档链接 平台基础 如何开发Action,理解前后端协议 如何开发Action,理解前后端协议 CDN配置及文件操作相关 OSS(CDN)配置和文件系统的一些操作 MINIO无公网访问地址下OSS的配置 MINIO无公网访问地址下OSS的配置 分库分表与自定义分表规则 分库分表与自定义分表规则 Oinone引入搜索引擎(增强模型) Oinone引入搜索引擎(增强模型) 引入搜索/增强模型Channel)常见问题解决办法 引入搜索(增强模型Channel)常见问题解决办法 框架之MessageHub(信息提示) 框架之MessageHub(信息提示) DsHint和BatchSizeHint的使用 DsHint(指定数据源)和BatchSizeHint(查询批次数量) Oinone连接外部数据源方案 Oinone连接外部数据源方案 如何自定义SQL(Mapper)语句 如何自定义SQL(Mapper)语句 IWrapper、QueryWrapper和LambdaQueryWrapper使用 IWrapper、QueryWrapper和LambdaQueryWrapper使用 如何在代码中使用自增ID、手动方式获取CODE 如何在代码中使用自增ID、手动方式获取CODE 函数之触发与定时配置和示例 函数之触发与定时配置和示例 函数之异步执行 函数之异步执行 查询时自定义排序字段和排序规则 查询时自定义排序字段和排序规则 非存储字段搜索 非存储字段搜索,适应灵活的搜索场景 枚举/二进制枚举/多值枚举 如何使用位运算的数据字典 全局首页及应用首页配置方法(homepage) 全局首页及应用首页配置方法(homepage) 缓存连接由Jedis切换为Lettuce 缓存连接由Jedis切换为Lettuce GraphQL请求:后端接口实现逻辑解析 GraphQL请求:后端接口实现逻辑解析 Nacos支持 Nacos作为注册中心 Oinone项目引入Nacos作为注册中心 Nacos作为配置中心 Oinone项目引入Nacos作为配置中心 Nacos做为注册中心调用SpringCloud服务 Nacos做为注册中心调用SpringCloud服务 分布式相关 如何构建分布式项目 Oinone如何支持构建分布式项目 构建分布式项目一些要点(dubbo日志关闭等) Oinone构建分布式项目一些注意点 信创支持 后端部署使用达梦数据库 【达梦】后端使用达梦数据库 后端部署使用PostgreSQL数据库 【PostgreSQL】后端使用PostgreSQL数据库 后端部署使用OpenGauss数据库 【OpenGauss】后端使用OpenGauss数据库 后端部署使用MSSQL数据库(SQLServer) 【MSSQL】后端部署使用MSSQL数据库(SQLServer) 东方通Web和Tomcat部署Oinone项目 东方通Web和Tomcat部署Oinone项目 常见扩展 如何增加用户中心的菜单 如何增加用户中心的菜单 导入导出 如何批量导入 如何批量导入 如何支持多Excel多个Sheet导入功能 如何支持多Excel多个Sheet导入功能 如何自定义Excel导入功能 如何自定义Excel导入功能 如何自定义Excel导出功能 如何自定义Excel导出功能 如何自定义表达式 如何自定义表达式 登录扩展 对接外部SSO Oinone登录扩展:对接SSO(4.7.8及之后的版本) 自定义占位符 自定义RSQL占位符及在权限中使用 自定义RSQL占位符(placeholder)及在权限中使用 自定义数据权限拦截处理 自定义数据权限拦截处理 设计器公共 后端无代码设计器Jar包启动方法 后端无代码设计器Jar包启动方法 界面设计器 页面跳转时增加跳转参数 页面跳转时增加跳转参数 界面设计器的导入导出 界面设计器的导入导出 流程设计器 项目中工作流引入和流程触发 项目中工作流引入和流程触发 流程扩展自定义函数示例代码汇总 工作流-流程扩展自定义函数示例代码汇总 工作流-流程代办等页面自定义 工作流-流程代办等页面自定义 审核撤回/回退/拒绝钩子使用 工作流审核撤回/回退/拒绝钩子使用 流程设计器的导入导出 流程设计器的导入导出 如何添加工作流运行时依赖 如何添加工作流运行时依赖 数据可视化 项目中图表设计器引入 数据可视化-项目中数据可视化的实现引入 自定义图表模版 数据可视化中图表的低无一体 图表设计器数据获取示例 数据可视化-数据可视化数据获取示例 如何添加数据可视化运行时依赖 如何添加数据可视化运行时依赖 图表设计器的设计数据导入导出 图表设计器的设计数据导入导出

    2024年5月21日
    2.3K00
  • 如何把平台功能菜单加到项目中?

    概述 在使用Oinone低代码平台进行项目开发的过程中,会存在把平台默认提供的菜单加到自己的项目中。这篇文章介绍实现方式,以角色管理为例。 1. 低代码情况 即项目是通过后端代码初始化菜单的方式。 通常在 XXXMenu.java类通过@UxMenu注解的方式,代码片段如下: 此种情况与添加项目本地的菜单无区别,具体代码: @UxMenu(value = "账号管理", icon = "icon-yonghuguanli") class AccountManage { @UxMenu("用户管理") @UxRoute(value = CustomerCompanyUserProxy.MODEL_MODEL, title = "用户管理") class CompanyUserManage { } @UxMenu("角色管理") @UxRoute(value = AuthRole.MODEL_MODEL, module = AdminModule.MODULE_MODULE, title = "角色管理") class RoleManage { } @UxMenu("操作日志") @UxRoute(value = OperationLog.MODEL_MODEL, module = AdminModule.MODULE_MODULE/***菜单所挂载的模块**/) class OperateLog { } } 2. 无代码情况 在界面设计器中,新建菜单–>选择绑定已有页面,进行发布即可。

    2024年4月19日
    1.8K00
  • 函数之触发与定时配置和示例

    异步任务总体介绍 函数的触发和定时在很多场景中会用到,也是一个oinone的基础能力。比如我们的流程产品中在定义流程触发时就会让用户选择模型触发还是时间触发,就是用到了函数的触发与定时能力。 触发任务TriggerTaskAction 触发任务的创建,使用sql-record模块监听mysql的binlog事件,通过rocketmq发送变更数据消息,收到MQ消息后,创建TriggerAutoTask。 触发任务的执行,使用TBSchedule拉取触发任务后,执行相应函数。 项目中引入依赖 1、项目的API工程引入依赖pamirs-core-trigger模块 <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-api</artifactId> </dependency> 2、DemoModule在模块依赖定义中增加@Module(dependencies={TriggerModule.MODULE_MODULE}) @Component @Module( name = DemoModule.MODULE_NAME, displayName = "oinoneDemo工程", version = "1.0.0", dependencies = {ModuleConstants.MODULE_BASE, CommonModule.MODULE_MODULE, UserModule.MODULE_MODULE, TriggerModule.MODULE_MODULE} ) @Module.module(DemoModule.MODULE_MODULE) @Module.Advanced(selfBuilt = true, application = true) @UxHomepage(PetShopProxy.MODEL_MODEL) public class DemoModule implements PamirsModule { ……其他代码 } 3、项目的boot工程引入依赖 <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-core</artifactId> </dependency> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-bridge-tbschedule</artifactId> </dependency> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-sql-record-core</artifactId> </dependency> yml文件修改(applcation-xxx.yml) a. 修改pamris.event.enabled和pamris.event.schedule.enabled为trueb. pamirs_boot_modules增加启动模块:trigger、sql_record pamirs: record: sql: #改成自己路径 store: /opt/pamirs/logs … event: enabled: true schedule: enabled: true rocket-mq: namesrv-addr: 127.0.0.1:9876 boot: init: true sync: true modules: – base -…… – trigger – sql_record -…… 新建触发任务 新建PetTalentTrigger类,当PetTalent模型的数据记录被新建时触发系统做一些事情 package pro.shushi.pamirs.demo.core.trigger; import pro.shushi.pamirs.demo.api.model.PetTalent; import pro.shushi.pamirs.meta.annotation.Fun; import pro.shushi.pamirs.meta.annotation.Function; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.trigger.annotation.Trigger; import pro.shushi.pamirs.trigger.enmu.TriggerConditionEnum; @Fun(PetTalent.MODEL_MODEL) @Slf4j public class PetTalentTrigger { @Function @Trigger(displayName = “PetTalent创建时触发”,name = “PetTalent#Trigger#onCreate”,condition = TriggerConditionEnum.ON_CREATE) public PetTalent onCreate(PetTalent data){ log.info(data.getName() + “,被创建”); //可以增加逻辑 return data; } } 定时任务 定时任务是一种非常常见的模式,这里就不介绍概念了,直接进入示例环节 新建PetTalentAutoTask实现ScheduleAction getInterfaceName()需要跟taskAction.setExecuteNamespace定义保持一致,都是函数的命名空间 taskAction.setExecuteFun("execute");跟执行函数名“execute”一致 TaskType需配置为CYCLE_SCHEDULE_NO_TRANSACTION_TASK,把定时任务的schedule线程分开,要不然有一个时间长的任务会导致普通异步或触发任务全部延时。 package pro.shushi.pamirs.demo.core.task; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import pro.shushi.pamirs.core.common.enmu.TimeUnitEnum; import pro.shushi.pamirs.demo.api.model.PetTalent; import pro.shushi.pamirs.meta.annotation.Fun; import pro.shushi.pamirs.meta.annotation.Function; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.domain.fun.FunctionDefinition; import pro.shushi.pamirs.middleware.schedule.api.ScheduleAction; import pro.shushi.pamirs.middleware.schedule.common.Result; import pro.shushi.pamirs.middleware.schedule.domain.ScheduleItem; import pro.shushi.pamirs.middleware.schedule.eunmeration.TaskType; import pro.shushi.pamirs.trigger.enmu.TriggerTimeAnchorEnum; import pro.shushi.pamirs.trigger.model.ScheduleTaskAction; import pro.shushi.pamirs.trigger.service.ScheduleTaskActionService; @Slf4j @Component @Fun(PetTalent.MODEL_MODEL) public class PetTalentAutoTask implements…

    2024年5月25日
    1.5K00

Leave a Reply

登录后才能评论