Spring Boot 各种事务操作实战(自动回滚、手动回滚、部分回滚)

概念

事务定义

事务,就是一组操作数据库的动作集合。事务是现代数据库理论中的核心概念之一。如果一组处理步骤或者全部发生或者一步也不执行,我们称该组处理步骤为一个事务。当所有的步骤像一个操作一样被完整地执行,我们称该事务被提交。由于其中的一部分或多步执行失败,导致没有步骤被提交,则事务必须回滚到最初的系统状态。

事务特点
  • 原子性:一个事务中所有对数据库的操作是一个不可分割的操作序列,要么全做要么全不做
  • 一致性:数据不会因为事务的执行而遭到破坏
  • 隔离性:一个事务的执行,不受其他事务的干扰,即并发执行的事务之间互不干扰
  • 持久性:一个事务一旦提交,它对数据库的改变就是永久的。

事务实现机制

Spring为事务管理提供了丰富的功能支持。Spring 事务管理分为编程式声明式的两种方式。

  • 编程式事务管理: 编程式事务管理使用TransactionTemplate或者直接使用底层的 PlatformTransactionManager。对于编程式事务管理,spring 推荐使用 TransactionTemplate。
  • 声明式事务管理: 建立在 AOP 之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
  • 声明式事务管理不需要入侵代码,更快捷而且简单,推荐使用。

声明式事务有两种方式:

  • 一种是在配置文件(xml)中做相关的事务规则声明(因为很少用本文不讲解)
  • 另一种是基于**@Transactional**注解的方式。注释配置是目前流行的使用方式,推荐使用。

在应用系统调用声明了 @Transactional 的目标方法时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,根据 @Transactional 的属性配置信息,这个代理对象决定该声明 @Transactional 的目标方法是否由拦截器 TransactionInterceptor 来使用拦截,在 TransactionInterceptor 拦截时,会在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑,最后根据执行情况是否出现异常,利用抽象事务管理器 AbstractPlatformTransactionManager 操作数据源 DataSource 提交或回滚事务。

Spring AOP 代理有 CglibAopProxy 和 JdkDynamicAopProxy 两种,以 CglibAopProxy 为例,对于 CglibAopProxy,需要调用其内部类的 DynamicAdvisedInterceptor 的 intercept 方法。对于 JdkDynamicAopProxy,需要调用其 invoke 方法。

开启事务

注解 @Transactional 的使用
注解 @Transactional 常用配置

参 数 名 称

功 能 描 述

readOnly

用于设置当前事务是否为只读事务,设置为 true 表示只读,false 则表示可读写,默认值为 false。例如:@Transactional(readOnly=true)

rollbackFor

用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:指定单一异常类:@Transactional(rollbackFor=RuntimeException.class);指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})

transactionManager / value

多个事务管理器托管在 Spring 容器中时,指定事务管理器的 bean 名称

rollbackForClassName

用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如:指定单一异常类名称 @Transactional(rollbackForClassName=”RuntimeException”) 指定多个异常类名称:@Transactional(rollbackForClassName={“RuntimeException”,”Exception”})

noRollbackFor

用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如:指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})

noRollbackForClassName

用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如:指定单一异常类名称:@Transactional(noRollbackForClassName=”RuntimeException”) 指定多个异常类名称:@Transactional(noRollbackForClassName={“RuntimeException”, ”Exception”})

propagation

用于设置事务的传播行为。例如:@Transactional(propagation=Propagation.NOT_SUPPORTED, readOnly=true)

isolation

用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置

timeout

该属性用于设置事务的超时秒数,默认值为 - 1 表示永不超时
事物超时设置:@Transactional(timeout=30) ,设置为 30 秒

Propagation 的属性(事务的传播行为)

例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)

Propagation 属性

含义

REQUIRED

默认值 在有 transaction 状态下执行;如当前没有 transaction,则创建新的 transaction;

SUPPORTS

如当前有 transaction,则在 transaction 状态下执行;如果当前没有 transaction,在无 transaction 状态下执行;

MANDATORY

必须在有 transaction 状态下执行,如果当前没有 transaction,则抛出异常 IllegalTransactionStateException;

REQUIRES_NEW

创建新的 transaction 并执行;如果当前已有 transaction,则将当前 transaction 挂起;

NOT_SUPPORTED

在无 transaction 状态下执行;如果当前已有 transaction,则将当前 transaction 挂起;

NEVER

在无 transaction 状态下执行;如果当前已有 transaction,则抛出异常 IllegalTransactionStateException。

事务 5 种隔离级别

例如:@Transactional(isolation = Isolation.READ_COMMITTED)

隔离级别

含义

DEFAULT

这是一个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别另外四个与 JDBC 的隔离级别相对应;

READ_UNCOMMITTED

最低的隔离级别。事实上我们不应该称其为隔离级别,因为在事务完成前,其他事务可以看到该事务所修改的数据。而在其他事务提交前,该事务也可以看到其他事务所做的修改。可能导致脏,幻,不可重复读

READ_COMMITTED

大多数数据库的默认级别。在事务完成前,其他事务无法看到该事务所修改的数据。遗憾的是,在该事务提交后,你就可以查看其他事务插入或更新的数据。这意味着在事务的不同点上,如果其他事务修改了数据,你就会看到不同的数据。可防止脏读,但幻读和不可重复读仍可以发生。

REPEATABLE_READ

比 ISOLATION_READ_COMMITTED 更严格,该隔离级别确保如果在事务中查询了某个数据集,你至少还能再次查询到相同的数据集,即使其他事务修改了所查询的数据。然而如果其他事务插入了新数据,你就可以查询到该新插入的数据。可防止脏读,不可重复读,但幻读仍可能发生。

SERIALIZABLE

完全服从 ACID 的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。代价最大、可靠性最高的隔离级别,所有的事务都是按顺序一个接一个地执行。避免所有不安全读取。

使用注意事项(防止事务失效)
  • 在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。

  • **@Transactional 注解应该只被应用在 public 修饰的方法上。**如果你在 protected、private 或者 package-visible 的方法上使用 该注解,它也不会报错(IDEA 会有提示), 但事务并没有生效。

  • 被外部调用的公共方法 A 有两个进行了数据操作的子方法 B 和子方法 C 的事务注解说明:

    • 被外部调用的公共方法 A 未声明事务 @Transactional,子方法 B 和 C 若是其他类的方法且各自声明事务,则事务由子方法 B 和 C 各自控制

    • 被外部调用的公共方法 A 未声明事务 @Transactional,子方法 B 和 C 若是本类的方法,则无论子方法 B 和 C 是否声明事务,事务均不会生效

    • 被外部调用的公共方法 A 声明事务 @Transactional,无论子方法 B 和 C 是不是本类的方法,无论子方法 B 和 C 是否声明事务,事务均由公共方法 A 控制

    • 被外部调用的公共方法 A 声明事务 @Transactional,子方法运行异常,但运行异常被子方法自己try-catch处理了,则事务回滚是不会生效的!

      如果想要事务回滚生效,需要将子方法的事务控制交给调用的方法来处理:

      • 方案 1:子方法中不用try-catch处理运行异常
      • 方案 2:子方法的 catch 里面将运行异常抛出【throw new RuntimeException();】
  • 默认情况下,Spring 会对 unchecked 异常进行事务回滚,也就是默认对 RuntimeException() 异常或是其子类进行事务回滚。

    如果是 checked 异常则不回滚,例如空指针异常、算数异常等会被回滚;文件读写、网络问题 Spring 就没法回滚。

    若想对所有异常(包括自定义异常)都起作用,注解上面需配置异常类型:@Transactional(rollbackFor = Exception.class

  • 数据库要支持事务,如果是 mysql,要使用 innodb 引擎,myisam 不支持事务

  • 事务 @Transactional 由 spring 控制时,它会在抛出异常的时候进行回滚。如果自己使用 try-catch 捕获处理了,是不生效的。如果想事务生效可以进行手动回滚或者在 catch 里面将异常抛出【throw new RuntimeException();】

    • 方案一:手动抛出运行时异常(缺陷是不能在 catch 代码块自定义返回值)

      try{....  }catch(Exception e){logger.error("",e);throw new RuntimeException(e);}
      
    • 方案二:手动进行回滚【 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 】

      try{...}catch(Exception e){log.error("fail",e);TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();return false;}
      
  • @Transactional 可以放在 Controller 下面直接起作用,看到网上好多同学说要放到 @Component 下面或者 @Service 下面,经过试验,可以不用放在这两个下面也起作用。

  • @Transactional 引入包问题,它有两个包:

    import javax.transaction.Transactional; 
    // 和
    import org.springframework.transaction.annotation.Transactional;         // 推荐
    

    这两个都可以用,对比了一下他们两个的方法和属性,发现后面的比前面的强大。建议使用后面的。

使用场景
自动回滚

直接抛出,不 try/catch

@Override
@Transactional(rollbackFor = Exception.class)
public Object submitOrder() throws Exception {  success();  //假如exception这个操作数据库的方法会抛出异常,方法success()对数据库的操作会回滚。 exception(); return ApiReturnUtil.success();
}
手动回滚

进行 try/catch,回滚并抛出

@Override
@Transactional(rollbackFor = Exception.class)
public Object submitOrder (){  success();  try {  exception(); } catch (Exception e) {  e.printStackTrace();     // 手动回滚事务TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();return ApiReturnUtil.error();}  return ApiReturnUtil.success();
}
回滚部分异常

使用【Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint(); 】设置回滚点。

使用【TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);】回滚到 savePoint。

@Override
@Transactional(rollbackFor = Exception.class)
public Object submitOrder (){  success();  //只回滚以下异常,Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();try {  exception(); } catch (Exception e) {  e.printStackTrace();     // 手工回滚事务TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);return ApiReturnUtil.error();}  return ApiReturnUtil.success();
}
手动创建、提交、回滚事务

PlatformTransactionManager 这个接口中定义了三个方法 getTransaction 创建事务,commit 提交事务,rollback 回滚事务。它的实现类是 AbstractPlatformTransactionManager。

@Autowired
priDataSourceTransactionManager dataSourceTransactionManager;
@Autowired
TransactionDefinition transactionDefinition;// 手动创建事务
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);// 手动提交事务
dataSourceTransactionManager.commit(transactionStatus);// 手动回滚事务。(最好是放在catch 里面,防止程序异常而事务一直卡在哪里未提交)
dataSourceTransactionManager.rollback(transactionStatus);
PlatformTransactionManager 手动提交事务,设置隔离级别
@Autowired
private PlatformTransactionManager transactionManager;public void test() {DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);TransactionStatus transactionStatus = transactionManager.getTransaction(defaultTransactionDefinition);try {// 数据库操作 //提交事务transactionManager.commit(transactionStatus);} catch (Exception e) {log.error("xxxx", e);//回滚事务transactionManager.rollback(transactionStatus);}
}

事务失效不回滚的原因及解决方案

异常被捕获导致事务失效

在 spring boot 中,使用事务非常简单,直接在方法上面加入 @Transactional 就可以实现。

@GetMapping("delete")
@ResponseBody
@Transactional    
public void delete(@RequestParam("id") int id) {       try {          //delete countrythis.repository.delete(id);         if(id == 1){              throw Exception("测试事务");}           //delete citythis.repository.deleteByCountryId(id);}catch (Exception e){logger.error("delete false:" + e.getMessage());        }
}

发现事务不回滚,即 this.repository.delete(id);成功把数据删除了。

原因:

默认 spring 事务只在发生未被捕获的 RuntimeException 时才回滚。

spring aop 异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样 aop 代理才能捕获到方法的异常,才能进行回滚,默认情况下 aop 只捕获 RuntimeException 的异常,但可以通过配置来捕获特定的异常并回滚。

换句话说在 service 的方法中不使用 try catch 或者在 catch 中最后加上 throw new RuntimeExcetpion() 抛出运行异常,这样程序异常时才能被 aop 捕获进而回滚。

解决方案:

<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>4.3.2.RELEASE</version></dependency>
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.8.9</version>
</dependency><plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.9</version>
<configuration><showWeaveInfo>true</showWeaveInfo><aspectLibraries><aspectLibrary><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId></aspectLibrary></aspectLibraries>
</configuration><executions>
<execution><goals><goal>compile</goal><goal>test-compile</goal></goals>
</execution>
</executions>
</plugin>
解决方案

方案 1、在类上(或者最外层的公共方法)加事务

@Service
@Slf4j
public class MyTransactional {// 最外层公共方法。自动回滚事务方式,insertOrder()方法报错后事务回滚,且线程中止,后续逻辑无法执行@Transactionalpublic void test1() {this.insertOrder();System.out.println("11111111111111111");}// 最外层公共方法。手动回滚事务方式,insertOrder()方法报错后事务回滚,可以继续执行后续逻辑@Transactionalpublic void test2() {try {  insertOrder();} catch (Exception e) {  log.error("faild to ...", e);// 手动回滚事务TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();// 其他操作}// 其他操作}// 进行数据库操作的方法(private 或 public 均可)private void insertOrder() {//insert log info//insertOrder//updateAccount}
}

方案 2、使用 AspectJ 取代 Spring AOP 代理

上面的两个问题 @Transactional 注解只应用到 public 方法和自调用问题,是由于使用 Spring AOP 代理造成的。为解决这两个问题,可以使用 AspectJ 取代 Spring AOP 代理。

需要将下面的 AspectJ 信息添加到 xml 配置信息中。

AspectJ 的 xml 配置信息

<tx:annotation-driven mode="aspectj" />
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property  />
</bean>
</bean class="org.springframework.transaction.aspectj.AnnotationTransactionAspect" factory-method="aspectOf"><property  />
</bean>

同时在 Maven 的 pom 文件中加入 spring-aspects 和 aspectjrt 的 dependency 以及 aspectj-maven-plugin。

AspectJ 的 pom 配置信息

其他

事务提交方式

默认情况下,数据库处于自动提交模式。每一条语句处于一个单独的事务中,在这条语句执行完毕时,如果执行成功则隐式的提交事务,如果执行失败则隐式的回滚事务。

对于正常的事务管理,是一组相关的操作处于一个事务之中,因此必须关闭数据库的自动提交模式。不过,这个我们不用担心,spring 会将底层连接的【自动提交特性】设置为 false 。也就是在使用 spring 进行事务管理的时候,spring 会将【是否自动提交】设置为 false,等价于 JDBC 中的 connection.setAutoCommit(false); ,在执行完之后在进行提交 connection.commit(); 。

事务回滚规则

指示 spring 事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常。spring 事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务。

默认配置下,spring 只有在抛出的异常为运行时 unchecked 异常时才回滚该事务,也就是抛出的异常为 RuntimeException 的子类 (Errors 也会导致事务回滚),而抛出 checked 异常则不会导致事务回滚。

可以明确的配置在抛出那些异常时回滚事务,包括 checked 异常。也可以明确定义那些异常抛出时不回滚事务。

事务并发会产生的问题

术语

含义

脏读

A 事务读取到了 B 事务还未提交的数据,如果 B 未提交的事务回滚了,那么 A 事务读取的数据就是无效的,这就是数据脏读

不可重复读

在同一个事务中,多次读取同一数据返回的结果不一致,这是由于读取事务在进行操作的过程中,如果出现更新事务,它必须等待更新事务执行成功提交完成后才能继续读取数据,这就导致读取事务在前后读取的数据不一致的状况出现

幻读

A 事务读取了几行记录后,B 事务插入了新数据,并且提交了插入操作,在后续操作中 A 事务就会多出几行原本不存在的数据,就像 A 事务出现幻觉,这就是幻读

第一类丢失更新

没有事务隔离的情况下,两个事务都同时更新一行数据,但是第二个事务却中途失败退出, 导致对数据的两个修改都失效了。

例如:

张三的工资为 5000,事务 A 中获取工资为 5000,事务 B 获取工资为 5000,汇入 100,并提交数据库,工资变为 5100;

与此同时,事务B正在读取张三的工资,读取到张三的工资为8000。随后,事务A发生异常,回滚了事务,张三的工资又回滚为5000。最后,事务B读取到的张三工资为8000的数据即为脏数据,事务B做了一次脏读。
脏读

脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

例如:

张三的工资为 5000,事务 A 中把他的工资改为 8000,但事务 A 尚未提交。

与此同时,事务B正在读取张三的工资,读取到张三的工资为8000。随后,事务A发生异常,回滚了事务,张三的工资又回滚为5000。最后,事务B读取到的张三工资为8000的数据即为脏数据,事务B做了一次脏读。
不可重复读

是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。

例如:

在事务 A 中,读取到张三的工资为 5000,操作没有完成,事务还没提交。
  与此同时,事务 B 把张三的工资改为 8000,并提交了事务。
  随后,在事务 A 中,再次读取张三的工资,此时工资变为 8000。在一个事务中前后两次读取的结果并不致,导致了不可重复读。

第二类丢失更新

不可重复读的特例。

有两个并发事务同时读取同一行数据,然后其中一个对它进行修改提交,而另一个也进行了修改提交。这就会造成第一次写操作失效。

例如:

在事务 A 中,读取到张三的存款为 5000,操作没有完成,事务还没提交。
  与此同时,事务 B 存入 1000,把张三的存款改为 6000,并提交了事务。
  随后,在事务 A 中,存储 500,把张三的存款改为 5500,并提交了事务,这样事务 A 的更新覆盖了事务 B 的更新。

幻读

是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

例如:

目前工资为 5000 的员工有 10 人,事务 A 读取到所有的工资为 5000 的人数为 10 人。
  此时,事务 B 插入一条工资也为 5000 的记录。
  这时,事务 A 再次读取工资为 5000 的员工,记录为 11 人。此时产生了幻读。

不可重复读和幻读的区别

不可重复读的重点是修改,同样的条件,你读取过的数据,再次读取出来发现值不一样了

幻读的重点在于新增或者删除,同样的条件,第 1 次和第 2 次读出来的记录数不一样

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ethernet 系列(12)-- 基础学习::SOME/IP

目录 1. SOME/IP简介&#xff1a; 1.1 什么是SOME/IP&#xff1a; 1.2 什么时候使用SOME/IP&#xff1a; 2. SOME/IP的特点&#xff1a; 2.1 序列化&#xff1a; 2.2 远程过程调用&#xff08;RPC&#xff09;: 2.3 服务发现&#xff1a; 2.4 发布/订阅&#xff1a; 2.5 UDP消息…

UE5.3 虚幻引擎 Windows插件开发打包(带源码插件打包、无源码插件打包)

0 引言 随着项目体量的增大&#xff0c;所有代码功能都放一起很难管理。所以有什么办法可以将大模块划分成一个个小模块吗。当然有&#xff0c;因为虚幻引擎本身就遇到过这个问题&#xff0c;他的解决办法就是使用插件的形式开发。 例如&#xff0c;一个团队开发了文件I/O模块插…

自学记录鸿蒙API 13:实现多目标识别Object Detection

起步&#xff1a;什么叫多目标识别&#xff1f; 无论是生活中的动物识别、智能相册中的场景分类&#xff0c;还是工业领域的检测任务&#xff0c;都能看到多目标识别的身影。这次&#xff0c;我决定通过学习HarmonyOS最新的Object Detection API&#xff08;API 13&#xff09…

光伏安装在屋顶:安全、环保还是潜在威胁?

随着环保意识的增强和科技的进步&#xff0c;光伏发电作为一种可再生能源技术&#xff0c;正逐渐走进千家万户。然而&#xff0c;随着光伏板的普及&#xff0c;关于其在屋顶安装是否对人体有害的疑问也随之而来。 一、光伏发电的基本原理 光伏发电是利用半导体界面的光生伏特效…

被催更了,2025元旦源码继续免费送

“时间从来不会停下&#xff0c;它只会匆匆流逝。抓住每一刻&#xff0c;我们才不会辜负自己。” 联系作者免费领&#x1f496;源&#x1f496;码。 三联支持&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;欢迎留言讨论 更多内容敬请期待。如有需要源码可以联系作者免…

MYsql--------ubantu中安装mysql

在Ubuntu平台上下载、启动和关闭MySQL的方法如下&#xff1a; 下载安装MySQL 更新软件包列表&#xff1a;打开终端&#xff0c;输入以下命令&#xff0c;确保软件包列表是最新的。sudo apt update安装MySQL服务器&#xff1a;执行以下命令安装MySQL服务器。在安装过程中&…

pygame飞机大战

飞机大战 1.main类2.配置类3.游戏主类4.游戏资源类5.资源下载6.游戏效果 1.main类 启动游戏。 from MainWindow import MainWindow if __name__ __main__:appMainWindow()app.run()2.配置类 该类主要存放游戏的各种设置参数。 #窗口尺寸 import random import pygame WIND…

Flutter中的网络请求图片存储为缓存,与定制删除本地缓存

Flutter中的网络请求图片存储为缓存&#xff0c;与定制删除本地缓存 1&#xff1a;封装请求图片函数 2&#xff1a;访问的图片都会转为本地缓存&#xff0c;当相同的请求url&#xff0c;会在本地调用图片 3&#xff1a;本地缓存管理【windows与andriod已经测试】【有页面】【有…

无线AP安装注意事项

现在的办公楼、酒店等项目中都设计含有网络无线覆盖这一项&#xff0c;在项目实施中&#xff0c;往往采用的是便捷并且后期便于网络无线设备管理的无线ap设备&#xff0c;作为前端无线信号的覆盖。在具体安装无线AP过程中&#xff0c;我们必须要注意以下几点才能保证项目实施完…

Golang的容器编排实践

Golang的容器编排实践 一、Golang中的容器编排概述 作为一种高效的编程语言&#xff0c;其在容器编排领域也有着广泛的运用。容器编排是指利用自动化工具对容器化的应用进行部署、管理和扩展的过程&#xff0c;典型的容器编排工具包括Docker Swarm、Kubernetes等。在Golang中&a…

计算机毕业设计Django+Tensorflow音乐推荐系统 音乐可视化 卷积神经网络CNN LSTM音乐情感分析 机器学习 深度学习 Flask

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

C# 在PDF中添加和删除水印注释 (Watermark Annotation)

目录 使用工具 C# 在PDF文档中添加水印注释 C# 在PDF文档中删除水印注释 PDF中的水印注释是一种独特的注释类型&#xff0c;它通常以透明的文本或图片形式叠加在页面内容之上&#xff0c;为文档添加标识或信息提示。与传统的静态水印不同&#xff0c;水印注释并不会永久嵌入…

分析服务器 systemctl 启动gozero项目报错的解决方案

### 分析 systemctl start beisen.service 报错 在 Linux 系统中&#xff0c;systemctl 是管理系统和服务的主要工具。当我们尝试重启某个服务时&#xff0c;如果服务启动失败&#xff0c;systemctl 会输出错误信息&#xff0c;帮助我们诊断和解决问题。 本文将通过一个实际的…

Dubbo扩展点加载机制

加载机制中已经存在的一些关键注解&#xff0c;如SPI、©Adaptive> ©Activateo然后介绍整个加载机制中最核心的ExtensionLoader的工作流程及实现原理。最后介绍扩展中使用的类动态编译的实 现原理。 Java SPI Java 5 中的服务提供商https://docs.oracle.com/jav…

如何利用Logo设计免费生成器创建专业级Logo

在当今的商业世界中&#xff0c;一个好的Logo是品牌身份的象征&#xff0c;它承载着公司的形象与理念。设计一个专业级的Logo不再需要花费大量的金钱和时间&#xff0c;尤其是当我们拥有Logo设计免费生成器这样的工具时。接下来&#xff0c;让我们深入探讨如何利用这些工具来创…

游戏如何检测iOS越狱

不同于安卓的开源生态&#xff0c;iOS一直秉承着安全性更高的闭源生态&#xff0c;系统中的硬件、软件和服务会经过严格审核和测试&#xff0c;来保障安全性与稳定性。 据FairGurd观察&#xff0c;虽然iOS系统具备一定的安全性&#xff0c;但并非没有漏洞&#xff0c;如市面上…

智联视频超融合平台:电力行业的智能守护者

文章目录 一、远程实时监控与设备状态监测二、提高应急响应能力三、实现无人值守与减员增效四、保障电力设施安全与防范外部破坏五、提升电网运行管理效率与决策科学性六、助力电力企业数字化转型与智能化发展七、智联视频超融合平台 在当今数字化浪潮下&#xff0c;视频联网平…

卸载干净 IDEA(图文讲解)

目录 1、卸载 IDEA 程序 2、注册表清理 3、残留清理 1、卸载 IDEA 程序 点击屏幕左下角 Windows 图标 -> 设置-控制面板->intellij idea 勾选第一栏 Delete IntelliJ IDEA 2022.2 caches and local history&#xff0c;表示同时删除 IDEA 本地缓存以及历史。 Delete I…

计算机网络•自顶向下方法:路由选路算法

路由选路算法 在网络层中&#xff0c;选路是指数据包从源主机到目的主机的传输过程中&#xff0c;如何通过网络中的路由器选择一条合适的路径。路由器根据网络拓扑、路由表、协议规则等来决定如何将数据包转发到下一跳&#xff0c;直到数据包到达目的地。 选路算法分类 静态算…

Qemu配置QXL显卡支持分辨率

默认情况下&#xff0c;创建的vm的视频RAM限制为16MB。在win操作系统中分辨率最高就只能调到1024x768。 <video><model typecirrus vram16384 heads1 primaryyes/><address typepci domain0x0000 bus0x00 slot0x02 function0x0/> </video>单单修改ram…