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

Spring事务和事务的传播机制

目录

Spring中事务的实现

MySQL中的事务使用

Spring 编程式事务

TransactionTemplate 编程式事务

TransactionManager编程式事务

 Spring声明式事务

@Transactional 参数说明

事务因为程序异常捕获不会自动回滚的解决方案

@Transactional 原理

 Spring 事务隔离级别

Spring事务失效的场景

非public修饰的方法

为什么非public修饰的方法使用@Transactional会失效?

timeout超时

代码中有try/catch

调用类内部@Transactional方法

数据库不支持事务

Spring 事务传播机制

事务隔离级别和传播机制有什么区别?

① REQUIRES_NEW: 新建事务执行,如果当前存在事务,把当前事务挂起。

② REQUIRED (默认值):如果当前方法没有事务,新建一个事务,如果已经存在一个事务中,则加入到这个事务中。

 ③ NESTED(嵌套事务):如果当前存在事务,则在嵌套事务内执行。如果不存在,则执行与PROPAGATION_REQUIRED 类似的操作。

加入事务和嵌套事务有什么区别?

嵌套事务的实现原理:


Spring中事务的实现

Spring中的事务操作分为两类:

  • 编程式事务 (手动写代码操作事务)
  • 声明式事务(利用注解自动开启和提交事务)

MySQL中的事务使用

事务在MySQL有3个重要的操作:开启事务、提交事务、回滚事务,它们对应的操作命令如下:

-- 开启事务
start transaction;
-- 业务执行-- 提交事务
commit;-- 回滚事务
rollback;

Spring 编程式事务

Spring 手动操作事务和上面MySQL操作事务类似,他也是有3个重要操作步骤:

  • 开启事务(获取事务)
  • 提交事务
  • 回滚事务

编程式事务有两种实现方式

  • 使用 TransactionTemplate 对象实现编程式事务
  • 使用更加底层的 TransactionManager 对象实现编程式事务

TransactionTemplate 编程式事务

要使用 TransactionTemplate 对象需要先将 TransactionTemplate 注入到当前类中,然后再使用它提供的 execute 方法执行事务并返回相应的执行结果,如果程序在执行途中出现了异常,那么就可以使用代码手动回滚事务,具体实现代码如下:

TransactionManager编程式事务

TransactionManager 实现编程式事务相对麻烦一点,它需要使用两个对象TransactionManager 的子类,加上 TransactionDefinition 事务定义对象,再通过调用TransactionManager的 getTransaction 获取并开启事务,然后调用 TransactionManager 提供的 commit 方法提交事务,或使用它的另一个方法 rollback 回滚事务,它的具体实现代码如下:

SpringBoot内置了两个对象:

  • DataSourceTransactionManager: 事务的管理器, 是用来获取事务(开启事务)、提交或回滚事务的。
  • TransactionDefinition :事务的属性,在获取事务的时候需要将TransactionDefinition传递进去从而获得一个事务 TransactionStatus。

实现代码如下:

package com.example.demo.controller;import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController // 这是@Controller + @ResponseBody 的结果
//目的是返回非页面数据 
@RequestMapping("/user")
public class UserController {//编程式事务@Autowiredprivate DataSourceTransactionManager dataSourceTransactionManager;@Autowiredprivate TransactionDefinition transactionDefinition;@Autowiredprivate UserService userService;@RequestMapping("/del")public int del(Integer id) {if (id == null || id <= 0) return 0;TransactionStatus transactionStatus = null;int result = 0;try {// 1. 开启事务transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);// 业务操作 删除用户result = userService.del(id);System.out.println("删除:" + result);dataSourceTransactionManager.commit(transactionStatus); // 提交事务} catch (Exception e) {if (transactionStatus != null) {dataSourceTransactionManager.rollback(transactionStatus); // 回滚事务}}return result;}
}

 Spring声明式事务

声明式事务:只需要事务的⽅法上添加@Transactional 注解就可以实现了
⽆需⼿动开启事务和提交事务,添加该注解后实现的效果如下:

  1. 进⼊⽅法时自动开启事务。
  2. 方法执行完会自动提交事务。
  3. 如果中途发⽣了没有处理的异常会自动回滚事务。
@RestController
public class UserController2 {@Autowiredprivate UserService userService;@RequestMapping("/del")@Transactionalpublic int del(Integer id) {if (id == null || id <= 0) return 0;int result = userService.del(id);System.out.println("删除: " + result);// int num = 10 / 0; return result;}
}

 如果上面的异常触发,就会导致事务回滚,如果没有,事务则正常提交。

需要注意的一点是:这里的@Transactional 与单元测试中的@Transactional不一样。

单元测试中的@Transactional 是: 无论方法是执行完方法后,一定回滚事务。

@Transactional的作用

@Transactional 可以⽤来修饰方法或类:

  • 修饰⽅法时: 只有修饰public⽅法时才⽣效(修饰其他⽅法时不会报错,也不生效)[推荐]
  • 修饰类时: 对 @Transactional 修饰的类中所有的public方法都生效.

@Transactional 参数说明

 需要注意的是:

@Transactional 在异常被捕获的情况下,不会进行事务自动回滚:

package com.example.demo.controller;import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user2")
public class UserController2 {@Autowiredprivate UserService userService;@RequestMapping("/del")@Transactional()public int del(Integer id) {if (id == null || id <= 0) return 0;int result = userService.del(id);System.out.println("删除: " + result);try {int num = 10 / 0;} catch (Exception e) {e.printStackTrace();}return result;}
}

观察代码运行结果,可以发现:由于异常被程序捕获(try-catch),事务不会进行回滚,数据被删除:

其实这是因为 @Transactional 在底层实现的时候是通过代理类来完成的,代理类会通过反射拿到目标方法,如果目标方法出现异常会进行回滚操作,否则就提交当前事务。

因此,如果我们在程序中显示的捕获异常,那么 @Transactional 里面的代理类就无法捕获到异常,于是就提交了事务。

事务因为程序异常捕获不会自动回滚的解决方案

方案①:将异常重新抛出

对于捕获的异常,事务是会自动回滚的,因此解决方案就是将异常重新抛出:

package com.example.demo.controller;import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user2")
public class UserController2 {@Autowiredprivate UserService userService;@RequestMapping("/del")@Transactional()public int del(Integer id) {if (id == null || id <= 0) return 0;int result = userService.del(id);System.out.println("删除: " + result);try {int num = 10 / 0;} catch (Exception e) {throw e;}return result;}
}

运行结果:

方案②:手动回滚事务

⼿动回滚事务,在⽅法中使⽤ TransactionAspectSupport.currentTransactionStatus() 可
以得到当前的事务,然后设置回滚⽅法 setRollbackOnly 就可以实现回滚了:

@Transactional 原理

@Transactional 是基于 AOP 实现的,AOP ⼜是使⽤动态代理实现的。如果⽬标对象实现了接⼝,默认情况下会采⽤ JDK 的动态代理,如果⽬标对象没有实现了接⼝,会使⽤ CGLIB 动态代理。
@Transactional 在开始执⾏业务之前,通过代理先开启事务,在执⾏成功之后再提交事务。如果中途遇到的异常,则回滚事务:

 实现细节:

 Spring 事务隔离级别

Spring 中事务隔离级别包含以下 5 种:

  • Isolation.DEFAULT:以连接的数据库的事务隔离级别为主。
  • Isolation.READ_UNCOMMITTED:读未提交,可以读取到未提交的事务,存在脏读。
  • Isolation.READ_COMMITTED:读已提交,只能读取到已经提交的事务,解决了脏读,存在不可重复读。
  • Isolation.REPEATABLE_READ:可重复读,解决了不可重复读,但存在幻读(MySQL默认级别)。
  • Isolation.SERIALIZABLE:串⾏化,可以解决所有并发问题,但性能太低。

相比于 MySQL 的事务隔离级别,Spring 的事务隔离级别只是多了⼀个 Isolation.DEFAULT(以数据库的全局事务隔离级别为主)。
Spring 中事务隔离级别只需要设置 @Transactional ⾥的 isolation 属性即可,具体实现代码如下:

Spring事务失效的场景

在开始之前,我们先要明确一个定义,什么叫做“失效”?

本文中的“失效”指的是“失去(它的)功效”,也就是当 @Transactional 不符合我们预期的结果时,我们就可以说 @Transactional 失效了。

那 @Transactional 失效的场景有哪些呢?接下来我们一一来看。

非public修饰的方法

当 @Transactional 修饰的方法为非 public 时,事务就失效了,比如以下代码当遇到异常之后,不能自动实现回滚:

@Transactional 
@RequestMapping("/save")
int save(UserInfo userInfo) {// 非空效验if (userInfo == null ||!StringUtils.hasLength(userInfo.getUsername()) ||!StringUtils.hasLength(userInfo.getPassword()))return 0;// 执行添加操作int result = userService.save(userInfo);System.out.println("add 受影响的行数:" + result);int num = 10 / 0; // 此处设置一个异常return result;
}
为什么非public修饰的方法使用@Transactional会失效?

答: 这分为两层原因: 浅层原因 和 深层次原因.
1. 浅层原因
浅层原因是 @Transactional 源码限制了必须是 public 才能执行后续的代理流程,它的部分实现源码如下:

protected TransactionAttribute computeTransactionAttribute(Method method, Class<?>
targetClass) {// Don't allow no-public methods as required.// 非 public 方法,设置为 nul1if(allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}// 后面代码省略....
}

2. 深层次原因
深层次的原因 Spring Boot 的动态代理只能代理公共方法,而不能代理私有方法或受保护的方法。

这是因为 Spring 的动态代理是基于 Java 的接口代理机制或者基于 CGLib 库来实现的,而这两种代理方式都只能代理公共方法。

  • 接口代理: 当目标类实现了接口时,Spring 使用JDK 动态代理来生成代理对象。JDK 动态代理是通过生成实现目标类接口的匿名类,并将方法调用委托给目标类的实例来实现的。由于接口中的方法都是公共的,所以JDK 动态代理只能代理公共方法。
  • CGLib 代理: 当目标类没有实现接门时,Spring 使用 CGLib 动态代理来生成代理对象CGLib 动态代理是通过生成目标类的子类,并将方法调用委托给子类的实例来实现的。然而Java 中的继承要求子类能够继承父类的方法,因此 CGLib 动态代理也只能代理目标类中的公共方法。

timeout超时

当在 @Transactional 上,设置了一个较小的超时时间时,如果方法本身的执行时间超过了设置的 timeout 超时时间,那么就会导致本来应该正常插入数据的方法执行失败,示例代码如下:

@Transactional(timeout = 3) // 超时时间为 3s
@RequestMapping("/save")
int save(UserInfo userInfo) throws InterruptedException {// 非空效验if (userInfo == null ||!StringUtils.hasLength(userInfo.getUsername()) ||!StringUtils.hasLength(userInfo.getPassword()))return 0;int result = userService.save(userInfo);return result;
}

 UserService 的 save 方法实现如下:

public int save(UserInfo userInfo) throws InterruptedException {// 休眠 5sTimeUnit.SECONDS.sleep(5);int result = userMapper.add(userInfo);return result;
}

代码中有try/catch

在前面 @Transactional 的执行流程中,我们提到:当方法中出现了异常之后,事务会自动回滚。然而,如果在程序中加了 try/catch 之后,@Transactional 就不会自动回滚事务了,示例代码如下:

@Transactional
@RequestMapping("/save")
public int save(UserInfo userInfo) throws InterruptedException {// 非空效验if (userInfo == null ||!StringUtils.hasLength(userInfo.getUsername()) ||!StringUtils.hasLength(userInfo.getPassword()))return 0;int result = userService.save(userInfo);try {int num = 10 / 0; // 此处设置一个异常} catch (Exception e) {}return result;
}

调用类内部@Transactional方法

当调用类内部的 @Transactional 修饰的方法时,事务是不会生效的,示例代码如下:

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class MyService {// 这个outerMethod方法的@Transactional无所谓有没有标注@Transactionalpublic void outerMethod() {// 调用内部方法innerMethod();}@Transactionalpublic void innerMethod() {// 这里的事务可能不会生效// ...}
}

说明:因为 @Transactional 是基于动态代理实现的,而当调用类内部的方法时,不是通过代理对象完成的,而是通过 this 对象实现的,这样就绕过了代理对象,从而事务就失效了。

数据库不支持事务

我们程序中的 @Transactional 只是给调用的数据库发送了:开始事务、提交事务、回滚事务的指令,但是如果数据库本身不支持事务,比如 MySQL 中设置了使用 MyISAM 引擎,那么它本身是不支持事务的,这种情况下,即使在程序中添加了 @Transactional 注解,那么依然不会有事务的行为,这就是巧妇也难为无米之炊吧。

Spring 事务传播机制

Spring 事务传播机制定义了多个包含了事务的⽅法: 相互调用时,事务是如何在这些⽅法间进⾏传递的。

比如有两个⽅法A,B都被 @Transactional 修饰,A⽅法调⽤B⽅法
A方法运行时,会开启⼀个事务.当A调⽤B时,B⽅法本⾝也有事务,此时B⽅法运⾏时,是加⼊A的事务,还是创建⼀个新的事务呢?这个就涉及到了事务的传播机制.

比如公司流程管理
执行任务之前,需要先写执行⽂档,任务执行结束,再写总结汇报。


此时A部门有⼀项⼯作,需要B部门的⽀援,此时B部门是直接使⽤A部门的⽂档,还是新建⼀个⽂档呢?
 

事务隔离级别和传播机制有什么区别?

事务的隔离级别是保证多个并发事务执行的可控性(稳定性),而事务传播机制是保证一个事务在多个调用方法间的可控性(稳定性)。

事务隔离级别描述的是多个事务同时执行时的某种行为;

而事务传播机制是描述包含了多个事务的方法在相互调用时事务的传播行为。

所以事务隔离级别描述的是纵向事务并发调用时的行为模式,而事务传播机制描述的是横向事务传递时的行为模式,如下图所示:

Spring 事务传播机制包含以下 7 种:

  1. Propagation.REQUIRED:默认的事务传播级别,它表示如果当前存在事务,则加⼊该事务;如果当前没有事务,则创建⼀个新的事务。
  2. Propagation.SUPPORTS:如果当前存在事务,则加⼊该事务;如果当前没有事务,则以⾮事务的⽅式继续运⾏。
  3. Propagation.MANDATORY:(mandatory:强制性)如果当前存在事务,则加⼊该事务;如果当前没有事务,则抛出异常。
  4. Propagation.REQUIRES_NEW:表示创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部⽅法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部⽅法会新开启⾃⼰的事务,且开启的事务相互独⽴,互不⼲扰。
  5. Propagation.NOT_SUPPORTED:以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。
  6. Propagation.NEVER:以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。
  7. Propagation.NESTED:如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED。

以上 7 种传播⾏为,可以根据是否⽀持当前事务分为以下 3 类:

为了加深印象,我们这里就演示其中的三种传播机制:

调用链关系:Controller --> UserService -> LogService

Controller:

UserService: 

LogService: 

 方便演示,这里更改传播机制时候是整个调用链一起修改。

① REQUIRES_NEW: 新建事务执行,如果当前存在事务,把当前事务挂起。

预期运行结果:用户添加成功,日志添加失败。 

实际运行结果:没有添加任何记录。

 

分析:因为没有将程序异常进行处理,导致整条调用链上的事务感知到,从而全部回滚。 

改进方法,将异常捕获,进行回滚:

运行结果:符合预期,日志出现回滚,用户加入操作没有回滚。 

② REQUIRED (默认值)
:如果当前方法没有事务,新建一个事务,如果已经存在一个事务中,则加入到这个事务中。

预期运行结果:全部事务都进行回滚。

运行结果:全部事务进行回滚。

这里即使我们进行异常处理,还是出现报错的原因是因为:这里使用REQUIRED 是把这些事务看成一个整体,而事务外部觉得不应该回滚,因为没有出现异常,而事务内部进行回滚了,所以这里程序不知道什么情况,所以报500了。

但是如果是外部事务进行回滚,那么内部事务也会进行回滚。(这时候不会报异常)

 ③ NESTED(嵌套事务):如果当前存在事务,则在嵌套事务内执行。如果不存在,则执行与PROPAGATION_REQUIRED 类似的操作。

 执行结果,日志添加失败,用户添加成功。

这里的效果跟REQUIRES_NEW是很像的。

看到这里,可能有人会问:

加入事务和嵌套事务有什么区别?

加入事务 (REQUIRED) 和嵌套事务(NESTED) 都是事务传播机制中的两种传播级别,如果当前不存在事务,那么二者的行为是一致的。但如果当前存在事务:

  1. 加入事务: 遇到异常时,会回滚全部事务
  2. 嵌套事务:遇到异常时,回滚部分事务

嵌套事务之所以能回滚部分事务,是因为数据库中存在一个保存点的概念,嵌套事务相对于新建了一个保存点,如果出现异常了,那么只需要回滚到保存点即可,这样就实现了部分事务的回滚。

嵌套事务的实现原理:

嵌套事务之所以能实现部分事务的回滚,是因为在数据库中存在一个保存点(savepoint)的概念,以 MySQL 为例,嵌套事务相当于新建了一个保存点,而滚回时只回滚到当前保存点,因此之前的事务是不受影响的,这一点可以在 MySQL 的官方文档汇总找到相应的资料: https://dev.mysgl.com/doc/refman/5.7/en/savepoint.html

而 REQUIRED 是加入到当前事务中,并没有创建事务的保存点,因此出现了回滚就是整个事务回滚,这就是嵌套事务和加入事务的区别。
保存点就像玩通关游戏时的“游戏存档”一样,如果设置了游戏存档,那么即使当前关卡失败了,也能继续上一个存档点继续玩,而不是从头开始玩游戏。

相关文章:

Spring事务和事务的传播机制

目录 Spring中事务的实现 MySQL中的事务使用 Spring 编程式事务 TransactionTemplate 编程式事务 TransactionManager编程式事务 Spring声明式事务 Transactional 参数说明 事务因为程序异常捕获不会自动回滚的解决方案 Transactional 原理 Spring 事务隔离级别 Spring…...

软件开发提效工具——低代码(Low-Code)

目录 一、什么是低代码&#xff08;Low-Code&#xff09;&#xff1f; 二、构建轻量化平台 1、软件开发快效率高 2、满足企业的多样化需求 3、轻松与异构系统集成 4、软件维护成本低 5、为企业实现降本增效 三、小结 一、什么是低代码&#xff08;Low-Code&#xff09;&#xf…...

菜单栏管理软件 Bartender 3 mac中文版功能介绍

​Bartender 3 mac是一款菜单栏管理软件&#xff0c;该软件可以将指定的程序图标隐藏起来&#xff0c;需要时呼出即可。 Bartender 3 mac功能介绍 Bartender 3完全支持macOS Sierra和High Sierra。 更新了macOS High Sierra的用户界面 酒吧现在显示在菜单栏中&#xff0c;使其…...

ef core code first pgsql

在使用efcode来操作pgsql的时候&#xff0c;总有些基础配置流程项目建立完之后后面就很少用&#xff0c;总是忘掉&#xff0c;写个文档记忆一下吧。基于net 6.0。 1.创建一个mvc项目和一个EF类库 2.在类库里面安装依赖dll Microsoft.EntityFrameworkCore.Design 需要添加的…...

容器化nacos部署并实现服务发现(gradle)

1.如何容器化部署mysql 2. 如何容器化部署nacos 为不暴露我的服务器地址&#xff0c;本文全部使用localhost来代替服务器地址&#xff0c;所有的localhost都应该调整为你自己的服务器地址。 为不暴露我的服务器地址&#xff0c;本文全部使用localhost来代替服务器地址&#x…...

金融行业如何数字化转型?_光点科技

金融行业的数字化转型涉及技术创新的引入、客户体验的改善、内部流程的优化、安全和合规性的加强以及员工技能和企业文化的转变。 技术创新 包括云计算、人工智能、大数据分析和区块链技术的采用。云计算增强数据处理的灵活性&#xff0c;AI和机器学习在风险评估和欺诈检测方面…...

【LeetCode刷题-滑动窗口】--1695.删除子数组的最大得分

1695.删除子数组的最大得分 注意&#xff1a;子数组为不同元素 方法&#xff1a;滑动窗口 使用变长滑动窗口寻找数组nums中的以每个下标作为结束下标的元素各不相同的最长子数组。用[start,end]表示滑动窗口&#xff0c;初始时startend0&#xff0c;将滑动窗口的右端点end向右…...

iOS OpenGL ES3.0入门实践

一、效果图 入门实践&#xff0c;做的东西比较简单&#xff0c;效果如下&#xff1a; 二、关于顶点坐标和纹理坐标 绘制图片需要设置顶点坐标和纹理坐标并加载像素数据&#xff0c;之所以要指定两组坐标是因为纹理和顶点使用不同的坐标系&#xff0c;就是告诉OpenGL&#xf…...

网络基础(一)

文章目录&#xff1a; 计算机网络认识计算机网络背景网络发展认识 “协议” 网络协议初识协议分层OSI七层模型TC/IP 五层&#xff08;或四层&#xff09;模型 网络传输基本流程网络传输流程图同局域网的两台主机进行通信跨网络的两台主机进行通信数据包的封装和分用 网络中的地…...

SQLServer添加Oracle链接服务器

又一次在项目中用到了在SQLServer添加Oracle链接服务器&#xff0c;发现之前文章写的也不太好使&#xff0c;那就再总结一次吧。 1、安装OracleClient 安装64位&#xff0c;多数SQLServer是64位&#xff0c;所以OracleClient也安装64位的&#xff1b; 再一个一般安装的Oracl…...

2017年计网408

第33题 假设 OSI 参考模型的应用层欲发送 400B 的数据 (无拆分), 除物理层和应用层之外, 其他各层在封装 PDU 时均引入 20 B 的额外开销, 则应用层数据传输效率约为( )A. 80%B. 83%C. 87%D. 91% 本题考察有关数据包逐层封装的相关概念。我们来一起分析一下。 这是要求大家必须…...

UE5中APlayerController属性与方法列表(翻译中......)

一、属性列表 类型 名称 描述 TObjectPtr< APa... AcknowledgedPawn 用于网络游戏&#xff0c;使客户端可以承认它拥有一个特定的卒。 TArray< FActive... ActiveForceFeedbackEffects TSharedPtr< str... ActiveHapticEffect_Gun TSharedPtr< str..…...

TCP连接保活机制

在TCP连接中有一个保活机制&#xff0c;叫做Keep-Alive&#xff0c;用语言描述就是如下&#xff1a; 在保活时间内&#xff0c;如果没有任何连接相关的活动&#xff0c;TCP 保活机制会开始作用&#xff0c;每隔一个时间间隔&#xff08;保活时间间隔&#xff09;&#xff0c;发…...

centos安装mysql8.0.20、tar包安装方式

基础环境centos7.5&#xff0c;mysql版本8.0.20&#xff0c;通过tar包安装&#xff0c;安装路径/usr/local。 mysql官网&#xff1a;https://dev.mysql.com/downloads/mysql/ wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.20-linux-glibc2.12-x86_64.tar.x…...

k8s yaml文件含义

文章目录 1、YAML的文件格式和注意事项2、YAML各个字段含义 1、YAML的文件格式和注意事项 不支持制表符tab键缩进&#xff0c;需要使用空格缩进&#xff0c;使用缩进表示层级关系通常开头缩进2个空格&#xff0c;缩进的空格数不重要&#xff0c;只要相同层级的元素左对齐即可字…...

ProEasy机器人:运动+通讯相关说明

----------------机械手运动------- --常用指令&#xff1a;MovP弧线运动、MovL直线运动 MovP(1) --弧线轨迹运动到一号点 MovP(2) --弧线轨迹运动到二号点 MovL(1) --直线轨迹运动到一号点 MovL(2) --直线轨迹运…...

Visual Studio Code 配置 C/C++ 开发环境的最佳实践(VSCode + Clangd + CMake)

Visual Studio Code 配置 C/C 开发环境的最佳实践(VSCode Clangd CMake) 知乎原文参考地址1 与 VSCode 官方文档配置 相比拥有的优势 Clangd 具有更优秀的性能&#xff0c;微软官方 Cpptools 的代码提示功能有明显延迟Clangd 提供更精准的「转到定义」、「重命名符号」、「…...

年产200万件的超级工厂投产!巨头「闭环」汽车电子全产业链

随着汽车电动化程度的提升&#xff0c;汽车电子部件占整车成本比重逐步升高&#xff0c;已经从2012年的25%上升到2021年的55%。 且汽车电子电气架构向整车集中演进&#xff0c;智能汽车已经进入了软件及数据定义时代&#xff0c;底层硬件打破了过去几十年围绕特定应用不断增加…...

智能穿戴AR眼镜主板方案定制_MTK平台AR智能眼镜PCB板开发

AR智能眼镜&#xff0c;是采用了多种技术实现增强现实效果&#xff0c;是将虚拟信息和现实场景相结合的智能设备。 AR智能眼镜硬件上&#xff0c;包括多个传感器、显示装置和处理器等。其中&#xff0c;传感器用于捕捉用户的动作和环境信息&#xff0c;如摄像头、陀螺仪、加速…...

【入门Flink】- 11Flink实现动态TopN

基本处理函数&#xff08;ProcessFunction&#xff09; stream.process(new MyProcessFunction())方法需要传入一个 ProcessFunction 作为参数&#xff0c;ProcessFunction 不是接口 &#xff0c; 而是一个抽象类 &#xff0c;继承了AbstractRichFunction&#xff0c;所有的处…...

大话软工笔记—需求分析概述

需求分析&#xff0c;就是要对需求调研收集到的资料信息逐个地进行拆分、研究&#xff0c;从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要&#xff0c;后续设计的依据主要来自于需求分析的成果&#xff0c;包括: 项目的目的…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

Java毕业设计:WML信息查询与后端信息发布系统开发

JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发&#xff0c;实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构&#xff0c;服务器端使用Java Servlet处理请求&#xff0c;数据库采用MySQL存储信息&#xff0…...

纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join

纯 Java 项目&#xff08;非 SpringBoot&#xff09;集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

接口自动化测试:HttpRunner基础

相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具&#xff0c;支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议&#xff0c;涵盖接口测试、性能测试、数字体验监测等测试类型…...

Redis:现代应用开发的高效内存数据存储利器

一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发&#xff0c;其初衷是为了满足他自己的一个项目需求&#xff0c;即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源&#xff0c;Redis凭借其简单易用、…...

Docker拉取MySQL后数据库连接失败的解决方案

在使用Docker部署MySQL时&#xff0c;拉取并启动容器后&#xff0c;有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致&#xff0c;包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因&#xff0c;并提供解决方案。 一、确认MySQL容器的运行状态 …...

如何配置一个sql server使得其它用户可以通过excel odbc获取数据

要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据&#xff0c;你需要完成以下配置步骤&#xff1a; ✅ 一、在 SQL Server 端配置&#xff08;服务器设置&#xff09; 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到&#xff1a;SQL Server 网络配…...