Java实现自定义字段

news2025/1/21 1:00:11

Java自定义字段

小黄最近在工作中遇到一个比较有意思的需求,在此跟大家分享一下

需求

这个需求是这样的,用户在添加表单时,除了一些固定信息填入之外,还可以自定义一些字段填入,例如一个用户表,默认情况下姓名和年龄是所有人都要填入的,除此之外,比如小红想多添加一个字段身高,小白想多添加一个字段体重…所以在用户填入表单时,就需要可拓展的自定义字段。

小红存在数据库中的字段如下:

{
    "name":"小红",
    "age":18,
    "height":180
}

小白存在数据库中的字段如下:

{
    "name":"小红",
    "age":18,
    "weight":65
}

分析

其实接口是非常简单的,就是一套CRUD接口,所以我们来分析一下如何解决这个问题。

首先我们先和前端约定好,如何传一个表单格式,关于自定义字段,小黄是这样定义的

  • en:表示字段名
  • cn:表示字段中文名
  • type:表示字段类型,因为前端需要靠这个渲染页面,和前端约定
  • required:表示是否必填项
  • value:表示字段值
[       
    {
        "en": "name",
        "cn": "用户名",
        "type": 1,
        "required":1,
        "value": "小红"
    }
]

思路一

使用MySQL,创建一张User表,表结构如下设计

  • ext:表示用户自定义字段内容,使用json格式存储

image-20230515085559691

使用这个方法可以将用户填入的多个自定义字段都存入在ext中,但是由于所有字段都挤在了一起,对于后续的可拓展性不是很高。

思路二

使用Mongo存储,Mongo是一个文档型数据库,每一条数据可以使用不同的字段存储,这对于我们这个需求来说就再适合不过了。

我们需要花两张表来存储信息,status用于表示逻辑删除

  • user表:用于存储字段对应的值,存储格式如下

    {
        "_id": "98c6db9985c8481a8d80f3555b6db326",
        "status": NumberInt("0"),
        "name": "yellowstar459",
        "age": NumberInt("18"),
        "height": 180.5,
        "happy": "唱跳rap"
    }
    
  • user_ext表:用于存储字段信息,存储格式如下,每个值分别对应cn,type,required

    {
        "_id": "98c6db9985c8481a8d80f3555b6db326",
        "status": NumberInt("0"),
        "name": "用户名,1,1",
        "age": "年龄,2,1",
        "height": "身高,3,1",
        "happy": "爱好,1,1"
    }
    

实现

这篇文章仅仅分享一下思路,代码写的都不是非常规范,见谅。

新增

定义一个接收前端参数的对象

@Data
public class Custom implements Serializable {
    private static final long serialVersionUID = 1L;

    private String en;

    private String cn;

    private Integer type;

    //1:必填,0:非必填
    private Integer required;

    private Object value;
}

编写业务类

/**
 * userExt对应
 * cn,type,required
 * status : 作为逻辑删除
 * @param list
 */
@PostMapping
public void add(@RequestBody List<Custom> list) {
    Document user = new Document();
    Document userExt = new Document();
    String id = UUID.randomUUID().toString().replaceAll("-", "");
    user.append("_id", id);
    user.append("status", 0);
    userExt.append("_id", id);
    userExt.append("status", 0);
    for (Custom custom : list) {
        user.append(custom.getEn(), custom.getValue());
        userExt.append(custom.getEn(), custom.getCn() + "," + custom.getType() + "," + custom.getRequired());
    }
    mongoTemplate.save(user, "user");
    mongoTemplate.save(userExt, "user_ext");
}

测试

我这边和前端约定type值

  • 1:字符串
  • 2:整型
  • 3:浮点型

通过postman发送如下请求

[       
    {
        "en": "name",
        "cn": "用户名",
        "type": 1,
        "required":1,
        "value": "yellowstar"
    },
    {
        "en": "age",
        "cn": "年龄",
        "type": 2,
        "required":1,
        "value": 18
    },
    {
        "en": "height",
        "cn": "身高",
        "type": 3,
        "required":1,
        "value": 180.5
    },
    {
        "en": "happy",
        "cn": "爱好",
        "type": 1,
        "required":1,
        "value": "唱跳rap"
    }
]

存入Mongo的数据分别为

image-20230515091648485

根据id查询

返回类

返回类需要一个id,在添加时将两张表的id设为相同

@Data
public class CustomVo implements Serializable {
    private static final long serialVersionUID = 1L;

    private String _id;

    private List<Custom> list;
}

业务类

@GetMapping("/{id}")
public CustomVo getById(@PathVariable(value = "id") String id) {
    Document user = mongoTemplate.findOne(Query.query(Criteria.where("_id").is(id).and("status").is(0)), Document.class, "user");
    Document userExt = mongoTemplate.findOne(Query.query(Criteria.where("_id").is(id).and("status").is(0)), Document.class, "user_ext");
    if (Objects.isNull(user) || Objects.isNull(userExt)) {
        throw new RuntimeException("数据不存在");
    }
    CustomVo customVo = new CustomVo();
    customVo.set_id(user.getString("_id"));
    List<Custom> list = new ArrayList<>();
    for (Map.Entry<String, Object> entry : user.entrySet()) {
        if ("_id".equals(entry.getKey()) || "status".equals(entry.getKey())) {
            continue;
        }
        Custom custom = new Custom();
        custom.setEn(entry.getKey());
        custom.setValue(entry.getValue());
        String ext = userExt.getString(entry.getKey());
        String[] split = ext.split(",");
        custom.setCn(split[0]);
        custom.setType(Integer.valueOf(split[1]));
        custom.setRequired(Integer.valueOf(split[2]));
        list.add(custom);
    }
    customVo.setList(list);
    return customVo;
}

更新

@PutMapping
public void update(@RequestBody CustomVo vo) {
    Document user = new Document();
    Document userExt = new Document();
    user.append("_id", vo.get_id());
    user.append("status", 0);
    userExt.append("_id", vo.get_id());
    userExt.append("status", 0);
    for (Custom custom : vo.getList()) {
        user.append(custom.getEn(), custom.getValue());
        userExt.append(custom.getEn(), custom.getCn() + "," + custom.getType() + "," + custom.getRequired());
    }
    Update userUpdate = Update.fromDocument(user);
    Update userExtUpdate = Update.fromDocument(userExt);
    mongoTemplate.updateFirst(Query.query(Criteria.where("_id").is(vo.get_id()).and("status").is(0)), userUpdate, "user");
    mongoTemplate.updateFirst(Query.query(Criteria.where("_id").is(vo.get_id()).and("status").is(0)), userExtUpdate, "user_ext");
}

删除

只需要修改status状态即可

@DeleteMapping("/{id}")
public void delete(@PathVariable(value = "id") String id) {
    Update update = Update.update("status", 1);
    mongoTemplate.updateFirst(Query.query(Criteria.where("_id").is(id).and("status").is(0)), update, "user");
    mongoTemplate.updateFirst(Query.query(Criteria.where("_id").is(id).and("status").is(0)), update, "user_ext");
}

列表

一般情况下,列表中肯定只显示一些通用的字段,目前只需要返回nameage即可

实体类

@Data
public class User {
    private String name;
    private Integer age;
}

业务逻辑

@GetMapping
public List<User> list(){
    Criteria criteria = Criteria.where("status").is(0);
    List<User> users = mongoTemplate.find(Query.query(criteria), User.class, "user");
    return users;
}

以上就是小黄的思路分析啦,有更好方法的小伙伴可以尽情提出想法,欢迎评论

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/527527.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

集文字查询、语音识别、拍照识别的垃圾分类系统,多端合一

一、开源项目简介 垃圾识别精灵 是一个 基于 uni-app 开发 微信小程序&#xff0c;使用 SpringBoot2 搭建后端服务&#xff0c;使用 Swagger2 构建 Restful 接口文档&#xff0c;实现了 文字查询、语音识别、图像识别其垃圾分类的功能。 这是一款个人开发的微信小程序&#x…

Python系列之变量和运算符

目录 一、变量 1.1 什么是变量 1.2 变量的特点 1.3 作用域 1.4 变量的命名规则 1.5 变量的定义和使用 1. 变量的定义 2. 两个变量值的交换 1.6 基本数据类型分类 1.7 数据类型转换 1.8 Python是解释型的强类型动态语言 1.8.1 解释型 1.8.2 强类型 1.8.2 动态语言…

Vue3系列——Pinia状态管理库

目录 Pinia 安装、创建 Store 定义Store Option Store Setup Store 使用Store storeToRefs action异步实现 Pinia Pinia是Vue的专属状态管理库&#xff0c;它允许跨组件或页面共享状态&#xff0c;实现和Vuex一样的数据共享&#xff0c;是Vuex状态管理工具的替代品。…

【直播回顾】AIGC产业研究报告2023图像生成篇报告解读

易观&#xff1a;5月9日&#xff0c;易观高级分析师陈晨带来了《AIGC产业研究报告2023图像生成篇》的报告解读&#xff0c;错过直播的朋友敬请观看回顾&#xff01; 图像作为人工智能内容生成的一种模态&#xff0c;一直在AIGC领域中扮演着重要角色&#xff0c;由于图像生成应用…

1135 Is It A Red-Black Tree(超详细注释+46行代码)

1135 Is It A Red-Black Tree 分数 30 全屏浏览题目 切换布局 作者 CHEN, Yue 单位 浙江大学 There is a kind of balanced binary search tree named red-black tree in the data structure. It has the following 5 properties: (1) Every node is either red or blac…

【C++】类和对象(中)---拷贝构造函数、赋值运算符重载

个人主页&#xff1a;平行线也会相交&#x1f4aa; 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【C之路】&#x1f48c; 本专栏旨在记录C的学习路线&#xff0c;望对大家有所帮助&#x1f647;‍ 希望我们一起努力、成长&…

MySQL之MHA高可用集群及故障切换

目录 一、MHA概述1.1 MHA 的组成1.2 MHA 的特点1.3 MHA的工作原理1.4 故障切换时MHA会做什么 二、搭建MySQLMHA2.1 实验思路2.2 实验环境、安装包2.3 服务搭建2.3-1 所有服务器挂壁防火墙和安全增强2.3-2 修改 Master、Slave1、Slave2 节点的主机名2.3-3 修改三台MySQL服务器主…

『新星计划 • 数据结构与算法』数据结构学习指南以及在校招中的重要性

文章目录 &#x1f490;文章导读&#x1f490;导师自我介绍&#x1f490;数据结构与算法的重要性&#x1f513;在岗位招聘中&#x1f513;在校招笔试中&#x1f513;在校招面试中&#x1f513;在未来工作中 &#x1f490;数据结构与算法如何学习⤴️学习路线⤵️学习方法↪️几…

tinyWebServer 学习笔记——一、半同步半反应堆线程池

文章目录 一、基础知识1. 服务器编程基本框架2. 五种 I/O 模型3. 事件处理模式4. 半同步/半反应堆模式5. 线程池 二、代码解析1. 线程池类定义2. 线程池创建与回收3. 向请求队列中添加任务4. 线程处理函数5. run 执行任务 参考文献 一、基础知识 1. 服务器编程基本框架 服务器…

软件测试 自动化测试selenium篇(一)

目录 一、什么是自动化测试 单元测试 接口自动化 UI自动化 二、如何实施自动化测试 自动化测试需要了解的技能 三、selenium介绍 webdriver的工作原理&#xff1a; 四、SeleniumJava环境搭建 验证环境是否搭建成功 创建java项目&#xff0c;添加pom文件中添加依赖 常见问…

一些使用树莓派遇到的问题

在长达两个月的实习期&#xff0c;白天去公司实习&#xff0c;晚上回到实验室学习树莓派。终于简单的入门了。来做个总结。 目录 1. wifi问题 树莓派之wifi连接 2.vnc树莓派桌面问题 3.安装库或者下载东西时&#xff0c;域名无法解析。 4.进行安装或者操作时&#xff0c…

【Java多线程编程】解决线程的不安全问题之volatile关键字

目录 1. 造成线程不安全的代码 2. volatile能保证内存可见性 3. synchronized与volatile的区别 3.1 synchronized能保证原子性 3.2 volatile不能保证原子性 1. 造成线程不安全的代码 有一代码&#xff0c;要求两个线程运行。并自定义一个标志位 flag&#xff0c;当线程2&…

UVM学习——搭建简单的UVM平台

引言 本专栏的博客均与 UVM 的学习相关&#xff0c;学习参考&#xff1a; 【1】UVM Tutorial 【2】张强著&#xff0c;UVM实战 &#xff08;卷 Ⅰ&#xff09; 【3】Download UVM (Standard Universal Verification Methodology) 本专栏的学习基本依照 资料【2】的主线&#…

【机器学习】正规方程法求解线性回归问题

前情提要&#xff1a;https://blog.csdn.net/weixin_45434953/article/details/130604086 正规方程 正规方程能以更好的方式求得假设函数中 θ \theta θ的最优值。它提供了一种用于求 θ \theta θ的解析方法&#xff0c;而不是梯度下降那样的迭代方法。也就是只需要一次运算…

Microsoft Power BI连接本地mysql 数据库 !power bi提示此连接器需要安装一个或多个其他组件才能使用怎么办!

一、步骤 &#xff08;一&#xff09;从菜单栏点击进入mysql数据库 点击主页>获取数据>更多 选择mysql数据库&#xff0c;点击连接 &#xff08;二&#xff09;已经安装了mysql connector/net还是提示此连接器需要安装一个或多个其他组件才能使用-解决 重装了几次都…

vue-cli 关闭 Uncaught error 的全屏提示

在使用vue-cli开发项目的时候&#xff0c;如果代码抛出异常了&#xff0c;那么就会出现一个全屏的提示框&#xff0c;长下面这样&#xff1a; 经过一段时间的排查发现是webpack的问题&#xff0c;排查方式就是打开控制台&#xff0c;看这个框的一些属性&#xff0c;通常会有一些…

【DNDC模型】在土地利用变化、未来气候变化下的建模方法及温室气体时空动态模拟实践技术

DNDC&#xff08;Denitrification-Decomposition&#xff0c;反硝化-分解模型&#xff09;是目前国际上最为成功的模拟生物地球化学循环的模型之一&#xff0c;自开发以来&#xff0c;经过不断完善和改进&#xff0c;从模拟简单的农田生态系统发展成为可以模拟几乎所有陆地生态…

界面开发框架Qt新手入门教程 - 可编辑树模型的示例(一)

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 The Qt Company是Di…

考研日语-详解ている、てある、ていく、てくる用法

目录 一、ている用法 1. 表示现在状态 2. 表示持续动作 3. 表示经验或习惯 4. 表示结果或效果 二、てある用法 1. 表示已经完成的动作 2. 表示现在状态 3. 表示被动 三、ていく用法 1. 表示未来的动作 2. 表示逐渐变化的过程 四、てくる用法 1. 表示过去到现在的…

Python实现哈里斯鹰优化算法(HHO)优化XGBoost分类模型(XGBClassifier算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 2019年Heidari等人提出哈里斯鹰优化算法(Harris Hawk Optimization, HHO)&#xff0c;该算法有较强的全…