java中如何优雅处理多租户系统的查询?
多租户系统通常是指一个应用服务多个客户(租户),每个租户的数据需要隔离,确保数据安全和隐私。处理这样的系统需要考虑数据隔离、查询效率、代码的可维护性等方面。
首先,我应该明确多租户的实现方式。常见的多租户数据隔离策略有:
1. **独立数据库**:每个租户有自己的数据库。这种方式隔离性最好,但成本高,管理复杂。
2. **共享数据库,独立Schema**:所有租户共享一个数据库,但每个租户有独立的Schema。隔离性较好,但跨租户查询复杂。
3. **共享数据库,共享Schema**:所有租户共享同一个数据库和Schema,通过租户ID字段区分数据。这种方式成本低,但需要在每个查询中过滤租户ID,确保数据隔离。
第三种方式最常见,尤其是在SaaS应用中,因为它节省资源且扩展性好。但这也意味着在每次数据库操作时都需要自动添加租户ID的条件,避免数据泄露。
接下来,我需要考虑如何在Java中实现这种模式。可能涉及到的方面包括:
- **框架支持**:使用如Hibernate、Spring Data JPA等ORM框架时,如何自动注入租户ID条件。
- **数据源路由**:如果是独立数据库或独立Schema的情况,可能需要动态数据源切换。
- **查询过滤**:在共享Schema的情况下,如何自动在SQL查询中添加租户ID的条件。
- **安全性**:防止开发人员忘记添加租户ID过滤,导致数据泄露。
- **性能**:确保租户ID的索引优化,避免全表扫描。
对于共享Schema的情况,处理多租户查询的优雅方式通常包括:
1. **使用Hibernate的过滤器(Filter)**:Hibernate提供了@Filter注解,可以在实体级别动态添加过滤条件。例如,在实体类上添加@Filter,并在查询时启用该过滤器,自动添加tenant_id = :tenantId的条件。
2. **AOP(面向切面编程)**:通过AOP在DAO层或Service层拦截查询方法,自动添加租户ID的条件。例如,使用Spring AOP在查询方法执行前修改查询参数或语句。
3. **自定义MyBatis拦截器**:如果使用MyBatis,可以编写拦截器(Interceptor),在SQL执行前自动追加租户ID的条件。
4. **租户上下文管理**:使用ThreadLocal或请求上下文来存储当前租户ID,在数据访问层自动获取并应用。
5. **数据库视图或存储过程**:在数据库层面创建视图,自动过滤租户ID,但这可能不够灵活,且维护成本高。
接下来,我需要详细分析这些方法,比较它们的优缺点,找到最优雅的实现方式。
首先,Hibernate的Filter功能。通过在实体类上添加@FilterDef和@Filter注解,可以在运行时动态添加过滤条件。例如:
```java
@Entity
@Table(name = "some_table")
@FilterDef(name = "tenantFilter", parameters = @ParamDef(name = "tenantId", type = "string"))
@Filter(name = "tenantFilter", condition = "tenant_id = :tenantId")
public class SomeEntity {
// 实体字段
}
```
在查询时,需要手动启用过滤器:
```java
session.enableFilter("tenantFilter").setParameter("tenantId", currentTenantId);
```
这种方式需要确保在每次查询时都启用了过滤器,否则可能导致数据泄露。如果使用Hibernate,可以在OpenSessionInViewFilter或类似机制中自动启用过滤器,但需要确保租户ID的正确传递。
其次,AOP的方式。通过定义一个切面,拦截所有DAO或Repository的方法调用,自动在查询条件中添加租户ID。例如,使用Spring AOP:
```java
@Aspect
@Component
public class TenantFilterAspect {
@Autowired
private TenantContext tenantContext;
@Pointcut("execution(* com.example.repository.*.*(..))")
public void repositoryMethods() {}
@Around("repositoryMethods()")
public Object applyTenantFilter(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
// 修改参数,添加租户ID条件
// 或者修改查询对象,如JPA的Specification、Criteria等
return joinPoint.proceed(args);
}
}
```
这种方式需要在切面中处理各种查询方法的参数,可能较为复杂,特别是对于不同的查询方式(如JPA Criteria、QueryDSL、原生SQL等),需要不同的处理逻辑。
第三种,自定义MyBatis拦截器。对于使用MyBatis的项目,可以编写一个拦截器,在SQL执行前自动追加tenant_id = #{tenantId}的条件。例如:
```java
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class TenantInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 获取当前租户ID
String tenantId = TenantContext.getCurrentTenantId();
// 修改参数对象或SQL,添加租户ID条件
return invocation.proceed();
}
}
```
这种方式需要解析SQL语句,找到对应的表并添加条件,可能较为复杂,但可以统一处理所有SQL操作,无论ORM框架如何。
第四种,租户上下文管理。使用ThreadLocal保存当前租户ID,在数据访问层自动获取。例如:
```java
public class TenantContext {
private static final ThreadLocal<String> currentTenant = new ThreadLocal<>();
public static void setCurrentTenant(String tenantId) {
currentTenant.set(tenantId);
}
public static String getCurrentTenant() {
return currentTenant.get();
}
public static void clear() {
currentTenant.remove();
}
}
```
在Service层或Controller层设置租户ID,例如通过拦截器解析请求头或URL中的租户信息,并设置到TenantContext中。然后在DAO层自动应用该租户ID到查询中。
结合以上几种方法,最优雅的方式可能是结合租户上下文和框架提供的过滤器或拦截器,自动注入租户ID条件,减少代码侵入性,提高可维护性。
例如,使用Hibernate的Filter配合租户上下文:
1. 在实体类上定义过滤器。
2. 在请求处理开始时,设置当前租户ID到租户上下文中。
3. 在数据访问层,自动启用Hibernate过滤器,并设置参数为当前租户ID。
4. 确保所有查询都经过过滤器处理,避免遗漏。
对于原生SQL或复杂查询,可能需要额外处理,但大部分CRUD操作可以通过Hibernate过滤器自动处理。
此外,需要注意以下几点:
- **租户ID的传递**:租户ID通常来自用户认证信息(如JWT令牌)、请求头、子域名等,需要在请求处理的最开始解析并设置到租户上下文中。
- **数据迁移和初始化**:在创建新租户时,需要确保其数据与其他租户隔离。
- **索引优化**:确保tenant_id字段在数据库表中有索引,避免全表扫描影响性能。
- **测试**:编写充分的测试用例,确保多租户隔离在各种场景下有效,包括联合查询、分页、排序等。
可能的陷阱包括:
- **忘记启用过滤器**:如果某次查询没有启用过滤器,可能导致数据泄露。需要确保所有数据访问路径都经过过滤器处理。
- **租户ID未正确传递**:例如异步任务、批处理作业中,租户上下文可能丢失,需要显式传递租户ID。
- **跨租户操作**:管理员可能需要跨租户查询数据,此时需要特别处理,临时禁用过滤器,并确保权限控制。
总结来说,优雅处理多租户查询需要结合框架特性、租户上下文管理和AOP等技术,自动透明地添加租户ID条件,减少重复代码,确保数据隔离的可靠性和系统性能。
在Java中优雅处理多租户系统的查询,关键在于透明化数据隔离并确保查询效率。以下是分步骤的解决方案:
1. 确定多租户数据隔离策略
选择 共享数据库,共享Schema 模式,通过 tenant_id
字段区分租户数据。优势包括:
-
资源高效:节省数据库实例和连接池资源。
-
扩展灵活:易于水平扩展和管理。
2. 租户上下文管理
使用 ThreadLocal 存储当前租户ID,确保在请求处理链路中可随时获取。
java
复制
下载
public class TenantContext {private static final ThreadLocal<String> CURRENT_TENANT = new ThreadLocal<>();public static void setTenantId(String tenantId) {CURRENT_TENANT.set(tenantId);}public static String getTenantId() {return CURRENT_TENANT.get();}public static void clear() {CURRENT_TENANT.remove();} }
3. 自动注入租户ID到查询
3.1 使用Hibernate过滤器(JPA/Hibernate项目)
-
步骤1:实体类添加过滤器注解
java
复制
下载
@Entity @Table(name = "orders") @FilterDef(name = "tenantFilter", parameters = @ParamDef(name = "tenantId", type = "string")) @Filter(name = "tenantFilter", condition = "tenant_id = :tenantId") public class Order {@Column(name = "tenant_id")private String tenantId;// 其他字段... }
-
步骤2:全局启用过滤器
java
复制
下载
@Configuration public class HibernateConfig {@Autowiredprivate EntityManagerFactory entityManagerFactory;@PostConstructpublic void registerFilters() {SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);sessionFactory.getFilterDefinitionRegistry().addFilterDefinition(new FilterDefinition("tenantFilter","tenant_id = :tenantId",Map.of("tenantId", Type.STRING)));} }
-
步骤3:拦截请求并设置租户ID
java
复制
下载
@Component public class TenantInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {String tenantId = resolveTenantId(request); // 从请求头/Token中解析TenantContext.setTenantId(tenantId);return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {TenantContext.clear();} }
-
步骤4:自动应用过滤器到所有查询
java
复制
下载
@Aspect @Component public class TenantFilterAspect {@Autowiredprivate EntityManager entityManager;@Before("execution(* com.example.repository.*.*(..))")public void enableTenantFilter() {Session session = entityManager.unwrap(Session.class);session.enableFilter("tenantFilter").setParameter("tenantId", TenantContext.getTenantId());} }
3.2 使用MyBatis拦截器(MyBatis项目)
-
步骤1:自定义拦截器
java
复制
下载
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}) }) public class TenantInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {Object parameter = invocation.getArgs()[1];if (parameter instanceof Map) {((Map) parameter).put("tenantId", TenantContext.getTenantId());} else if (parameter instanceof BaseEntity) {((BaseEntity) parameter).setTenantId(TenantContext.getTenantId());}return invocation.proceed();} }
-
步骤2:在Mapper XML中引用租户ID
xml
复制
下载
运行
<select id="selectOrders" resultType="Order">SELECT * FROM orders WHERE tenant_id = #{tenantId}<!-- 其他条件 --> </select>
4. 处理复杂查询与联表操作
4.1 自动关联表租户过滤
在联表查询中,确保所有关联表都包含 tenant_id
并添加条件:
sql
复制
下载
SELECT o.*, c.name FROM orders o JOIN customers c ON o.customer_id = c.id WHERE o.tenant_id = :tenantId AND c.tenant_id = :tenantId
4.2 动态SQL生成(MyBatis)
使用 <if>
标签动态插入租户条件:
xml
复制
下载
运行
<select id="findOrders" parameterType="map" resultType="Order">SELECT * FROM orders<where>tenant_id = #{tenantId}<if test="status != null">AND status = #{status}</if></where> </select>
5. 多租户下的数据迁移与初始化
-
独立数据初始化脚本:为每个新租户执行初始化SQL时,自动插入
tenant_id
。 -
数据隔离导出:备份或导出数据时按
tenant_id
过滤。
6. 性能优化
-
索引优化:为
tenant_id
字段创建复合索引。sql
复制
下载
ALTER TABLE orders ADD INDEX idx_tenant_status (tenant_id, status);
-
分区表:按
tenant_id
分区,提升查询效率。sql
复制
下载
CREATE TABLE orders (... ) PARTITION BY HASH(tenant_id) PARTITIONS 10;
7. 特殊场景处理
7.1 管理员跨租户查询
-
权限控制:仅允许特定角色(如ADMIN)进行跨租户操作。
-
动态关闭过滤器:
java
复制
下载
@Transactional public List<Order> adminQueryAllOrders() {Session session = entityManager.unwrap(Session.class);session.disableFilter("tenantFilter");return session.createQuery("FROM Order", Order.class).list(); }
7.2 异步任务与批处理
-
显式传递租户ID:在异步线程或任务中手动设置租户上下文。
java
复制
下载
CompletableFuture.runAsync(() -> {TenantContext.setTenantId(tenantId);// 执行业务逻辑 });
8. 安全审计与测试
-
日志记录:记录所有数据访问操作的租户ID。
-
单元测试:验证多租户隔离是否生效。
java
复制
下载
@Test public void testTenantIsolation() {TenantContext.setTenantId("tenantA");List<Order> ordersA = orderRepository.findAll();TenantContext.setTenantId("tenantB");List<Order> ordersB = orderRepository.findAll();assertThat(ordersA).noneMatch(order -> order.getTenantId().equals("tenantB")); }
总结
通过 租户上下文管理 + 框架级过滤(Hibernate/MyBatis) + 索引优化 的组合策略,实现多租户查询的优雅处理。确保:
-
透明隔离:开发者无需手动添加
tenant_id
条件。 -
高性能:通过索引和分区优化查询速度。
-
安全性:严格防止跨租户数据泄露。
相关文章:
java中如何优雅处理多租户系统的查询?
多租户系统通常是指一个应用服务多个客户(租户),每个租户的数据需要隔离,确保数据安全和隐私。处理这样的系统需要考虑数据隔离、查询效率、代码的可维护性等方面。 首先,我应该明确多租户的实现方式。常见的多租户数据…...
Ubuntu服务器部署多语言项目(Node.js/Python)方式实践
Ubuntu服务器部署多语言项目(Node.js/Python)方式实践 服务器脚本运行方式命令行直接执行nohup后台执行进程 Screen概述安装基本操作命令启动 Screen退出当前会话(不终止进程)查看所有会话重连会话关闭会话 常用快捷键典型使用场景…...
【MySQL】基础操作
MySQL(二)基础操作 一、数据库操作 1.创建库 2.查看库 3.选中库 4.删除库 二、表操作 1.创建表 1.1[comment 注释]: 1.2,...: 2.查看表 2.1查看所有表 2.2查看表结构 3.删除表 三、记录操作 1.插入记录 1.1全列插入 1.2指定列插入 1.3…...
在 Java MyBatis 中遇到 “操作数类型冲突: varbinary 与 float 不兼容” 的解决方法
在 MyBatis 中遇到 “操作数类型冲突: varbinary 与 float 不兼容” 错误,通常是因为当字段值为 null 时,MyBatis 无法正确推断其 JDBC 类型,导致向数据库传递 null 值时类型不匹配。以下是原因分析和解决方案: 问题原因 未指定 j…...
课题推荐——扩展卡尔曼滤波(EKF)估计pmsm的位置误差
扩展卡尔曼滤波(EKF)是一种常用于非线性系统状态估计的方法,特别适用于永磁同步电机(PMSM)的位置和速度估计。EKF可以实时估计电机的转子位置误差(与实际转子位置的偏差),从而提高控…...
elasticsearch之记录es7.17升级8.17 springboot2.7.0 程序改造坑
es7.17升级8.x问题目录 一、硬件安装1-1. centos7 服务器上,删除elasticsearch7.17,安装es8.17 二、 程序改造2-1. Java API Client 8.17.52-2. 依赖引入2-3. 配置文件2-4. Java 配置类 三、根据 Elasticsearch 集群信息(版本 8.17.2…...
SpringBoot+ELK 搭建日志监控平台
ELK 简介 ELK(Elasticsearch, Logstash, Kibana)是一个目前主流的开源日志监控平台。由三个主要组件组成的: Elasticsearch: 是一个开源的分布式搜索和分析引擎,可以用于全文检索、结构化检索和分析,它构建…...
家庭数字生态构建实战:基于飞牛fnOS的智能家居数据中台搭建全流程解析
文章目录 前言1. VMware安装飞牛云(fnOS)1.1 打开VMware创建虚拟机1.3 初始化系统 2. 安装Cpolar工具3. 配置远程访问地址4. 远程访问飞牛云NAS5. 固定远程访问地址 前言 在数字生活时代,数据管理正成为每个家庭的刚需。今天要向大家重点推荐…...
博客系统功能测试
博客系统网址:http://8.137.19.140:9090/blog_list.html 主要测试内容 功能测试、界面测试、性能测试、易用性测试、安全测试、兼容性测试、弱网测试、安装卸载测试、压力测试… 测试方法及目的 利用selenium和python编写测试脚本,对博客系统进行的相关…...
抽奖相关功能测试思路
1. 抽奖系统功能测试用例设计(登录 每日3次 中奖40% 道具兑换码) ✅ 功能点分析 必须登录后才能抽奖每天最多抽奖3次抽奖有 40% 概率中奖中奖返回兑换码 ✅ 测试用例设计 编号 用例描述 前置条件 操作 预期结果 TC01 未登录时抽奖 未登录 …...
paddle ocr本地化部署进行文字识别
一、Paddle 简介 1. 基本概念 Paddle(全称 PaddlePaddle,飞桨)是百度开发的 开源深度学习平台,也是中国首个自主研发、功能丰富、技术领先的工业级深度学习平台。它覆盖了深度学习从数据准备、模型训练、模型部署到预测的全流程…...
在CentOS系统上部署GitLabRunner并配置CICD自动项目集成!
在CentOS系统上部署GitLabRunner并配置CICD自动项目集成 在CentOS系统上部署GitLab Runner并配置CI/CD自动项目集成GitLab CI/CD是一个强大的持续集成和持续部署工具,能够显著提高开发团队的效率。 本文将详细介绍如何在CentOS系统上部署GitLab Runner,…...
python学习day2(未写完,明天继续补充)
今天主要学习了变量的数据类型,以及如何使用格式化符号进行输出。 一、认识数据类型 在python里为了应对不同的业务需求,也把数据分为不同的类型。 代码如下: """ 1、按类型将不同的变量存储在不同的类型数据 2、验证这些…...
深度强化学习框架DI-engine
深度强化学习框架DI-engine 一、DI-engine概述:决策智能的通用引擎 DI-engine是由OpenDILab开源的决策智能引擎,基于PyTorch和JAX构建,旨在为强化学习(RL)、模仿学习(IL)、离线学习等场景提供…...
gitlab迁移
需求:需要将A服务器上的 gitlab 迁移到B服务器上,均使用docker 部署 一、备份数据 进入到A服务器的 gitlab 的容器中,运行gitlab-rake gitlab:backup:create 该命令会在 /var/opt/gitlab/backups/ 目录下创建一个xxx_gitlab_backup.tar 压缩…...
UEFI Spec 学习笔记---33 - Human Interface Infrastructure Overview---33.2.6 Strings
33.2.6 Strings UEFI 环境中的 string 是使用 UCS-2 格式定义,每个字符由 16bit 数据表示。对于用户界面,strings 也是一种可以安装到 HIIdatabase 的一种数据。 为了本土化,每个 string 通过一个唯一标识符来识别,而每一个标识…...
如何确保低空经济中的数据安全?
低空经济涉及大量敏感数据,如无人机的飞行轨迹、拍摄的地理图像和视频等。为确保这些数据的安全,可从以下几方面着手: 加强数据加密 传输加密 :采用 SSL/TLS 等加密协议,对数据在传输过程中进行加密,防止…...
在linux平台下利用mingw64编译windows程序
背景 笔者平时都是基于linux平台开发C代码,已经熟悉使用CMake这一套工具上一次开发windows应用程序还要追溯到10多年前,彼时还是使用微软的visual studio这个IDE,这个IDE确实也很强大,但也确实很笨重,当时用起来也很不…...
虚幻引擎5-Unreal Engine笔记之什么时候新建GameMode,什么时候新建关卡?
虚幻引擎5-Unreal Engine笔记之什么时候新建GameMode,什么时候新建关卡? code review! 参考笔记: 1.虚幻引擎5-Unreal Engine笔记之GameMode、关卡(Level) 和 关卡蓝图(Level Blueprint)的关系 2.虚幻引擎…...
[IMX] 04.定时器 - Timer
目录 1.周期中断定时器 - EPIT 1.1.工作模式 1.2.配置寄存器 - EPIT_CR 1.3.状态寄存器 - EPIT_SR 1.4.加载寄存器 - EPIT_LR 1.5.比较寄存器 - EPIT_CMPR 1.6.计数寄存器 - EPIT_CNR 2.通用定时器 - GPT 2.1. 时钟源 2.2.模块结构 2.3.工作模式 2.4.配置寄存器 - …...
前端 vue + element-ui 框架从 0 - 1 搭建
1. 安装node 地址: Node.js — 在任何地方运行 JavaScript 2. 安装 vue 2.1 执行安装命令 npm uninstall -g vue-cli npm install -g vue/cli 安装最新的vue3版本 2.2 使用vue 脚手架 搭建项目 vue create project_name 2.2.1 注意 项目名称不能包…...
【IDEA】删除/替换文件中所有包含某个字符串的行
目录 前言 正则表达式 示例 使用方法 前言 在日常开发中,频繁地删除无用代码或清理空行是不可避免的操作。许多开发者希望找到一种高效的方式,避免手动选中代码再删除的繁琐过程。 使用正则表达式是处理字符串的一个非常有效的方法。 正则表达式 …...
算法刷题(Java与Python)2.数组、列表
目录 Java的数组 数组介绍 注意事项 Python的列表 列表介绍 Python 的列表和 Java 的 ArrayList 一样吗? 例题1 代码分析 Java代码 Python代码 对比代码 例题2 代码分析 Java代码 Python代码 对比代码 例题三 Java代码 Python代码 代码对比 Jav…...
uniapp打包H5,输入网址空白情况
由于客户预算有限,最近写了两个uniapp打包成H5的案例,总结下面注意事项 1. 发行–网站-PCWeb或手机H5按钮,输入名称,网址 点击【发行】,生成文件 把这个给后端,就可以了 为什么空白呢 最重要一点…...
JavaScript 中使用 Elasticsearch 的正确方式,第一部分
作者:来自 Elastic Jeffrey Rengifo 讲解如何用 JavaScript 创建一个可用于生产环境的 Elasticsearch 后端。 想获得 Elastic 认证?看看下一期 Elasticsearch 工程师培训什么时候开始吧! Elasticsearch 拥有大量新功能,能帮助你…...
每日一道leetcode(增加版)
901. 股票价格跨度 - 力扣(LeetCode) 题目 设计一个算法收集某些股票的每日报价,并返回该股票当日价格的 跨度 。 当日股票价格的 跨度 被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今…...
排序复习/下(C语言版)
目录 1.快速排序(hoare法) 单趟: 整体: 代码优化: 编辑三数取中代码: 小区间优化代码: hoare法疑问解答: 2.快速排序(挖坑法) 3.快速排序&#x…...
Vue百日学习计划Day33-35天详细计划-Gemini版
总目标: 在 Day 33-35 理解 Vue 组件从创建到销毁的完整生命周期,熟练掌握 Composition API 中主要的生命周期钩子,并知道在不同阶段执行哪些操作。 所需资源: Vue 3 官方文档 (生命周期钩子): https://cn.vuejs.org/guide/essentials/lifecycle.html你…...
Apidog MCP服务器,连接API规范和AI编码助手的桥梁
#作者:曹付江 文章目录 1.了解 MCP2.什么是 Apidog MCP 服务器?3.Apidog MCP 服务器如何工作4.利用人工智能改变开发工作流程5.设置 Apidog MCP 服务器: 分步指南5.高级功能和提示5.1 使用 OpenAPI 规范5.2.多个项目配置5.3.安全最佳实践5.4…...
统计客户端使用情况,使用es存储数据,实现去重以及计数
这篇文件的重点在tshark、filebeat、和logstash。 需求:统计客户使用的客户端版本 实现工具:tshark 1.10.14,filebeat 8.17.0,logstash 8.17.0,elasticsearch 8.17.0,kibana 8.17.0 总体设计:…...
Git基础面试题
git的rm命令与系统的rm命令有什么区别 git rm 和系统的 rm (在 Windows 上是 del) 命令都用于删除文件,但它们在 Git 仓库的上下文中作用有所不同: 系统 rm (或 del) 命令: 作用: 直接从文件系统中删除文件。Git 的感知ÿ…...
conda 的常用命令
好的,下面为你介绍conda的常用命令: 环境管理 # 创建新环境 conda create -n env_name python3.8# 激活环境 conda activate env_name# 查看所有环境 conda env list# 复制环境 conda create -n new_env --clone old_env# 删除环境 conda remove -n en…...
PLC双人舞:profinet转ethernet ip网关奏响施耐德与AB的协奏曲
PLC双人舞:ethernet ip转profinet网关奏响施耐德与AB的协奏曲 案例分析:施耐德PLC与AB PLC的互联互通 在现代工业自动化中,设备之间的互联互通至关重要。本案例旨在展示如何通过北京倍讯科技的EtherNet/IP转Modbus网关,将施耐德P…...
百度OCR:证件识别
目录 一、编写目的 二、准备工作 2.1 OCR密钥 三、代码实现 3.1 配置文件 3.2 请求接收封装 3.3 请求响应封装 3.4 服务类参数初始化 3.5 服务类实现 3.6 解析结果 3.7 定义Web接口 四 测试效果 五、总结 欢迎来到盹猫🐱的博客 本篇文章主要介绍了 [百…...
纯前端实现图文识别 OCR
Tesseract.js Tesseract.js 是一个基于 Google Tesseract OCR 引擎的 JavaScript 库,利用 WebAssembly 技术将的 OCR 引擎带到了浏览器中。它完全运行在客户端,无需依赖服务器,适合处理中小型图片的文字识别。 基本使用 以下示例展示了如何…...
2025.05.01【Barplot】柱状图的多样性绘制
Custom color A few examples showing how to custom barplot color. Horizontal barchart It makes sense to make your barchart horizontal: group labels are now much easier to read 文章目录 Custom colorHorizontal barchart 探索Barplot的奥秘Barplot基础什么是Barp…...
在资源受限环境下,移动端如何实现流畅动画?如何在内存、CPU、GPU、网络等多种限制条件下,依然保持动画高帧率、低延迟、不卡顿?
在日常生活中,移动设备已经成为不可或缺的工具。从社交、购物到游戏、教育,几乎所有的应用场景都依赖于移动终端的计算和显示能力。然而,随着用户体验的不断提升需求,动画成为了界面交互中不可忽视的一环。动画不仅提升了视觉吸引…...
HJ10 字符个数统计【牛客网】
文章目录 零、原题链接一、题目描述二、测试用例三、解题思路四、参考代码 零、原题链接 HJ10 字符个数统计 一、题目描述 二、测试用例 三、解题思路 基本思路: 建立字符串的散列表,然后统计不同字符个数具体思路: 遍历字符串的字…...
关键点检测算法-RTMPose
一、网络框架(top-down模式) 二、各部分内容 1、骨干网络 对于网络而言,CXPset太大,可以换成starnet 2、一个卷积层 7x7的卷积核对性能提升最大 3、一个全连接层 将一维关键点表示扩展到由超参数控制的所需维度。 4、一个用…...
云原生安全:错误策略S3存储桶ACL设置为Everyone:FullControl
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 ——从基础到实践的深度解析 1. 基础概念 S3存储桶与ACL Amazon S3(Simple Storage Service)是AWS提供的对象存储服务,支持存储和检索任意规模的数据。ACL(访问控制列表…...
Axure疑难杂症:垂直菜单展开与收回(4大核心问题与专家级解决方案)
亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢!如有帮助请订阅专栏! Axure产品经理精品视频课已登录CSDN可点击学习https://edu.csdn.net/course/detail/40420 课程主题:垂直菜单展开与收回 主要内容:超长菜单实现、展开与收回bug解释、Axure9版本限制等问题解…...
图漾相机错误码解析(待补充)
文章目录 1.相机错误码汇总2.常见报错码2.1 -1001报错2.1.1 没有找到相机2.1.2 SDK没有进行初始化 2.2 -1005报错2.2.1 跨网段打开相机2.2.2 旧版本SDK在软触发失败后提示的报错2.2.3 相机初始化上电时报错2.2.4 USB相机被占用 2.3 -1009报错2.3.1 相机本身不支持改属性 2.4 -1…...
SpringBoot 中文转拼音 Pinyin4j库 拼音转换 单据管理 客户管理
介绍 在客户管理系统中部分客户的名字会有生僻字为了沟通时候不叫错客户的名称,因此决定将客户名称的拼音一起返回给前端,也可以直接交给前端去处理。这里介绍后端的做法 Pinyin4j 是一个用于将汉字转换为拼音的 Java 库。在需要对中文文本进行拼音转换…...
使用 Whisper 生成视频字幕:从提取音频到批量处理
生成视频字幕是许多视频处理任务的核心需求。本文将指导你使用 OpenAI 的 Whisper 模型为视频文件(如电视剧《Normal People》或电影《花样年华》)生成字幕(SRT 格式)。我们将从提取音频开始,逐步实现字幕生成…...
Kotlin Compose Button 实现长按监听并实现动画效果
想要实现长按按钮开始录音,松开发送的功能。发现 Button 这个控件如果去监听这些按下,松开,长按等事件,发现是不会触发的,究其原因是 Button 已经提前消耗了这些事件所以导致,这些监听无法被触发。因此为了…...
SQL练习——(15/81)
目录 1.计算次日留存率 2.多条件查询 方法1:子查询 方法2:窗口函数实现 3.条件查询——自连接相关 1.计算次日留存率 550. 游戏玩法分析 IV - 力扣(LeetCode) 错误查询1:(没有考虑从首次登录日期开始…...
数据中心 智慧机房解决方案
该文档介绍数据中心智慧机房解决方案,涵盖模块化数据中心(机柜式、微模块),具备低成本快速部署、标准化建设等特点;监控管理系统(DCIM)可实现设施、资产、容量、能效管理;节能解决方案含精密空调节能控制柜,节能率高达 30%;还有7X24 小时云值守运维服务。方案亮点包括…...
网络-MOXA设备基本操作
修改本机IP和网络设备同网段,输入设备IP地址进入登录界面,交换机没有密码,路由器密码为moxa 修改设备IP地址 交换机 路由器 环网 启用Turbo Ring协议:在设备的网络管理界面中,找到环网配置选项,启用Turb…...
Docker构建 Dify 应用定时任务助手
概述 Dify 定时任务管理工具是一个基于 GitHub Actions 的自动化解决方案,用于实现 Dify Workflow 的定时执行和状态监控。无需再为缺乏定时任务支持而感到困扰,本工具可以帮助设置自动执行任务并获取实时通知,优化你的工作效率。 注意&…...
前端测试策略:单元测试到 E2E 测试
引言 在现代前端开发中,测试已成为确保应用质量和可靠性的关键环节。随着前端应用复杂度的不断提高,仅依靠手动测试已经远远不够。一个全面的前端测试策略应该包含多个层次的测试,从最小粒度的单元测试到模拟真实用户行为的端到端(E2E)测试。…...