当前位置: 首页 > news >正文

Mysql数据库表结构迁移PostgreSQL

1、背景:

公司本来用的数据库都是mysql,为了国产化适配兼容pg和dm。dm提供了数据迁移工具,可以直接做数据迁移,生成脚本之后在其他环境执行。但是pg貌似没有工具能直接用。navicat由于版权问题公司也用不了。pgloader使用总是有问题,可以执行pgloader命令,但是没有任何报错,表结构也没有迁移过去。所以自己写了个工具类,用于生成mysql表结构,然后通过脚本执行的方式在psql中执行。

2、难点:

1、mysql与pg建表语句不同,需要根据mysql的元数据自定义pg的表结构。

2、没有办法直接将mysql的字段类型拿来用,需要映射字段类型(如果没有特殊的类型,基本网上都可以找到映射表)。

3、直接用DriverManager的方式无法获取到字段的其他属性,比如索引等。

4、mysql的一些特性无法在pg中直接使用,比如mysql的自增直接指定auto_increment,而pg则需要指定字段为bigserial。mysql还可以设置时间的自动更新,而pg需要使用其他函数等等问题。

3、思路:

1、尽可能的找mysql与pg的类型映射关系,测试中发现问题在补全。没啥好的办法。

2、不使用JDBC提供的获取元数据的api,而是使用sql查询mysql元数据信息。然后根据mysql的元数据进行解析生成pg的建表语句。

3、对于mysql的特殊函数比如时间自动更新,修改源码。在代码层面设置时间和更新时间。

4、具体实现:

(1)、mysql字段实体和索引实体对象

package com.example.canaltest.entry;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;/*** @author Shaon* @version 1.0* @title MysqlTableColEntity* @description* @create 2024/9/2 10:30*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MysqlTableColEntity implements Serializable {/*** 表对应的库名*/private String tableSchema;/*** 表名*/private String tableName;/*** 列名*/private String columnName;/*** 列的类型*/private String dataType;/*** 列的长度*/private String dataLength;/*** 类是不是主键 PRI;UNI;MUL*/private String columnKey;/*** 是否可以为空 NO:不为空;YES:可以为空*/private String isNullable;/*** 列默认值*/private String columnDefault;/*** 列注释*/private String columnComment;/*** 类扩展 是否自增(auto_increment);时间函数(其他数据库不一定可以使用)*/private String extra;
}
package com.example.canaltest.entry;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;/*** @author Shaon* @version 1.0* @title MysqlTableIndexEntity* @description NON_UNIQUE, INDEX_NAME, SEQ_IN_INDEX, COLUMN_NAME* @create 2024/9/2 17:11*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MysqlTableIndexEntity implements Serializable {/*** 是否不是唯一索引  0:唯一索引;1:不是唯一索引*/private int nonUnique;/*** 索引的名称*/private String indexName;/*** 索引的序号,从 1 开始,如果存在多个递增*/private int seqInIndex;/*** 组成索引的列名*/private String columnName;
}

(2)、生成pg的ddl到指定的文件。这里每个数据库生成一个文件。

package com.example.canaltest.utils;import com.example.canaltest.entry.MysqlTableColEntity;
import com.example.canaltest.entry.MysqlTableIndexEntity;
import com.google.common.base.Joiner;
import com.google.common.collect.Maps;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.*;
import java.util.stream.Collectors;/*** @author Shaon* @version 1.0* @title MysqlTrasformPostgresql* @description* @create 2024/9/2 9:30*/
public class MysqlTransformPostgresql {private static final String MYSQL_URL = "jdbc:mysql://192.168.16.240:3306/dbName";private static final String MYSQL_USER = "username";private static final String MYSQL_PASSWORD = "password";/*** 需要迁移的mysql数据库*/private static final List<String> databaseList = Arrays.asList("dbName");private static final Map<String, String> typeMapping = Maps.newHashMap();static {// 初始化映射表typeMapping.put("tinyint", "smallint");typeMapping.put("smallint", "smallint");typeMapping.put("mediumint", "integer");typeMapping.put("int", "integer");typeMapping.put("integer", "integer");typeMapping.put("bigint", "bigint");typeMapping.put("decimal", "numeric");typeMapping.put("numeric", "numeric");typeMapping.put("float", "real");typeMapping.put("double", "double precision");typeMapping.put("real", "double precision");typeMapping.put("date", "date");typeMapping.put("time", "time without time zone");typeMapping.put("datetime", "timestamp without time zone");typeMapping.put("timestamp", "timestamp without time zone");typeMapping.put("year", "smallint");typeMapping.put("char", "character");typeMapping.put("varchar", "varchar");typeMapping.put("tinytext", "text");typeMapping.put("text", "text");typeMapping.put("mediumtext", "text");typeMapping.put("longtext", "text");typeMapping.put("blob", "bytea");typeMapping.put("enum", "varchar"); // PostgreSQL 没有 ENUM 类型,可以使用 VARCHAR 替代typeMapping.put("set", "varchar"); // PostgreSQL 没有 SET 类型,可以使用 VARCHAR 替代typeMapping.put("json", "jsonb");typeMapping.put("binary", "bytea");typeMapping.put("varbinary", "bytea");typeMapping.put("bit", "boolean");typeMapping.put("bool", "boolean");typeMapping.put("boolean", "boolean");typeMapping.put("point", "point");typeMapping.put("linestring", "line");typeMapping.put("polygon", "polygon");typeMapping.put("geometry", "geometry");typeMapping.put("geography", "geography");typeMapping.put("inet", "inet");typeMapping.put("cidr", "cidr");typeMapping.put("macaddr", "macaddr");typeMapping.put("uuid", "uuid");typeMapping.put("hstore", "hstore");}private static final String sql = "select TABLE_SCHEMA,\n" +"       TABLE_NAME,\n" +"       COLUMN_NAME,\n" +"       COLUMN_DEFAULT,\n" +"       IS_NULLABLE,\n" +"       DATA_TYPE,\n" +"       CHARACTER_MAXIMUM_LENGTH,\n" +"       COLUMN_KEY,\n" +"       EXTRA,\n" +"       COLUMN_COMMENT\n" +"from INFORMATION_SCHEMA.COLUMNS\n" +"where TABLE_SCHEMA = ?";private static final String indexSql = "select NON_UNIQUE,INDEX_NAME,SEQ_IN_INDEX,COLUMN_NAME\n" +"from information_schema.STATISTICS\n" +"where TABLE_SCHEMA = ? and TABLE_NAME = ?;";/*** 存储文件的路径前缀*/private static final String filePathPrefix = "E:\\mysql-table\\";public static void mysqlDDL2File() {try (Connection connection = DriverManager.getConnection(MYSQL_URL, MYSQL_USER, MYSQL_PASSWORD);PreparedStatement statement = connection.prepareStatement(sql)) {for (String dbName : databaseList) {Path path = Paths.get(filePathPrefix + dbName + ".sql");StringJoiner joiner = new StringJoiner("\n");List<MysqlTableColEntity> tableColEntities = Lists.newArrayList();statement.setString(1, dbName);ResultSet resultSet = statement.executeQuery();while (resultSet.next()) {String tableSchema = resultSet.getString("TABLE_SCHEMA");String tableName = resultSet.getString("TABLE_NAME");String columnName = resultSet.getString("COLUMN_NAME");String columnDefault = resultSet.getString("COLUMN_DEFAULT");String isNullable = resultSet.getString("IS_NULLABLE");String dataType = resultSet.getString("DATA_TYPE");String dataLength = resultSet.getString("CHARACTER_MAXIMUM_LENGTH");String columnKey = resultSet.getString("COLUMN_KEY");String extra = resultSet.getString("EXTRA");String columnComment = resultSet.getString("COLUMN_COMMENT");MysqlTableColEntity build = MysqlTableColEntity.builder().tableSchema(tableSchema).tableName(tableName).columnName(columnName).columnDefault(columnDefault).isNullable(isNullable).dataType(dataType).dataLength(dataLength).columnKey(columnKey).extra(extra).columnComment(columnComment).build();tableColEntities.add(build);}Map<String, List<MysqlTableColEntity>> groupByTableName = groupByTableName(tableColEntities);for (String tableName : groupByTableName.keySet()) {String tableSql = buildTableSql(connection, dbName, tableName, groupByTableName.get(tableName));joiner.add(tableSql);}System.out.println(joiner);writeSqlFile(path, joiner.toString());}// 生成建库脚本createDatabaseSql(databaseList);} catch (Exception e) {e.printStackTrace();}}private static void createDatabaseSql(List<String> databaseList) throws IOException {Path path = Paths.get(filePathPrefix + "pg_create_database.sql");StringJoiner joiner = new StringJoiner("\n");for (String dbName : databaseList) {joiner.add("create database " + dbName + ";");joiner.add("\n");}writeSqlFile(path, joiner.toString());}/*** 将内容写到文件中** @param path* @param context* @throws IOException*/private static void writeSqlFile(Path path, String context) throws IOException {Files.write(path, context.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.APPEND);}private static Map<String, List<MysqlTableColEntity>> groupByTableName(List<MysqlTableColEntity> tableColEntities) {return tableColEntities.stream().collect(Collectors.groupingBy(MysqlTableColEntity::getTableName));}private static String buildTableSql(Connection connection, String dbName, String tableName, List<MysqlTableColEntity> tableColEntities) {String colComment = "comment on column " + tableName + ".%s is '%s';";StringJoiner colJoin = new StringJoiner(",\n");StringJoiner commentJoin = new StringJoiner("\n");List<MysqlTableIndexEntity> mysqlIndexList = getMysqlIndexList(connection, dbName, tableName);for (MysqlTableColEntity tableColEntity : tableColEntities) {StringJoiner col = new StringJoiner(" ");String columnName = tableColEntity.getColumnName();String dataType = tableColEntity.getDataType();String isNullable = tableColEntity.getIsNullable();String dataLength = tableColEntity.getDataLength();String columnKey = tableColEntity.getColumnKey();String columnDefault = tableColEntity.getColumnDefault();String columnComment = tableColEntity.getColumnComment();String extra = tableColEntity.getExtra();String pgType = typeMapping.get(dataType);// 处理不需要长度的数据类型if (StringUtils.isNotBlank(dataLength)&& !dataType.contains("text")&& !Objects.equals("blob", dataType)) {pgType += ("(" + dataLength + ")");}if (Objects.equals("PRI", columnKey)&& Objects.equals("auto_increment", extra)) {pgType = "bigserial";}col.add("\t");col.add("\"" + columnName + "\"");col.add(pgType);if (StringUtils.isNotBlank(columnDefault)&& !columnDefault.contains("CURRENT_TIMESTAMP")) {if (Objects.equals(dataType, "bit")) {if (columnDefault.contains("0")) {columnDefault = "false";} else {columnDefault = "true";}}col.add("default " + "'" + columnDefault + "'");}if (Objects.equals("NO", isNullable)) {col.add("not null");}colJoin.add(col.toString());String format = String.format(colComment, "\"" + columnName + "\"", columnComment);commentJoin.add(format);}if (StringUtils.isNotBlank(buildPkIndex(mysqlIndexList))) {String constraint = "\tconstraint " + tableName + "_pk" + "\n";String pk = "\t\tprimary key" + " (" + buildPkIndex(mysqlIndexList) + ")";colJoin.add(constraint + pk);}StringBuilder builder = new StringBuilder();builder.append("create table if not exists " + tableName + "\n").append("(\n").append(colJoin).append("\n").append(");\n").append(commentJoin).append("\n").append("alter table " + tableName + " owner to postgres;").append("\n");String indexSql = buildCreateIndexSql(tableName, mysqlIndexList);// 处理 非主键索引if (StringUtils.isNotBlank(indexSql)) {builder.append(indexSql).append("\n");}return builder.toString();}private static String buildCreateIndexSql(String tableName, List<MysqlTableIndexEntity> mysqlIndexList) {StringJoiner joiner = new StringJoiner("\n");joiner.setEmptyValue("");// 1:索引的类型  index 和unique index// 2:索引的名称// 3:索引的字段,多个用逗号+空格分割String indexSql = "create %s if not exists %s\n" +"\ton " + tableName + " (%s);";Map<String, List<MysqlTableIndexEntity>> indexMapping = mysqlIndexList.stream().filter(item -> !Objects.equals("PRIMARY", item.getIndexName())).sorted(Comparator.comparingInt(MysqlTableIndexEntity::getSeqInIndex)).collect(Collectors.groupingBy(MysqlTableIndexEntity::getIndexName));for (String indexName : indexMapping.keySet()) {List<MysqlTableIndexEntity> mysqlTableIndexEntities = indexMapping.get(indexName);int nonUnique = mysqlTableIndexEntities.get(0).getNonUnique();String indexType = "index";if (nonUnique == 0) {indexType = "unique index";}List<String> colNameList = mysqlTableIndexEntities.stream().map(item -> "\"" + item.getColumnName() + "\"").collect(Collectors.toList());if (CollectionUtils.isEmpty(colNameList)) {continue;}String colName = Joiner.on(", ").join(colNameList);joiner.add(String.format(indexSql, indexType, indexName, colName));}return joiner.toString();}private static String buildPkIndex(List<MysqlTableIndexEntity> indexEntityList) {List<MysqlTableIndexEntity> primary = indexEntityList.stream().filter(item -> Objects.equals("PRIMARY", item.getIndexName())).collect(Collectors.toList());List<String> colNameList = primary.stream().map(item -> "\"" + item.getColumnName() + "\"").collect(Collectors.toList());if (CollectionUtils.isEmpty(colNameList)) {return null;}return Joiner.on(", ").join(colNameList);}private static List<MysqlTableIndexEntity> getMysqlIndexList(Connection connection, String dbName, String tableName) {List<MysqlTableIndexEntity> list = Lists.newArrayList();try (PreparedStatement statement = connection.prepareStatement(indexSql)) {statement.setString(1, dbName);statement.setString(2, tableName);ResultSet resultSet = statement.executeQuery();while (resultSet.next()) {// NON_UNIQUE, INDEX_NAME, SEQ_IN_INDEX, COLUMN_NAMEint nonUnique = resultSet.getInt("NON_UNIQUE");String indexName = resultSet.getString("INDEX_NAME");int seqInIndex = resultSet.getInt("SEQ_IN_INDEX");String columnName = resultSet.getString("COLUMN_NAME");MysqlTableIndexEntity build = MysqlTableIndexEntity.builder().nonUnique(nonUnique).indexName(indexName).seqInIndex(seqInIndex).columnName(columnName).build();list.add(build);}} catch (Exception e) {e.printStackTrace();}return list;}public static void main(String[] args) {mysqlDDL2File();}
}

(3)、执行脚本

#! /bin/bash# 检查输入参数数量  
if [ "$#" -ne 3 ]; then  echo "Usage: $0 <username> <password> <folder_path>"  exit 1  
fi  # 检查文件夹是否存在  
if [ ! -d "$FOLDER_PATH" ]; then  echo "Error: Folder does not exist: $FOLDER_PATH"  exit 1  
fi  USERNAME=$1
PASSWORD=$2
FILEPATH=$3export PGPASSWORD=${PASSWORD}
psql -U ${USERNAME} -h 127.0.0.1 -p 5432 -f ${FILEPATH}
#! /bin/bash
# 检查输入参数数量  
if [ "$#" -ne 3 ]; then  echo "Usage: $0 <username> <password> <folder_path>"  exit 1  
fi USERNAME=$1  
PASSWORD=$2  
FOLDER_PATH=$3 # 检查文件夹是否存在  
if [ ! -d "$FOLDER_PATH" ]; then  echo "Error: Folder does not exist: $FOLDER_PATH"  exit 1  
fi  # 检查文件夹是否存在  
if [ ! -d "$FOLDER_PATH" ]; then  echo "Error: Folder does not exist: $FOLDER_PATH"  exit 1  
fi  # 遍历文件夹中的每个文件  
for FILE in "$FOLDER_PATH"/*; do if [ -f "$FILE" ];thenecho ${FILE}DB_NAME=$(basename "$FILE" | cut -d'.' -f1)SQL_SCRIPT=${DB_NAME}.sqlecho ${DB_NAME}echo ${SQL_SCRIPT}psql -d ${DB_NAME} -U ${USERNAME} -h 127.0.0.1 -p 5432 -f ${FILE}fi
done  echo "Script execution completed."

两个脚本分别是:

1、生成数据库

2、生成表

脚本的逻辑都是执行脚本输入三个参数,分别是用户名、密码、执行路径。组装成psql语句,执行。只不过执行建表的逻辑的执行路径是个文件夹,遍历这个文件夹下的所有文件,执行建表。

5、问题:

1、可能存在个别字段映射不正确的问题。

2、mysql外键无法同步到pg中。

3、存储过程没有同步。

4、insert数据脚本没有生成。

上边存在的问题,在使用的时候可以修改一下,对于外键和存储过程,一般情况下应该在功能设计的时候就避免。对于数据的迁移还欠缺,但是基本insert就可以用,所以可以用工具生成insert然后执行。

相关文章:

Mysql数据库表结构迁移PostgreSQL

1、背景&#xff1a; 公司本来用的数据库都是mysql&#xff0c;为了国产化适配兼容pg和dm。dm提供了数据迁移工具&#xff0c;可以直接做数据迁移&#xff0c;生成脚本之后在其他环境执行。但是pg貌似没有工具能直接用。navicat由于版权问题公司也用不了。pgloader使用总是有问…...

店匠科技携手Stripe共谋电商支付新篇章

在全球电商行业蓬勃发展的背景下,支付环节作为交易闭环的核心,其重要性日益凸显。随着消费者对支付体验要求的不断提高,以及跨境电商的迅猛发展,支付市场正经历着前所未有的变革与挑战。在这一充满机遇与竞争的领域,店匠科技(Shoplazza)凭借其创新的嵌入式支付解决方案—— Sho…...

大众(奥迪)汽车继电器编号对照表

数字功能放大器零件编号1化油器进气歧管加热器40a1719063832燃油泵(CE1 MK1 Golf 和 Early Rocco/cabrio K-Jet,无转速限制器)-443906059A 321906059D/E3燃油泵(CE1 MK1 Golf 和 Early Rocco/cabrio K-Jet,无转速限制器)-4439060594安全带警告继电器5早期 MFA 时钟的换档…...

《佛脚闪卡watch》——Apple Watch上的高效学习助手

在快节奏的生活环境中&#xff0c;时间管理成为了学习成功的关键因素之一。**《佛脚闪卡watch》**是一款专为Apple Watch设计的应用程序&#xff0c;旨在帮助学生和自学者更高效地利用碎片时间进行学习。无论是等待公交、散步还是短暂休息&#xff0c;您都可以随时随地进行复习…...

六、桥接模式

桥接模式&#xff08;Bridge Pattern&#xff09;是一种结构型设计模式&#xff0c;旨在将抽象与实现分离&#xff0c;使得两者可以独立变化。通过使用桥接模式&#xff0c;可以避免在多个维度上进行继承&#xff0c;降低代码的复杂度&#xff0c;从而提高系统的可扩展性。 组成…...

Vue eslint 语法检测问题

1. 修改 prettier 配置文件 确保你的项目中有 .prettierrc 配置文件&#xff0c;并在其中添加或修改 endOfLine 设置为 lf&#xff0c;确保统一使用 LF 换行符。 在 .prettierrc 文件中添加&#xff1a; {"endOfLine": "lf" }2. 修改 .editorconfig 文件…...

QT Creater实现国庆节主题项目【0基础完成版】

本文适用对象 想要学习qt creater的小白;想要学习c++制作软件的编程爱好者。可以先下载这篇博客绑定的资源,然后一边操作,一边学习,会更高效~0. 创建初始项目 一步步来操作吧,首先下载qt creter,之前发布过相关资源,大家直接查找下载,或者自行下载。 1. 初始代码 mai…...

Qt 加载 WPS 时提示要登录

项目中Qt加载word时 默认用wps打开word文档 程序一运行老是提示要立即登录 看着很烦 可以按下面的方法去掉这个烦人的东西 在下面的项目中新建字符串enableforceloginforfirstinstalldevice&#xff0c;值为false即可。...

vue3的el-tree的default-checked-keys无法勾选的问题解决

前言:有些树形控件是需要默认勾选的 但是请求后渲染不显示 刷新外部的key值也没有用 看了一下文档 我们使用自带的方法来解决 <el-treenode-key"id":data"state.parentMenuList":default-checked-keys"state.checkIdList":check-on-click-n…...

class 5: vue.js 3 v-model和表单输入

v-model是Vue.js 3中用于实现双向绑定的重要指令&#xff0c;双向绑定就是对于数据的修改会映射回UI组件上&#xff0c;同时对于UI组件上数据的变更也会映射回底层数据当中&#xff0c;v-model会根据控件的类型自动选取正确的方法来更新元素v-model底层实现的原理实际上是v-bin…...

了解一下HTTP 与 HTTPS 的区别

介绍&#xff1a; HTTP是超文本传输协议。规定了客户端&#xff08;通常是浏览器&#xff09;和服务器之间如何传输超文本&#xff0c;也就是包含链接的文本。通常使用TCP【1】/IP协议来传输数据&#xff0c;默认端口为80。 HTTPS是超文本传输安全协议&#xff0c;具有CA证书。…...

Opencv中的直方图(1)计算反向投影直方图函数calcBackProject()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 计算直方图的反向投影。 cv::calcBackProject 函数计算直方图的反向投影。也就是说&#xff0c;类似于 calcHist&#xff0c;在每个位置 (x, y)…...

VUE3项目的几种创建方式

文章目录 1.使用 Vue CLI (Vue Command Line Interface):2.使用 Create Vue App:3.使用 Vite:4.使用图形用户界面 (GUI):5.纯手工搭建6.基于模板创建: 1.使用 Vue CLI (Vue Command Line Interface): Vue CLI 是官方推荐的方式来快速搭建 Vue 项目的基础结构。命令&#xff1a…...

VBA进行excel坐标转换

在Excel里利用坐标绘图时&#xff0c;可以比较容易想到采用数据透视表&#xff0c;但是数据透视表生成的图不可更改&#xff0c;因此本案例采用VBA进行坐标变换而不改变原始值来转换图像&#xff0c;即实现图像的左右翻转和上下翻转&#xff0c;如下图所示&#xff0c;选择map的…...

使用pytorch深度学习框架搭建神经网络

简介 现在主流有两个框架pytorch和TensorFlow,本文主要介绍pytorch PyTorch&#xff1a;由 Facebook 的人工智能研究小组开发和维护。PyTorch 以其动态计算图&#xff08;Dynamic Computational Graph&#xff09;和易用性著称&#xff0c;非常适合研究人员和开发者进行实验和…...

Hive数据库与表操作全指南

目录 Hive数据库操作详解 创建数据库 1&#xff09;语法 2&#xff09;案例 查询数据库 1&#xff09;展示所有数据库 &#xff08;1&#xff09;语法 &#xff08;2&#xff09;案例 2&#xff09;查看数据库信息 &#xff08;1&#xff09;语法 &#xff08;2&#…...

UniaApp引入Iconfont

一、下载图标资源 登录到阿里巴巴矢量图标库&#xff08;iconfont&#xff09;官网&#xff08;https://www.iconfont.cn/&#xff09;。找到你需要的图标并添加到购物车&#xff0c;然后点击购物车图标进入到管理界面&#xff0c;选择 “下载代码”。在下载选项中选择 “Font…...

面试题:软件测试缺陷产生的原因有哪些?

软件缺陷产生的原因多种多样&#xff0c;一般可能有以下几种原因&#xff1a; 1.需求表述、理解、编写引起的错误。 2.系统架构设计引起的错误。 3.开发过程缺乏有效的沟通及监督&#xff0c;甚至没有沟通或监督。 4.程序员编程中产生的错误。 5.软件开发工具本身隐藏的问…...

RabbitMQ 04 集群

01...

axure9勾选多个删除,弹框显示多个中继器编号

文本框 情形是删除逗号 文本框内插入数据...

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

蓝桥杯 2024 15届国赛 A组 儿童节快乐

P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡&#xff0c;轻快的音乐在耳边持续回荡&#xff0c;小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下&#xff0c;六一来了。 今天是六一儿童节&#xff0c;小蓝老师为了让大家在节…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...