数据一致性的守护神:深入Spring声明式事务管理 (@Transactional)
在前面几篇文章中,我们一路打怪升级,掌握了Spring IoC/DI、Bean管理、AOP,并用JdbcTemplate和Spring Data JPA简化了数据库访问。我们的代码越来越简洁、高效。但想象一下这个场景:
用户A向用户B转账100元。这至少需要两个数据库操作:
-
从用户A的账户余额中减去100。
-
向用户B的账户余额中增加100。
如果第一步成功了,但第二步因为某些原因(数据库连接中断、服务器宕机、账户B不存在等)失败了,会发生什么?用户A的钱扣了,用户B没收到!这显然是灾难性的数据不一致。
如何确保这类多步骤操作的原子性——要么全部完成,要么一步都不做,保持原始状态?答案就是数据库事务 (Transaction)。
你是否还在为手动编写下面这样的JDBC事务代码而头疼?
Connection conn = null;
try {conn = dataSource.getConnection();// 1. 关闭自动提交!!!conn.setAutoCommit(false);// 操作1: 扣款updateBalance(conn, accountA, -100);// 操作2: 加款updateBalance(conn, accountB, 100);// 2. 如果都成功, 提交事务!conn.commit();System.out.println("Transaction successful!");} catch (SQLException e) {// 3. 如果任何一步出错, 回滚事务!if (conn != null) {try {conn.rollback();System.err.println("Transaction rolled back due to error: " + e.getMessage());} catch (SQLException ex) {System.err.println("Error during rollback: " + ex.getMessage());}}// 处理或向上抛出异常throw new RuntimeException("Transaction failed", e);
} finally {// 4. 最终恢复自动提交并关闭连接 (极其繁琐!)if (conn != null) {try {conn.setAutoCommit(true); // 恢复默认设置conn.close();} catch (SQLException e) {System.err.println("Error closing connection: " + e.getMessage());}}
}
这种编程式事务管理充斥着大量的样板代码,与业务逻辑紧密耦合,极易出错(忘记rollback?忘记恢复autoCommit?finally块处理不当?)。
幸运的是,Spring带来了声明式事务管理,特别是通过@Transactional注解,让事务控制变得无比简单和优雅。
读完本文,你将彻底搞懂:
-
事务的基本概念和ACID原则。
-
Spring声明式事务的核心原理(AOP的应用)。
-
如何轻松使用@Transactional注解管理事务。
-
@Transactional的关键属性(传播行为、隔离级别、回滚规则等)及其应用场景。
-
常见的@Transactional失效场景及原因分析(面试高频点!)。
-
使用@Transactional的最佳实践。
准备好为你的数据一致性加上“守护神光环”了吗?
一、温故知新:什么是事务 (Transaction)?
在深入Spring事务之前,我们快速回顾一下数据库事务的基础知识。
事务是一组原子性的操作单元,这些操作要么全部成功执行,要么全部不执行(回滚到初始状态)。事务旨在保证数据的一致性。
事务具有四个基本特性,通常被称为ACID:
-
原子性 (Atomicity): 事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。就像原子是物质的基本单位一样。
-
一致性 (Consistency): 事务执行前后,数据库都必须处于一致的状态。事务将数据库从一个一致性状态转变到另一个一致性状态。例如,转账前后,两个账户的总金额应该保持不变。
-
隔离性 (Isolation): 一个事务所做的修改在最终提交之前,对其他并发事务是不可见的(或有不同程度的可见性,取决于隔离级别)。这可以防止多个事务并发执行时互相干扰。
-
持久性 (Durability): 一旦事务成功提交,它对数据库所做的更改就是永久性的,即使后续系统发生崩溃也不会丢失。
理解ACID是理解事务管理价值的基础。
二、Spring的魔法棒:声明式事务管理
Spring的声明式事务管理是其AOP(面向切面编程)能力的经典应用。核心思想是:将事务管理的横切逻辑(开启事务、提交、回滚)从业务代码中分离出来,通过配置(早期XML,现在主要是注解@Transactional)的方式声明在需要事务管理的方法上。
当你调用一个被@Transactional标记的方法时,Spring AOP会创建一个代理对象来包装你的原始Bean。这个代理对象在调用实际业务方法之前会自动开始一个事务,在方法成功执行后自动提交事务,如果方法抛出(特定的)异常,则自动回滚事务。
底层机制:
Spring通过一个PlatformTransactionManager接口来统一不同的事务管理技术(如JDBC的DataSourceTransactionManager,JPA的JpaTransactionManager,Hibernate的HibernateTransactionManager等)。Spring Boot会根据你的项目依赖(如spring-boot-starter-data-jpa)自动配置合适的PlatformTransactionManager Bean。
三、@Transactional 实战:让事务管理“不留痕迹”
让我们用Spring Data JPA和@Transactional来重写之前的转账逻辑。
1. 准备工作 (假设已有):
-
UserAccount 实体类 (包含 id, userId, balance)
-
UserAccountRepository 接口 (继承 JpaRepository<UserAccount, Long>)
2. 创建 Service 层:
package com.example.service;import com.example.model.UserAccount;
import com.example.repository.UserAccountRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; // 导入注解
import java.math.BigDecimal;
import java.util.Optional;@Service
public class TransferService {private final UserAccountRepository accountRepository;@Autowiredpublic TransferService(UserAccountRepository accountRepository) {this.accountRepository = accountRepository;}// 核心转账方法: 使用 @Transactional 注解@Transactional // <--- 魔法就在这里!public void transferMoney(Long fromAccountId, Long toAccountId, BigDecimal amount) {System.out.println("Attempting to transfer " + amount + " from account " + fromAccountId + " to " + toAccountId);// 1. 扣款 (假设 findById 返回 Optional, orElseThrow 抛出异常如果账户不存在)UserAccount fromAccount = accountRepository.findById(fromAccountId).orElseThrow(() -> new IllegalArgumentException("Source account not found: " + fromAccountId));if (fromAccount.getBalance().compareTo(amount) < 0) {throw new InsufficientFundsException("Insufficient funds in account: " + fromAccountId);}fromAccount.setBalance(fromAccount.getBalance().subtract(amount));accountRepository.save(fromAccount); // JPA save 会执行 UPDATESystem.out.println("Debited " + amount + " from account " + fromAccountId + ". New balance: " + fromAccount.getBalance());// --- 模拟一个可能发生的错误 ---// if (true) { throw new RuntimeException("Simulated error after debit!"); }// 2. 加款UserAccount toAccount = accountRepository.findById(toAccountId).orElseThrow(() -> new IllegalArgumentException("Destination account not found: " + toAccountId));toAccount.setBalance(toAccount.getBalance().add(amount));accountRepository.save(toAccount); // JPA save 会执行 UPDATESystem.out.println("Credited " + amount + " to account " + toAccountId + ". New balance: " + toAccount.getBalance());System.out.println("Transfer completed successfully!");}
}// 自定义异常示例
class InsufficientFundsException extends RuntimeException {public InsufficientFundsException(String message) {super(message);}
}
代码对比:
看看现在的transferMoney方法!里面完全没有任何try-catch-finally、commit、rollback的代码。业务逻辑非常纯粹。只需要在方法上添加一个@Transactional注解,Spring就自动处理了所有事务相关的复杂性:
-
方法开始时,开启事务。
-
accountRepository.save()等操作都在同一个事务中执行。
-
如果方法正常结束,事务自动提交。
-
如果方法抛出运行时异常 (RuntimeException) 或 错误 (Error)(默认情况下),事务自动回滚。例如,如果账户不存在抛出IllegalArgumentException,或者余额不足抛出InsufficientFundsException,或者模拟的RuntimeException发生,之前的扣款操作会被撤销。
这就是声明式事务的强大之处!
四、深入@Transactional:掌控事务细节的关键属性
@Transactional不仅仅是一个开关,它还提供了多个属性来精细控制事务的行为:
-
propagation (传播行为 - 最重要!)
-
定义了当一个已存在事务的方法调用另一个带有@Transactional的方法时,事务应该如何传播。
-
常用值:
-
REQUIRED (默认): 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是最常用的设置,保证方法总是在事务中运行。
-
REQUIRES_NEW: 总是创建一个新的事务。如果当前存在事务,则将当前事务挂起。新事务有自己的提交/回滚,独立于外部事务。适用于需要独立事务单元的场景(如记录审计日志,无论主业务成功与否都要保存)。
-
SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。适用于不强制需要事务,但能参与现有事务的方法。
-
NOT_SUPPORTED: 以非事务方式执行操作。如果当前存在事务,则将当前事务挂起。
-
MANDATORY: 必须在一个已存在的事务中执行,否则抛出异常。
-
NEVER: 必须不在事务中执行,否则抛出异常。
-
NESTED: 如果当前存在事务,则在嵌套事务内执行。嵌套事务是外部事务的一部分,可以单独设置保存点 (Savepoint) 进行部分回滚。如果不存在事务,行为类似REQUIRED。**注意:**并非所有PlatformTransactionManager都支持NESTED(例如JpaTransactionManager通常不支持,需要特定的JDBC驱动和配置)。
-
-
示例场景: 假设transferMoney调用另一个logTransaction方法(也标记了@Transactional)。如果logTransaction使用REQUIRED,它会加入transferMoney的事务;如果使用REQUIRES_NEW,它会开启一个独立的日志事务,即使transferMoney后续失败回滚,日志记录也可能已经成功提交。
-
-
isolation (隔离级别)
-
定义了事务并发执行时,一个事务的修改对其他事务的可见程度。对应数据库的隔离级别。
-
常用值 (从低到高):
-
READ_UNCOMMITTED: 可能读取到其他事务未提交的数据(脏读)。性能最高,但数据一致性最差。很少使用。
-
READ_COMMITTED: 只能读取到其他事务已经提交的数据。解决了脏读,但可能出现不可重复读(同一事务内,两次读取同一数据可能得到不同结果,因为其他事务在此期间提交了修改)。大多数数据库的默认级别(如Oracle, PostgreSQL, SQL Server)。
-
REPEATABLE_READ: 保证在同一事务中多次读取同一数据时,结果总是一致的。解决了不可重复读,但可能出现幻读(同一事务内,两次执行范围查询,结果集可能包含新的“幻影”行,因为其他事务在此期间插入了新数据)。MySQL的默认级别。
-
SERIALIZABLE: 最高的隔离级别,强制事务串行执行,避免了脏读、不可重复读和幻读。性能最低,可能导致大量超时和锁竞争。
-
DEFAULT: 使用底层数据库的默认隔离级别。
-
-
选择建议: 通常使用数据库默认级别 (DEFAULT或READ_COMMITTED/REPEATABLE_READ)即可。只有在特定场景下需要避免不可重复读或幻读时,才考虑提升隔离级别,并注意其性能影响。
-
-
readOnly (只读)
-
true / false (默认)。
-
将事务标记为只读。这是一个优化提示,告诉数据库(和JPA提供者如Hibernate)这个事务不会执行任何写操作。
-
好处:
-
数据库可能应用一些只读优化(如避免不必要的锁)。
-
Hibernate等JPA提供者可以避免进行“脏检查”(检查实体状态是否改变),提升性能。
-
-
建议: 对于明确只执行查询操作的方法(如find..., get..., count...),强烈建议设置@Transactional(readOnly = true)。
-
-
timeout (超时时间)
-
指定事务允许执行的最长时间(单位:秒)。如果事务超时仍未完成,会被底层事务系统自动回滚。
-
例如:@Transactional(timeout = 30) // 30秒超时
-
用于防止长时间运行的事务锁定资源,影响系统性能。
-
-
rollbackFor 和 noRollbackFor (回滚规则 - 非常重要!)
-
默认行为: Spring事务默认只在遇到未检查异常 (Unchecked Exception),即RuntimeException及其子类,或者Error时,才会自动回滚。对于已检查异常 (Checked Exception),默认不回滚。
-
原因: 这是遵循EJB规范的设计哲学,认为检查异常通常代表可预期的、业务层可以处理的状况,不一定需要回滚事务。
-
定制行为:
-
rollbackFor: 指定哪些异常类型应该触发回滚(即使它们是检查异常)。
-
noRollbackFor: 指定哪些异常类型不应该触发回滚(即使它们是运行时异常)。
-
-
示例:
// 即使 MyBusinessException 是检查异常, 也触发回滚 @Transactional(rollbackFor = MyBusinessException.class) public void processOrder(Order order) throws MyBusinessException {// ...if (order.isInvalid()) {throw new MyBusinessException("Invalid order data");}// ... }// 即使 InsufficientFundsException 是运行时异常, 也不回滚 (可能用于记录失败尝试) @Transactional(noRollbackFor = InsufficientFundsException.class) public void attemptCharge(User user, BigDecimal amount) {try {chargeCard(user, amount);} catch (InsufficientFundsException e) {logFailedAttempt(user, amount, e);// 异常被捕获, 事务不会因为这个异常回滚} }
-
关键: 一定要清楚哪些异常会(或不会)导致你的事务回滚,并根据业务需求使用rollbackFor / noRollbackFor进行调整!
-
五、揭秘失效之谜:为何我的@Transactional不起作用?
这是面试和实际开发中经常遇到的问题。@Transactional看似简单,但其基于AOP代理的机制决定了它在某些情况下会“失效”:
-
方法不是public的: AOP代理默认(特别是使用CGLIB时)主要拦截public方法。将@Transactional用在private, protected或包可见方法上,通常会失效。解决方案: 始终将需要事务管理的方法声明为public。
-
方法是final的: 如果方法被final修饰,CGLIB代理(基于继承)无法覆盖该方法,事务增强会失效。解决方案: 移除final关键字。
-
方法是static的: static方法属于类而不是对象,AOP代理基于对象,无法代理静态方法。解决方案: 将逻辑移到非静态方法中。
-
同一个类中的方法调用 (自调用/Self-Invocation - 最常见!)
-
现象: 在一个类中,一个没有@Transactional注解的方法A,调用了同一个类中带有@Transactional注解的方法B (this.methodB())。
-
原因: 调用是通过this引用直接进行的,绕过了Spring生成的代理对象。代理对象的事务增强逻辑没有机会介入,因此方法B的事务不会生效。
-
解决方案 (几种思路):
-
推荐:将方法B移到另一个Bean中,然后注入这个新Bean来调用方法B。这是最清晰、最符合面向对象设计的方式。
-
注入自身代理: 在当前类中注入自身的代理对象(需要配置或使用@Lazy避免循环依赖),然后通过代理对象调用方法B。
@Service public class MyService {@Autowired @Lazy // 注意 @Lazy 可能需要private MyService self; // 注入自身代理public void methodA() {// ...self.methodB(); // 通过代理调用// ...}@Transactionalpublic void methodB() { // 这个事务会生效// ... 事务性操作 ...} } // 可能需要在配置类上开启 @EnableAspectJAutoProxy(exposeProxy = true)
-
使用TransactionTemplate编程式事务: 对于复杂场景,可以回退到编程式事务。
-
-
-
异常被catch ولم re-throw: 如果在@Transactional方法内部catch了一个应该导致回滚的异常(如RuntimeException),并且没有将其重新抛出(或者手动调用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()),那么Spring的事务管理器就不知道发生了错误,事务不会回滚。
@Transactional public void badCatchExample() {try {// ... 执行可能抛出 RuntimeException 的操作 ...riskyOperation();} catch (RuntimeException e) {// 错误! 异常被捕获但没有处理或重新抛出, 事务可能不会回滚!System.err.println("Caught exception, but transaction might commit! " + e.getMessage());// 正确做法: 1. 向上抛出 e; 2. 抛出自定义异常; 3. 调用 setRollbackOnly()} }
-
数据库引擎不支持事务: 确保你使用的数据库表类型支持事务(例如,MySQL的MyISAM引擎就不支持事务,需要使用InnoDB等)。
-
propagation配置不当: 错误地使用了NOT_SUPPORTED, NEVER等传播行为,导致方法没有在预期的事务中运行。
理解这些失效场景对于正确使用@Transactional至关重要。
六、最佳实践
-
应用层面: 通常将@Transactional注解应用在Service层的方法上,而不是Repository/DAO层。Service层代表一个完整的业务操作单元,更适合作为事务边界。
-
粒度: 尽量让事务方法的粒度适中,包含一个完整的业务逻辑单元,避免过大或过小的事务。
-
只读优化: 对于所有只读操作,坚持使用@Transactional(readOnly = true)。
-
明确回滚规则: 仔细考虑你的业务异常,必要时使用rollbackFor / noRollbackFor定制回滚策略。
-
避免自调用陷阱: 注意同一个类中方法的调用问题,优先通过重构解决。
-
测试: 编写集成测试来验证你的事务行为是否符合预期(例如,验证异常发生时数据是否真的回滚了)。
七、总结:让数据一致性无忧
Spring的声明式事务管理,特别是@Transactional注解,是现代Java开发中保障数据一致性的利器。它通过AOP将复杂的事务控制逻辑与业务代码解耦,让我们只需一个简单的注解就能获得强大的事务支持。
掌握@Transactional的基本用法及其核心属性(传播行为、隔离级别、回滚规则等),并理解其背后的代理机制以及常见的失效场景,将使你能够编写出更加健壮、可靠、易于维护的服务端应用程序。
@Transactional就像一个尽职尽责的守护神,默默地保护着你的数据完整性,让你能更专注于实现核心业务价值。
相关文章:
数据一致性的守护神:深入Spring声明式事务管理 (@Transactional)
在前面几篇文章中,我们一路打怪升级,掌握了Spring IoC/DI、Bean管理、AOP,并用JdbcTemplate和Spring Data JPA简化了数据库访问。我们的代码越来越简洁、高效。但想象一下这个场景: 用户A向用户B转账100元。这至少需要两个数据库…...
神经网络复习
1 机器学习 1.1 定义 机器学习是人工智能的一个分支领域,它致力于让计算机系统能够自动从数据中学习规律,并利用这些规律对新的数据进行预测或决策,而无需显式地编写针对特定任务的规则。 1.2 分类 全监督学习 全监督学习是指在训练模型时…...
从代码学习深度学习 - 自注意力和位置编码 PyTorch 版
这里写自定义目录标题 前言一、自注意力:Transformer 的核心1.1 多头注意力机制的实现1.2 缩放点积注意力1.3 掩码和序列处理1.4 自注意力示例二、位置编码:为序列添加位置信息2.1 位置编码的实现2.2 可视化位置编码总结前言 深度学习近年来在自然语言处理、计算机视觉等领域…...
Web前端开发——图像与多媒体文件(上)
学习目标: 优秀的商业网站往往通过为页面添加大量的图像、声音、视频、动画等多媒体信息来丰富网站的内容,吸引更多网络访问者的关注。目前大型商业网站非常注重对Web前端开发技术的研究,通过组合各类前端开发技术来改善用户体验和增加用户互…...
C#核心学习(三)常见的泛型数据结构类(1)List和Dictionary
前面我们刚刚学习了,什么是泛型。今天我们就来看看C#中有哪些,常见的泛型数据结构,今天要介绍的是List,和Dictionary。 引言 在C#编程中,泛型集合是高效管理数据的核心工具。List<T>和Dictionary<TKey, TValue>作为两…...
运行时数据区
运行时数据区 Java 虚拟机在运行 Java 程序过程中管理的内存区域称之为运行时数据区,运行时数据区负责管理 JVM 使用到的内存,例如创建对象和销毁对象。 程序计数器 程序计数器又叫 PC 寄存器,每个线程都会通过程序计数器记录当前要执行的字…...
union all 关联查询
UNION ALL 并非用于表的关联查询,而是用于将多个 SELECT 语句的结果集合并成一个结果集。它会保留所有重复的行,不像 UNION 会去除重复行。下面为你提供几种使用 UNION ALL 的场景示例。 示例表结构 假设存在两个表 sales_2024 和 sales_2025ÿ…...
npm ERR! vue-admin-beautiful@1.0.0 dev: `vue-cli-service serve` 问题解决
笔者在跑实习公司的前端时,老是报这个错,并且网上(csdn)并没有好的解决方法,于是这篇文章就诞生了。[吐槽一下]:小公司的实习生干的活是真的多啊。。。 解决方案 依赖有些包装不上,换个源就行了,这里采用…...
玩转Docker | 使用Docker部署Xnote笔记工具
玩转Docker | 使用Docker部署Xnote笔记工具 前言一、Xnote介绍Xnote简介1.2 Xnote特点二、系统要求环境要求环境检查Docker版本检查检查操作系统版本三、部署Xnote服务下载镜像编辑配置文件编辑部署文件创建容器检查容器状态检查服务端口安全设置四、访问Xnote服务访问Xnote首页…...
TDengine 与其他时序数据库对比:InfluxDB/TimescaleDB 选型指南(二)
四、应用场景分析 (一)TDengine 适用场景 TDengine 适用于对写入性能和存储效率要求极高的物联网设备数据采集场景。在一个拥有数百万个传感器的智能工厂中,每个传感器每秒都会产生多条数据,TDengine 能够高效地处理这些高并发的…...
ARM Cortex汇编宏定义
在ARM架构(尤其是Cortex-M系列MCU)的汇编中,**宏定义(Macro)**用于复用代码片段,支持参数化编程,简化重复逻辑(如寄存器操作、循环、条件判断等)。以下是ARM汇编宏定义的核心语法、用法及示例: 一、宏定义的基本语法 使用 MACRO 和 MEND 伪指令包裹宏体,通过 参数…...
【含文档+PPT+源码】基于Python心理健康管理系统
项目视频介绍: 毕业作品心理健康管理系统 课程简介: 本课程演示的是一款基于Python心理健康管理系统,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Python学习者。 1.包含:项目源码、项目文档、数据库脚本、软…...
Dockerfile项目实战-单阶段构建Vue2项目
单阶段构建镜像-Vue2项目 1 项目层级目录 以下是项目的基本目录结构: 2 Node版本 博主的Windows电脑安装了v14.18.3的node.js版本,所以直接使用本机电脑生成项目,然后拷到了 Centos 7 里面 # 查看本机node版本 node -v3 创建Vue2项目 …...
大数据驱动的供应链透明化:从数据孤岛到智能协同
大数据驱动的供应链透明化:从数据孤岛到智能协同 在全球化供应链环境下,透明化不仅是一种企业责任,更是提高效率、降低成本和增强供应链韧性的关键举措。然而,传统供应链体系因数据孤岛、信息滞后、信任缺失等问题,常常导致生产过剩、库存积压、资源浪费,甚至供应链断裂…...
第四篇:[特殊字符] 深入理解MyBatis[特殊字符] 掌握MyBatis Generator ——入门与实战
引言 什么是 MyBatis Generator? MyBatis Generator (MBG) 是一个代码生成工具,专为 MyBatis 框架设计。它可以根据数据库表结构自动生成 Java 实体类、Mapper 接口、Mapper XML 文件以及 Example 类。通过使用 MBG,开发者可以显著减少编写…...
LeetCode算法题(Go语言实现)_48
题目 在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一: 值 0 代表空单元格; 值 1 代表新鲜橘子; 值 2 代表腐烂的橘子。 每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。 返回 直到单元格中…...
ESP-ADF外设子系统深度解析:esp_peripherals组件架构与核心设计(核心API详解之单个外设管理)
目录 单个外设管理APIesp_periph_createesp_periph_set_functionesp_periph_startesp_periph_stopesp_periph_set_dataesp_periph_get_dataesp_periph_get_stateesp_periph_get_idesp_periph_set_idesp_periph_initesp_periph_runesp_periph_destroy 单个外设管理API esp_peri…...
基于vue2+ElementUI的el-tree封装一个带搜索的树形组件
需求 实现一个如图带搜索框的下拉树形组件。 解决方案 利用el-inputel-tree实现自定义带搜索的下拉树形组件。 具体实现步骤 1、创建TreeSelect组件 <template><div class"tree-select-wrapper" v-clickoutside"handleClose"><el-inpu…...
G2学习打卡
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 DCGAN实践 import torch, random, random, os import torch.nn as nn import torch.nn.parallel import torch.optim as optim import torch.utils.data im…...
【广州华锐互动】汽车生产引入数字孪生系统,优化生产流程,提升汽车产品质量
数字孪生系统的应用为企业带来了生产流程的革命性变革。以汽车制造企业为例,该企业在生产过程中引入数字孪生系统,实现了生产流程的全面优化和产品质量的显著提升 。 在生产流程优化方面,数字孪生系统对汽车生产线进行了全方位的模拟和实时…...
从Gradio App创建Discord Bot/Slack Bot/Website Widget(2)——从Gradio App创建Slack Bot
从Gradio App创建Discord Bot/Slack Bot/Website Widget(2)——从Gradio App创建Slack Bot 本篇摘要18. 从Gradio App创建Discord Bot/Slack Bot/Website Widget18.2 从Gradio App创建Slack Bot18.2.1 运作原理及前置条件1. 运作原理2. 前置条件 18.2.2 …...
基于STM32G474的SPI获取MT6816编码器绝对角度配置指南
前言:最近上手使用了一款编码器芯片,也是先艰难阅读了一下全英文版本的编码器的规格手册,然后通过SPI读取了一下绝对值角度。虽然发现使用起来还是挺简单的,但使用后还是会产生一个对其原理层面的好奇,比如磁编码器内部…...
深入学习ReentrantLock
ReentrantLock 0. 前言:为什么需要 ReentrantLock?1. 基础概念与核心特性1.1 什么是 ReentrantLock?1.2 ReentrantLock vs. synchronized1.3 核心特性详解1.3.1 可重入性 (Reentrancy)1.3.2 公平性选择 (Fairness Choice)1.3.3 可中断获取锁 …...
Spring Boot 集成金蝶 API 演示
✨ Spring Boot 集成金蝶 API 演示:登录 / 注销 Cookie 保存 本文将通过 Spring Boot 完整实现一套金蝶接口集成模型,包括: ✅ 普通登录✅ AppSecret 登录✅ 注销✅ Cookie 保存与复用 📅 项目结构 src/ ├── controller/ │…...
适用于 HAL 的 AIDL
目录 设计初衷 注意 编写AIDLHAL接口 查找AIDLHAL接口 扩展接口 将现有HAL从HIDL转换为AIDL AIDL与HIDL之间的主要差异 针对HAL的供应商测试套件(VTS)测试 Android 11 中引入了在 Android 中使用 AIDL 实现 HAL 的功能, 从而可以在不使用 HIDL 的情况下实现 Android 的部分…...
49、Spring Boot 详细讲义(六)(SpringBoot2.x整合Mybatis实现CURD操作和分页查询详细项目文档)
项目文档:银行借据信息CURD操作和分页查询 一、项目概述 1. 项目简介 本项目旨在使用Spring Boot框架整合MyBatis连接Mysql数据库实现借据信息的增加、删除、修改和查询功能,同时支持分页查询,并提供对应的Restful风格的接口。 2.环境准备 2.1.工具和软件准备 JDK(建议…...
C# 运行web项目
1、web项目直接点击顶部运行...
GPU服务器声音很响可以怎么处理
当GPU服务器运行时噪音过大,通常是由于高负载下散热风扇高速运转所致。以下是分步骤的解决方案,帮助您有效降低噪音并保持设备稳定运行: 一、排查噪音来源 定位声源 • 使用 声级计 或手机分贝检测APP,确定最大噪音位置࿰…...
Java如何选择ojdbc驱动
如何选择ojdbc驱动? 取决于短板。 如果JDK版本高,数据库版本低,根据Oracle数据库版本选择。如果JDK版本低,数据库版本高,根据Java版本选择。 Oracle官网OJDBC驱动和受支持的JDK版本 23ai 21c 19c 驱动类型选择 oj…...
【微思就业推荐 】T岗位-北京,福州,厦门等地
到微思学习,免费推荐就业!学员内推! 原创 厦门微思网络 2025年04月 有哪些大公司在招OCP认证人才? 有哪些大公司在招聘拥有HCIE认证的人才 ① 委托单位:润欣商业管理(厦门)有限公司 央企-华润资产的子公司 岗位&am…...
Linux 命令全解析:从零开始掌握 Linux 命令行
Linux 作为一款强大的开源操作系统,广泛应用于服务器、嵌入式系统以及超级计算机领域。掌握 Linux 命令行技能,是每一位开发者和系统管理员的必备能力。本文将从基础开始,为你详细介绍常用的 Linux 命令,以及它们的使用场景和示例…...
2025年4月份生活有感
今天在5000B培训的下午,一起入所来的小伙伴,有个申请了深圳大学的博士,已录取。哎,想起了当年申博时候信心和决心不足,导致后面匆匆的拿了offer去工作。看到同事的选择还是非常羡慕,想到自己5月份的婚礼&am…...
鸿蒙系统开发状态更新字段区别对比
在鸿蒙系统开发中,状态管理是构建响应式UI的核心机制,主要通过装饰器(Decorators)实现字段的状态观测与更新。根据鸿蒙的版本(V1稳定版和V2试用版),支持的装饰器及其特性有所不同。以下是主要状…...
CEPH OSD_SLOW_PING_TIME_FRONT/BACK 警告处理
ceph config set mgr mon_warn_on_slow_ping_time 2000说明:mon_warn_on_slow_ping_time 该值默认为0,那么只要 osd 心跳超过 mon_warn_on_slow_ping_ratio of osd_heartbeat_grace. 也就是超过 mon_warn_on_slow_ping_ratio和mon_warn_on_slow_ping_rat…...
HTML应用指南:利用POST请求获取全国小菜园门店位置信息
小菜园作为一家以徽菜为主的快餐品牌,自2013年成立以来,凭借其独特的烹饪理念和精致的东方口味菜品,在中国市场上迅速崛起。该品牌强调少油少盐、减少调味品使用,旨在传承并发扬徽州风味的独特魅力。这种健康且不失美味的烹饪方式…...
Python在去中心化物联网中的应用:数据安全、智能合约与边缘计算的融合
Python在去中心化物联网中的应用:数据安全、智能合约与边缘计算的融合 在万物互联的时代,传统物联网(IoT)架构依赖于集中式服务器来管理数据、设备互联与身份认证。然而,随着设备数量激增,中心化架构的可扩展性、安全性和隐私问题逐渐暴露。去中心化物联网(DeIoT)通过…...
CEPH配置优化建议
一、硬件配置优化 磁盘选择: SSD 与 HDD 搭配:使用 SSD 作为 OSD 日志盘(Journal)或元数据存储,HDD 作为数据盘。推荐 SSD 与 HDD 的比例为 1:3~5,具体根据业务负载调整。 RAID 禁用:避免使用硬…...
深度学习入门:神经网络的学习
目录 1 从数据中学习1.1 数据驱动1.2 训练数据和测试数据 2损失函数2.1 均方误差2.2 交叉熵误差2.3 mini-batch学习2.4 mini-batch版交叉熵误差的实现2.5 为何要设定损失函数 3 数值微分3.1 数值微分3.3 偏导数 4 梯度4.1 梯度法4.2 神经网络的梯度 5 学习算法的实现5.1 2层神经…...
机器学习_决策树
决策树的特点 可以处理非线性的问题可解释强,没有θ模型简单,模型预测效率高 if else不容易显示的使用函数表达,不可微 决策树的生成和预测 生成:通过大量数据生成一颗非常好的树,用这棵树来预测新来的数据。 预测&…...
深入理解UML动态图:系统行为建模全景指南
目录 前言1. 动态图概述2. 用例图(Use Case Diagram)2.1 定义与作用2.2 应用价值2.3 实践建议 3. 顺序图(Sequence Diagram)3.1 定义与特征3.2 应用优势3.3 建模建议 4. 活动图(Activity Diagram)4.1 定义与…...
Linux驱动开发进阶(九)- SPI子系统BSP驱动
文章目录 1、前言2、SPI总线注册3、SPI设备注册4、SPI驱动注册5、SPI BSP驱动 1、前言 学习参考书籍以及本文涉及的示例程序:李山文的《Linux驱动开发进阶》本文属于个人学习后的总结,不太具备教学功能。 2、SPI总线注册 驱动源码文件:dri…...
wabpack学习记录
wabpack学习记录 前言 项目写了不少 对webpack了解甚少 只记住一些 必要的概念以及指令 所以像深究一下具体是什么 可以做什么 如何做等 package.json 文件详解 name: 项目的名称。 version: 项目的版本号。 description: 项目的描述。 author: 项目的作者或维护者信息。 l…...
计算机视觉——基于 Yolov8 目标检测与 OpenCV 光流实现目标追踪
1. 概述 目标检测(Object Detection)和目标追踪(Object Tracking)是计算机视觉中的两个关键技术,它们在多种实际应用场景中发挥着重要作用。 目标检测指的是在静态图像或视频帧中识别出特定类别的目标对象࿰…...
React 更新 state 中的数组
更新 state 中的数组 数组是另外一种可以存储在 state 中的 JavaScript 对象,它虽然是可变的,但是却应该被视为不可变。同对象一样,当你想要更新存储于 state 中的数组时,你需要创建一个新的数组(或者创建一份已有数组…...
[250415] OpenAI 推出 GPT-4.1 系列,支持 1M token
目录 OpenAI 推出 GPT-4.1 系列 OpenAI 推出 GPT-4.1 系列 OpenAI 宣布,新一代 GPT-4.1 模型系列正式发布,包括 GPT-4.1, GPT-4.1 mini 和 GPT-4.1 nano 三款模型,该系列模型在各项性能指标上全面超越 GPT-4o 和 GPT-4o mini,尤其…...
分布式锁+秒杀异步优化
文章目录 问题思路setnx实现锁误删问题和解决方案Redis Lua脚本问题引出解决方案 setnx实现的问题Redission快速入门redission可重入锁原理 秒杀优化(异步优化)异步秒杀思路秒杀资格判断Redis消息队列 问题 比如我们两个机器都部署了我们项目,这里nginx使用轮询的方…...
数据服务化 VS 数据中台:战略演进中的价值重构
在企业数据战略的演进历程中,数据中台曾被视为解决数据孤岛的 “万能钥匙”,而数据服务化的兴起则标志着企业从 “数据资源囤积” 向 “数据价值释放” 的深刻转型。两者的核心差异不仅在于技术架构,更在于对数据资产的定位与使用理念的根本分…...
PL/SQL登录慢,程序连接Oracle 提示无法连接或无监听
PL/SQL登录慢,程序连接Oracle 提示无法连接或无监听 错误提示:ORA-12541: TNS: 无监听程序 的解决办法, 现象:PL/SQL登录慢,程序连接Oracle 提示无法连接或无监听 监听已经正常开起,但还是PL/SQL登录慢或…...
【JAVAFX】自定义FXML 文件存放的位置以及使用
情况 1:FXML 文件与调用类在同一个包中(推荐) 假设类 MainApp 的包是 com.example,且 FXML 文件放在 resources/com/example 下: 项目根目录 ├── src │ └── sample │ └── Main.java ├── src/s…...
DDoS(分布式拒绝服务)攻击
DDoS(分布式拒绝服务)攻击 这是一份全面系统的 DDoS(分布式拒绝服务攻击)知识总结,适合用于学习、报告、讲稿或者面试准备。内容涵盖定义、原理、危害、利用、工具、防护策略等。 一、什么是DDoS DDoS(Distributed Denial of Se…...