一二三应用开发平台自定义查询设计与实现系列2——查询方案功能实现

查询方案功能实现

上面实现了自定义查询功能框架,从用户角度出发,有些条件组合可以形成特定的查询方案,对应着业务查询场景。诸多查询条件的组合,不能每次都让用户来设置,而是应该保存下来,下次可以直接使用或者在其基础上按需调整,也就是将查询方案持久化。

实体配置

使用平台的低代码配置功能来实现查询方案的实体配置。

考虑到自定义查询属于通用性功能,因此放到了平台架构体系中位于底层的系统管理模块下。

实体配置如下:

实体模型配置如下:

查询方案内置于自定义查询功能内部的,因此无需配置相关视图。

使用平台功能生成库表和代码、拷贝代码、编译、配置权限项。

功能整合

首先上整体效果图,有个直观的了解,如下图:

在原有基础上,顶部增加了查询方案的下拉列表,用户可以通过下拉选择快速加载先前保存的查询方案,并可以通过修改按钮来修改名称、通过删除按钮来删除方案。

查询方案的主体,仍是由筛选器组件来承担,集成整合过程中增加了交互。

底部通过保存按钮来保存查询方案;通过另存为按钮来拷贝新增方案,点击查询按钮后,关闭对话框,将查询方案对应查询条件组合,传递给父窗口(自定义查询功能页面),发起查询操作。

查询方案下拉列表实现

在查询方案目录下,新增一个select.vue的页面,源码如下:

<template>
  <el-select
    v-model="selectedValue"
    :size="size"
    clearable
    :disabled="readonly"
    style="width: 200px; margin: 20 auto"
    @change="change"
  >
    <el-option
      v-for="item in dictionaryItemList"
      :key="item.id"
      :value="item.id"
      :label="item.name"
    />
  </el-select>

</template>

<script>
export default {
  name: 'QueryPlanSelect',
  label: '查询方案下拉',
  props: {
    modelValue: {
      type: String,
      required: false,
      default: ''
    },
    code: {
      type: String,
      default: ''
    },
    readonly: {
      type: Boolean,
      required: false,
      default: false
    },
    size: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      dictionaryItemList: [],
      selectedValue: ''
    }
  },
  watch: {
    modelValue: {
      immediate: true,
      handler: 'setSelected'
    }
  },
  mounted() {
    this.loadData()
  },
  methods: {
    change(value) {
      let rowData = null
      this.dictionaryItemList.forEach((item) => {
        if (item.id === value) {
          rowData = item
          return
        }
      })
      // 更新绑定值
      this.$emit('update:modelValue', value)
      // 注意,此处若命令为change,则可能会与底层实现冲突,导致执行两次
      this.$emit('my-change', value, rowData)
    },
    setSelected() {
      this.selectedValue = this.modelValue
    },
    loadData() {
      this.dictionaryItemList = []
      this.$api.system.queryPlan.list().then((res) => {
        this.dictionaryItemList = res.data
        if (this.dictionaryItemList.length == 1) {
          this.selectedValue = this.dictionaryItemList[0].id
          this.$emit('my-change', this.dictionaryItemList[0].id, this.dictionaryItemList[0])
        } else {
          this.selectedValue = ''
          this.$emit('my-change', '', null)
        }
      })
    }
  }
}
</script>

就是把查询方案实体的列表数据,转换为下拉列表的数据格式,并对传入值以及下拉选择变更后,将数据通过事件触发返回给父页面,也就是自定义查询页面。

查询方案下拉列表选中项变更

自定义查询页面会监听查询方案下拉列表的选择的变更,可能有两种情况,一是变更选择项,二是清空选择,对应的事件处理如下:

    // 查询方案变更
    queryPlanChanged(value, data) {
      // 保存为当前查询方案
      this.currentQueryPlan = data
      if (data) {
        //加载条件
        this.$refs.everrightFilter.setData(JSON.parse(data.content))
      } else {
        this.$refs.everrightFilter.clearData()
      }
    }

注意当查询方案内容不为空时,调用筛选器的setData方法,来填充条件组合,便于用户查看或在其基础上调整。

查询方案改名

点击下拉列表右侧的“修改”按钮,弹出输入框,这里实际是用于改名,系统会弹出对话框,默认填充当前选中的查询方案名称,用户修改后点击确定按钮后保存,如下图:

功能实现使用了element plus的MessageBox的Prompt模式,代码如下:

    // 修改方案名称
    modify() {
      if (this.currentQueryPlan == null) {
        this.$message.warning('请先选择要改名的查询方案')
        return
      }

      //获取查询方案内容,并做非空判断
      const content = JSON.stringify(this.$refs.everrightFilter.getData(), null, '\t')
      if (this.checkContentIsNull(content)) {
        return
      }
      // 输入新的方案名称
      ElMessageBox.prompt('请输入新的方案名称', '修改查询方案', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        inputValue: this.currentQueryPlan.name,
        inputPattern: /.+/,
        inputErrorMessage: '不能为空'
      }).then(({ value }) => {
        this.currentQueryPlan.name = value
        this.currentQueryPlan.content = content
        this.$api.system.queryPlan.modify(this.currentQueryPlan).then((res) => {
          //刷新查询方案列表
          this.refreshQuerPlanList()
        })
      })
    },

查询方案删除

点击下拉列表右侧的“删除”按钮,弹出确认框,确定后删除,如下:

源码实现如下:

 // 删除
    remove() {
      if (this.currentQueryPlan == null) {
        this.$message.warning('请先选择要删除的查询方案')
        return
      }
      this.$confirm('确定要删除该查询方案吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.$api.system.queryPlan.remove(this.currentQueryPlan.id).then((res) => {
          // 刷新查询方案列表
          this.refreshQuerPlanList()
        })
      })
    }

查询方案新增/修改

在查询方案下拉列表无选中项的情况下,通过筛选器配置查询条件组合后,点击保存按钮,弹出对话框,输入方案名称后,新增查询方案,如下:

在查询方案下拉列表有选中项的情况下,点击保存按钮,直接调用后端服务,保存数据,实际更新的是条件组合。

代码如下:

     // 保存查询方案
    save() {
      //获取查询方案内容,并做非空判断
      const content = JSON.stringify(this.$refs.everrightFilter.getData(), null, '\t')
      if (this.checkContentIsNull(content)) {
        return
      }

      if (this.currentQueryPlan) {
        // 如当前查询方案不会空,更新数据
        this.currentQueryPlan.content = content
        this.$api.system.queryPlan.modify(this.currentQueryPlan)
      } else {
        // 如当前查询方案为空,新增数据
        ElMessageBox.prompt('请输入方案名称', '新增查询方案', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          inputPattern: /.+/,
          inputErrorMessage: '不能为空'
        }).then(({ value }) => {
          const data = {
            name: value,
            entityModelCode: this.entityModelCode,
            content: content
          }
          this.$api.system.queryPlan.add(data).then((res) => {
            //刷新查询方案列表
            this.refreshQuerPlanList()
          })
        })
      }
    }

查询方案另存为

用户往往需要基于某个现有的查询方案调整,另存为另一个查询方案,通过功能按钮实现。

这里没有根据当前是否有选中的查询方案来控制另存为按钮的可用,而是在逻辑上进行了处理,如当前查询方案为空,新增数据,否则参照当前数据新增(即另存),实现如下:

//另存为
    saveAs() {
      //获取查询方案内容,并做非空判断
      const content = JSON.stringify(this.$refs.everrightFilter.getData(), null, '\t')
      if (this.checkContentIsNull(content)) {
        return
      }
      if (this.currentQueryPlan == null) {
        // 如当前查询方案为空,新增数据
        ElMessageBox.prompt('请输入方案名称', '新增查询方案', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          inputPattern: /.+/,
          inputErrorMessage: '不能为空'
        }).then(({ value }) => {
          const data = {
            name: value,
            entityModelCode: this.entityModelCode,
            content: content
          }
          this.$api.system.queryPlan.add(data).then((res) => {
            //刷新查询方案列表
            this.refreshQuerPlanList()
          })
        })
      } else {
        // 如当前查询方案不为空,参照当前数据新增
        ElMessageBox.prompt('请输入方案名称', '查询方案另存为', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          inputPattern: /.+/,
          inputErrorMessage: '不能为空'
        }).then(({ value }) => {
          const data = {
            name: value,
            entityModelCode: this.entityModelCode,
            content: content
          }
          this.$api.system.queryPlan.add(data).then((res) => {
            //刷新查询方案列表
            this.refreshQuerPlanList()
          })
        })
      }
    }

辅助方法

有几个公共辅助方法,在这里也简单列一下:

保存数据前需要判断是否已添加了条件,即进行数据验证,如下:

     // 判断方案内容是否为空
    checkContentIsNull(content) {
      if (content == '{}') {
        this.$message.warning('请先添加至少一个查询条件')
        return true
      }
      return false
    }

在对方案新增、修改、删除时,需要刷新下拉列表,如下:

    // 刷新查询方案列表
    refreshQuerPlanList() {
      this.$refs.queryPlanSelect.loadData()
    }

后端处理

先前搭建框架的时候,仅实现了单组单条件,接下来,就来完善该部分逻辑以及其他细节。

单组多条件

配置查询方案条件如下:

后端逻辑完善如下:

        // 转换数据
        DataFilterRuleVO dataFilterRule = JSON.parseObject(customQueryString, DataFilterRuleVO.class);
        // 获取组集合
        List<DataFilterGroupVO> dataFilterGroupList = dataFilterRule.getFilters();

        // 遍历组集合
        for (DataFilterGroupVO dataFilterGroup : dataFilterGroupList) {
            // 获取条件集合
            List<DataFilterConditionVO> conditionList = dataFilterGroup.getConditions();
            // 组内条件逻辑关系
            String logicalOperator = dataFilterGroup.getLogicalOperator();
            if (CollectionUtils.isNotEmpty(conditionList)) {

                // 遍历条件集合
                for (DataFilterConditionVO condition : conditionList) {
                    if (logicalOperator.equals(OR)) {
                        queryWrapper = queryWrapper.or();
                    }
                    // 获取字段名,命名风格驼峰转换成下划线
                    String fieldName = condition.getProperty();
                    Object value = condition.getValue();
                    // 获取操作
                    String operator = condition.getOperator();
                    QueryRuleEnum queryRule = EnumUtils.getEnum(QueryRuleEnum.class, operator, QueryRuleEnum.EQ);

                    addEasyQuery(queryWrapper, fieldName, queryRule, value);
                }
            }
        }

运行,查看生成SQL,如下:

SELECT COUNT(*) AS total FROM cfg_template WHERE delete_flag = 'NO' AND (name = ? AND content LIKE ?)

将组内逻辑关系调整为或,如下:

运行,查看生成SQL,如下:

SELECT COUNT(*) AS total FROM cfg_template WHERE delete_flag = 'NO' AND (name = ? OR content LIKE ?)

多组多条件

配置查询方案条件如下:

后端逻辑增加组间逻辑关系处理,调整后如下:

           // 转换数据
        DataFilterRuleVO dataFilterRule = JSON.parseObject(customQueryString, DataFilterRuleVO.class);
        // 获取组集合
        List<DataFilterGroupVO> dataFilterGroupList = dataFilterRule.getFilters();
        // 组间条件逻辑关系
        String logicalOperatorGroup = dataFilterRule.getLogicalOperator();
        // 遍历组集合
        for (DataFilterGroupVO dataFilterGroup : dataFilterGroupList) {
            if (logicalOperatorGroup.equals(OR)) {
                queryWrapper = queryWrapper.or();
            }
            // 获取条件集合
            List<DataFilterConditionVO> conditionList = dataFilterGroup.getConditions();
            // 组内条件逻辑关系
            String logicalOperator = dataFilterGroup.getLogicalOperator();
            if (CollectionUtils.isNotEmpty(conditionList)) {

                // 遍历条件集合
                for (DataFilterConditionVO condition : conditionList) {
                    if (logicalOperator.equals(OR)) {
                        queryWrapper = queryWrapper.or();
                    }
                    // 获取字段名,命名风格驼峰转换成下划线
                    String fieldName = condition.getProperty();
                    Object value = condition.getValue();
                    // 获取操作
                    String operator = condition.getOperator();
                    QueryRuleEnum queryRule = EnumUtils.getEnum(QueryRuleEnum.class, operator, QueryRuleEnum.EQ);

                    addEasyQuery(queryWrapper, fieldName, queryRule, value);
                }
            }
        }

运行,查看生成SQL,如下:

==>  Preparing: SELECT COUNT(*) AS total FROM cfg_template WHERE delete_flag = 'NO' AND (name = ? AND content LIKE ? AND name LIKE ? AND name LIKE ?)
==> Parameters: 1(String), %2%(String), 3%(String), %4(String)

看上去,SQL从最终查询结果而言是正确的,但是没有分组,如果组内关系是or,推测应该会出问题,我们继续验证下。

将组间逻辑关系调整为或,如下:

运行,查看生成SQL,如下:

==>  Preparing: SELECT COUNT(*) AS total FROM cfg_template WHERE delete_flag = 'NO' AND (name = ? AND content LIKE ? OR name LIKE ? AND name LIKE ?)
==> Parameters: 1(String), %2%(String), 3%(String), %4(String)

可以看出来,果然出了问题,我们希望各条件组相对独立,用小括号包裹,然后再参与组间逻辑运算。

基于上述需求,对逻辑转换代码进行调整,如下:

private static <E, VO> void build(QueryWrapper<E> queryWrapper, Class<E> entityClass, String customQueryString, SortInfo sortInfo) {
        // 转换数据
        DataFilterRuleVO dataFilterRule = JSON.parseObject(customQueryString, DataFilterRuleVO.class);
        // 获取组集合
        List<DataFilterGroupVO> dataFilterGroupList = dataFilterRule.getFilters();
        // 组间条件逻辑关系
        String logicalOperatorGroup = dataFilterRule.getLogicalOperator();
        // 遍历组集合
        for (DataFilterGroupVO dataFilterGroup : dataFilterGroupList) {
            if (logicalOperatorGroup.equals(OR)) {
                queryWrapper.or(x ->
                        generateQueryWrapper(x, dataFilterGroup)
                );
            } else {
                queryWrapper.and(x ->
                        generateQueryWrapper(x, dataFilterGroup)
                );
            }
        }
        // 附加排序
        if (sortInfo != null && StringUtils.isNotBlank(sortInfo.getField())) {
            // 此处未使用注解,而是按照约定的规则,将驼峰命名转换为下划线,从而获取到数据库表字段名
            String orderField = CommonUtil.camelToUnderline(sortInfo.getField());
            if (sortInfo.getAscType()) {
                queryWrapper.orderByAsc(orderField);
            } else {
                queryWrapper.orderByDesc(orderField);
            }
        }
    }

    private static <E> QueryWrapper<E> generateQueryWrapper(QueryWrapper<E> queryWrapper, DataFilterGroupVO dataFilterGroup) {
        // 获取条件集合
        List<DataFilterConditionVO> conditionList = dataFilterGroup.getConditions();
        // 组内条件逻辑关系
        String logicalOperator = dataFilterGroup.getLogicalOperator();
        if (CollectionUtils.isNotEmpty(conditionList)) {

            // 遍历条件集合
            for (DataFilterConditionVO condition : conditionList) {
                if (logicalOperator.equals(OR)) {
                    queryWrapper = queryWrapper.or();
                }
                // 获取字段名,命名风格驼峰转换成下划线
                String fieldName = condition.getProperty();
                Object value = condition.getValue();
                // 获取操作
                String operator = condition.getOperator();
                QueryRuleEnum queryRule = EnumUtils.getEnum(QueryRuleEnum.class, operator, QueryRuleEnum.EQ);

                addEasyQuery(queryWrapper, fieldName, queryRule, value);
            }
        }
        return queryWrapper;
    }

运行,查看生成SQL,如下:

==>  Preparing: SELECT COUNT(*) AS total FROM cfg_template WHERE delete_flag = 'NO' AND ((name = ? AND content LIKE ?) OR (name LIKE ? AND name LIKE ?))
==> Parameters: 1(String), %2%(String), 3%(String), %4(String)

可以看到,条件组外层使用了小括号包裹,组间的逻辑运算也是正确的。

日期类型的处理

前面提到过,在做筛选器组件前端功能验证时,发现日期类型的属性,生成查询规则与其他类型不同,如下:

value属性不是一个值,而是对应了一个对象,并且也不是日期字符串,而是时间戳,这种情况下,需要后端另行解析处理。

设置测试条件如下:

增加逻辑处理如下:

运行,查看生成SQL,如下:

==>  Preparing: SELECT COUNT(*) AS total FROM cfg_template WHERE delete_flag = 'NO' AND ((name = ? AND content LIKE ?) OR (name LIKE ? AND name LIKE ?))
==> Parameters: 1(String), %2%(String), 3%(String), %4(String)

开源平台资料

平台名称:一二三开发平台
简介: 企业级通用开发平台
设计资料:[csdn专栏]
开源地址:[Gitee]
开源协议:MIT
如果您在阅读本文时获得了帮助或受到了启发,希望您能够喜欢并收藏这篇文章,为它点赞~
请在评论区与我分享您的想法和心得,一起交流学习,不断进步,遇见更加优秀的自己!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySql基础:事务

1. 事务的简介 1.1 什么是事务 事务就是一组DML语句组成&#xff0c;这些语句在逻辑上存在相关性&#xff0c;这一组DML语句要么全部成功&#xff0c;要么全部失败&#xff0c;是一个整体。MySQL提供一种机制&#xff0c;保证我们达到这样的效果。事务还规定不同的客户端看到的…

数字IC开发:布局布线

数字IC开发&#xff1a;布局布线 前端经过DFT&#xff0c;综合后输出网表文件给后端&#xff0c;由后端通过布局布线&#xff0c;将网表转换为GDSII文件&#xff1b;网表文件只包含单元器件及其连接等信息&#xff0c;GDS文件则包含其物理位置&#xff0c;具体的走线&#xff1…

Linux 进程优先级 进程切换

目录 优先级 概念 为什么优先级要限制在一定范围内 进程切换 方式 EIP寄存器(程序计数器) 进程在运行时会使用寄存器来保存临时数据 进程的上下文是什么&#xff1f; 进程的上下文保存到哪&#xff1f; 内核栈或专门的上下文结构也在内核空间&#xff1f;那为什么不直…

Visual Studio Code

代码自动保存 打开设置搜索auto save&#xff0c;设置为afterDelay 设置延迟时间&#xff0c;单位是毫秒 启用Ctrl鼠标滚轮对字体进行缩放 搜索Mouse Wheel Zoom&#xff0c;把该选项勾选上即可 Python插件 运行和调试Python

在zabbix5.0中监控hpe 3par8440存储

前言 通常在3par ssmc或者命令行才能完全查看各项数据&#xff0c;比如硬件状态&#xff0c;在zabbix中如何详细并集中监控查看3par的各项系统软硬件数据或者状态呢&#xff1f;3par 利用snmp协议搜集数据貌似不可行&#xff0c;但是在zabbix官网推出了一个基于SMI-S接口结合p…

软件测试学习笔记丨Selenium学习笔记:css定位

本文转自测试人社区&#xff0c;原文链接&#xff1a;https://ceshiren.com/t/topic/22511 本文为霍格沃兹测试开发学社的学习经历分享&#xff0c;写出来分享给大家&#xff0c;希望有志同道合的小伙伴可以一起交流技术&#xff0c;一起进步~ 说明&#xff1a;本篇博客基于sel…

即插即用篇 | YOLOv8 引入 空间自适应特征调制模块 SAFM

代码地址: https://github.com/sunny2109/SAFMN 论文地址:https://arxiv.org/pdf/2302.13800 虽然已经提出了许多图像超分辨率的解决方案,但它们通常与许多计算和内存限制的低功耗设备不兼容。本文通过提出一个简单而有效的深度网络来高效地解决图像超分辨率问题。具体来说,…

layui扩展组件之----右键菜单

源码&#xff1a;rightmenu.js layui.define([element], function (exports) {let element layui.element;const $ layui.jquery;let MOD_NAME rightmenu;let RIGHTMENUMOD function () {this.v 1.0.0;this.author raowenjing;};String.prototype.format function () {…

本质矩阵分解计算Rt

1 本质矩阵的计算 上一文章中描述了本质矩阵的计算&#xff0c;计算机视觉-对极几何-CSDN博客&#xff0c;那么计算得到本质矩阵有什么用&#xff1f;其中一个应用是通过本质矩阵计算得到2D-2D的相对变换。 在相关矩阵计算时&#xff0c;一般会在两幅图像中&#xff0c;根据特征…

谷歌云GCP基础概念讲解

概览 云的基础是虚拟化&#xff1a;服务器&#xff0c;存储&#xff0c;网络。服务器是远程计算机的逻辑分区。存储是物理硬盘的逻辑划分。网络则是虚拟私有云。 谷歌是唯一一个拥有全球私有基础设施的公司&#xff1b;他们的谷歌云基础设施没有任何一部分通过公共互联网。换句…

HarmonyOS 组件样式@Style 、 @Extend、自定义扩展(AttributeModifier、AttributeUpdater)

1. HarmonyOS Style 、 Extend、自定义扩展&#xff08;AttributeModifier、AttributeUpdater&#xff09; Styles装饰器&#xff1a;定义组件重用样式   ;Extend装饰器&#xff1a;定义扩展组件样式   自定义扩展&#xff1a;AttributeModifier、AttributeUpdater 1.1. 区…

排序(一)插入排序,希尔排序,选择排序,堆排序,冒泡排序

目录 一.排序 1.插入排序 2.希尔排序 3.选择排序 4.堆排序 5.冒泡排序 二.整体代码 1.Sort.h 2.Sort.c 3.test.c 一.排序 1.插入排序 插入排序基本思想:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插入完为 止…

【UE5.3 Cesium for Unreal】编译GlobePawn

目录 前言 效果 步骤 一、下载所需文件 二、下载CesiumForUnreal插件 三、处理下载的文件 四、修改代码 “CesiumForUnreal.uplugin”部分 “CesiumEditor.cpp”部分 “CesiumEditor.h”部分 “CesiumPanel.cpp”部分 “IonQuickAddPanel.cpp”部分 “IonQuickAd…

线程的理解及基本操作

目录 一、线程的理解 &#xff08;1&#xff09;什么是线程呢&#xff1f; &#xff08;2&#xff09;线程的优缺点及异常 二、线程的基本操作 &#xff08;1&#xff09;创建一个新的进程 &#xff08;2&#xff09;获取线程id &#xff08;3&#xff09;线程终止 &…

SpringBoot 集成RabbitMQ 实现钉钉日报定时发送功能

文章目录 一、RabbitMq 下载安装二、开发步骤&#xff1a;1.MAVEN 配置2. RabbitMqConfig 配置3. RabbitMqUtil 工具类4. DailyDelaySendConsumer 消费者监听5. 测试延迟发送 一、RabbitMq 下载安装 官网&#xff1a;https://www.rabbitmq.com/docs 二、开发步骤&#xff1a;…

AC的旁挂和直连的方式的使用场景

AC组网架构 AC中文含义为无线接入控制器&#xff0c;主要功能是可以批量配置和管理无线AP。经常工作在大中型园区网络、企业办公网络等应用场景。 下面来介绍一下无线AC的几种经典架构。 一1旁挂式组网 旁挂式组网顾名思义&#xff0c;就是旁挂在网络中&#xff0c;对AP来进行…

view design之table自定义单元格模版

View Design之table自定义单元格模版 在 columns 的某列声明 slot 后&#xff0c;就可以在 Table 的 slot 中使用参数。 slot 的参数有 3 个&#xff1a;当前行数据 row&#xff0c;当前列数据 column&#xff0c;当前行序号 index。 完整示例 <template><Table …

乘云而上,OceanBase再越山峰

一座山峰都是一个挑战&#xff0c;每一次攀登都是一次超越。 商业数据库时代&#xff0c;面对国外数据库巨头这座大山&#xff0c;实现市场突破一直都是中国数据库产业多年夙愿&#xff0c;而OceanBase在金融核心系统等领域的攻坚克难&#xff0c;为产业突破交出一副令人信服的…

在Ubuntu(Linux)系统下安装Anaconda3

1、到官网下载Linux版本的包&#xff1a;https://www.anaconda.com/download/success 2、到所在目录中&#xff0c;运行下方命令&#xff0c;Anaconda3-2024.06-1-Linux-x86_64.sh是下载包的名字 bash Anaconda3-2024.06-1-Linux-x86_64.sh输入yes确定 3、输入~/anaconda3/b…

MySQL数据库集群-PXC方案视频教程下载 MySQL架构设计及常见业务处理

MySQL数据库集群-PXC方案视频教程下载 MySQL架构设计及常见业务处理30套数据库系列Mysql/SQLServer/Redis/Mongodb/Nosql精讲训练营项目实战&#xff0c;数据库设计&#xff0c;架构设计&#xff0c;性能管理&#xff0c;集群搭建&#xff0c;查询优化&#xff0c;索引优化&…