SpringBoot 事务失效及其对应解决办法

简介

本文主要讲述Spring事务会去什么情况下失效及其解决办法

Spring 通过AOP 进行事务控制,如果操作数据库报异常,则会进行回滚;如果没有报异常则会提交事务;但是,如果Spring 事务失效,会导致数据缺失/重复等异常问题。

演示环境 

编译工具:IDEA 社区版本

数据库:MySQL 8

框架:SpringBoot 2.1.0 + MyBatis-plus3.4.3 + lombok等

库表:

-- bill.base_project definition

CREATE TABLE `base_project` (
  `id` varchar(64) NOT NULL COMMENT '主键',
  `tid` varchar(64) NOT NULL COMMENT '地区行政编码',
  `ptid` varchar(64) NOT NULL COMMENT '所属省级行政编码',
  `project_name` varchar(256) NOT NULL COMMENT '项目名称',
  `project_address` varchar(512) DEFAULT NULL COMMENT '项目地址',
  `project_no` varchar(128) NOT NULL COMMENT '项目编号',
  `developer_entity_name` varchar(218) DEFAULT NULL COMMENT '开发企业名称',
  `developer_entity_no` varchar(128) DEFAULT NULL COMMENT '开发企业编号',
  `suery_unit` varchar(128) DEFAULT NULL COMMENT '勘察单位',
  `design_unit` varchar(128) DEFAULT NULL COMMENT '设计单位',
  `build_unit` varchar(128) DEFAULT NULL COMMENT '施工单位',
  `supervisor_unit` varchar(128) DEFAULT NULL COMMENT '监理单位',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

SpringBootCase 父类项目 pom.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <packaging>pom</packaging>
    <modules>
        <module>SpringBoot-MyBatisPlus</module>
    </modules>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
    </parent>

    <groupId>org.example</groupId>
    <artifactId>SpringBootCase</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-properties-migrator</artifactId>
            <scope>runtime</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

SpringBoot-MyBatisPlus 子类项目 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>SpringBootCase</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>SpringBoot-MyBatisPlus</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <mybatisplus-spring-boot-starter.version>3.4.3</mybatisplus-spring-boot-starter.version>
        <mysql-spring-boot-starter.version>8.0.19</mysql-spring-boot-starter.version>
    </properties>


    <dependencies>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatisplus-spring-boot-starter.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql-spring-boot-starter.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <executable>true</executable>
                    <!--必须为ZIP模式,不指定的话-Dloader.path不生效-->
                    <layout>ZIP</layout>
                    <!-- 打包的时候排除的jar包-->
                    <includes>
                        <include>
                            <groupId>non-exists</groupId>
                            <artifactId>non-exists</artifactId>
                        </include>
                    </includes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <!--指定的依赖路径-->
                            <outputDirectory>
                                ${project.build.directory}/lib
                            </outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

资源配置文件:application.yml

server:
  port: 8080
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.43.10:3306/bill?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&autoReconnect=true
    username: root
    password: 123456
    hikari:
      minimum-idle: 10
      maximum-pool-size: 20
      idle-timeout: 500000
      max-lifetime: 540000
      connection-timeout: 60000
      connection-test-query: select 1

mybatis-plus:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: cn.zzg.mybatisplus.entity


其他相关文件,会在相关代码演示中一一讲解

JDBC 事务原理

我这里简单编写了一份jdbc 事务管理的通用伪代码:

//获取数据库连接
Connection connection = DriverManager.getConnection();
//设置自动提交为false
connection.setAutoCommit(false);
// SQL 操作
.........
//事务提交或回滚
connection.commit()/connection.rollback
//关闭数据库连接
connection.close();

 Spring 事务原理

Spring 通过事务注解@Transactional来控制事务,底层实现是基于切面编程AOP实现的,而Spring中实现AOP机制采用的是动态代理,具体分为JDK动态代理和CGLIB动态代理两种模式。

功能描述:

1.Spring 在bean的初始化过程中,检查方法是否被Transactional注解修饰,如果方法被修饰需要对相应的Bean进行代理,生成代理对象。

2. 在方法调用的时候,会执行切面的逻辑,而这里切面的逻辑中就包含了开启事务、提交事务或者回滚事务等逻辑。

温馨提示:Spring 本身不实现事务,底层还是依赖于数据库的事务。

Spring 事务失效场景

前提Mapper接口和PO对象:

package cn.zzg.mybatisplus.mapper;

import cn.zzg.mybatisplus.entity.BaseProjectPO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface BaseProjectMapper extends BaseMapper<BaseProjectPO> {
}
package cn.zzg.mybatisplus.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;

@Data
@TableName("base_project")
public class BaseProjectPO extends Model {
    private String id;

    private String tid;

    private String ptid;

    private String projectName;

    private String projectAddress;

    private String projectNo;

    private String developerEntityName;

    private String developerEntityNo;

    private String sueryUnit;

    private String designUnit;

    private String buildUnit;

    private String supervisorUnit;
}
package cn.zzg.mybatisplus.service;

import cn.zzg.mybatisplus.entity.BaseProjectPO;
import com.baomidou.mybatisplus.extension.service.IService;

import java.io.IOException;

public interface IBaseProjectService extends IService<BaseProjectPO> {
}

1.抛出异常

比如事务代码控制如下:

在IBaseProjectService服务接口,新增如下方法定义:

 void insertBaseProject1(BaseProjectPO baseProject) throws IOException;

 在BaseProjectServiceImpl服务实现类中如下实现:

   @Transactional
   @Override
    public void insertBaseProject1(BaseProjectPO baseProject) throws IOException {
        boolean target = this.baseMapper.insert(baseProject) > 0 ? true: false;
        throw new IOException();
   }

在Controller调用该方法

   @GetMapping("/insert1")
    public boolean insert1() throws IOException {
        BaseProjectPO po = new BaseProjectPO();
        po.setId("4");
        po.setTid("4");
        po.setPtid("4");
        po.setProjectNo("4");
        po.setProjectName("4");
        service.insertBaseProject1(po);
        return true;
    }

最终数据库库表截图: 

说明此事务失效。 

造成原因

@Transactional 没有特别指定异常,Spring 只会在遇到运行时异常RuntimeException或者error时进行回滚,而IOException等异常不会影响回滚。

解决办法:

配置rollbackFor属性,常用配置为:@Transactional(rollbackFor = Exception.class)

服务类调整后,事务生效代码片段

   @Transactional(rollbackFor = Exception.class)
   @Override
    public void insertBaseProject1(BaseProjectPO baseProject) throws IOException {
        boolean target = this.baseMapper.insert(baseProject) > 0 ? true: false;
        throw new IOException();
   }

2.业务方法捕获异常

在IBaseProjectService服务接口,新增如下方法定义:
 void insertBaseProject2(BaseProjectPO baseProject) ;

在BaseProjectServiceImpl服务实现类中如下实现:

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void insertBaseProject2(BaseProjectPO baseProject) {
        try{
            this.baseMapper.insert(baseProject);
            int i = 1/0;
        }catch (Exception e){
            e.printStackTrace();
        }
    }

在Controller调用该方法

 @GetMapping("/insert1")
    public boolean insert1() throws IOException {
        BaseProjectPO po = new BaseProjectPO();
        po.setId("5");
        po.setTid("5");
        po.setPtid("5");
        po.setProjectNo("5");
        po.setProjectName("5");
        service.insertBaseProject2(po);
        return true;
    }

最终数据库库表截图: 

 说明此事务失效。 

造成原因:

Spring是否进行回滚是根据你的代码是否抛出异常决定的,所以如果你自己捕获了异常,自己处理并且没有抛出异常会导致Spring 事务失效。

解决办法:

1.配置rollbackFor属性,常用配置为:@Transactional(rollbackFor = Exception.class)。并在try...catch 代码片段中抛出相关异常。

2.使用编写式事务,个人推荐使用。

transactionTemplate.execute

服务类调整后,事务生效代码片段

第一种方法:

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void insertBaseProject2(BaseProjectPO baseProject) throws IOException {
        try{
            this.baseMapper.insert(baseProject);
            int i = 1/0;
        }catch (Exception e){
            e.printStackTrace();
            throw new IOException();
        }
    }

第二种方法:

    @Override
    public void insertBaseProject2(BaseProjectPO baseProject) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult(){
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                   BaseProjectServiceImpl.super.baseMapper.insert(baseProject);
                    int i = 1/0;
                } catch (Exception e) {
                    status.setRollbackOnly() ;
                }
            }
        });
    }

温馨提示:记录用户操作日志优化点......

3.同一类中的方法调用

在IBaseProjectService服务接口,新增如下方法定义:

 boolean insertBaseProject(BaseProjectPO baseProject);

在BaseProjectServiceImpl服务实现类中如下实现:

  @Override
  public boolean insertBaseProject(BaseProjectPO baseProject) {
      return this.insertTrans(baseProject);
   
  }

  @Transactional
  public boolean insertTrans(BaseProjectPO baseProject){
    // 数据库操作
   boolean target = this.baseMapper.insert(baseProject) > 0 ? true: false;
   // 模拟异常操作
    int i = 1/0;
   return target;
  }

在Controller调用该方法


    @GetMapping("/insert")
    public boolean insert() {
        BaseProjectPO po = new BaseProjectPO();
        po.setId("2");
        po.setTid("2");
        po.setPtid("3");
        po.setProjectNo("2");
        po.setProjectName("2");
        return service.insertBaseProject(po);
    }

最终数据库库表截图: 

 说明此事务失效。

造成原因:

Spring的事务管理功能是通过动态代理实现的,而Spring默认使用JDK动态代理,而JDK动态代理采用接口实现的方式,通过反射调用目标类。简单理解,就是insertBaseProject()方法中调用this.insertTrans(),这里的this是真实对象,所以会直接走insertTrans()的业务逻辑,而不会走切面逻辑,所以事务失败。

Aop 事务伪代码

@Service
class A{
    @Transactinal
    method b(){...}
    
    method a(){    //标记1
        b();
    }
}
 
//Spring扫描注解后,创建了另外一个代理类,并为有注解的方法插入一个startTransaction()方法:
class proxy$A{
    A objectA = new A();
    method b(){    //标记2
        startTransaction();
        objectA.b();
    }
 
    method a(){    //标记3
        objectA.a();    //由于a()没有注解,所以不会启动transaction,而是直接调用A的实例的a()方法
    }
}

详细说明:

当我们调用A的bean的a()方法的时候,也是被proxy$A拦截,执行proxy$A.a()(标记3),然而,由以上代码可知,这时候它调用的是objectA.a(),也就是由原来的bean来调用a()方法了,所以代码跑到了“标记1”。由此可见,“标记2”并没有被执行到,所以startTransaction()方法也没有运行。

 解决办法:

方案一:解决方法可以是直接在调用类中添加@Transactional注解insertBaseProject()

方案二@EnableAspectJAutoProxy(exposeProxy = true)在启动类中添加,会由Cglib代理实现。

在pom.xml 添加cglib 相关依赖

   <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>

Application 程序入口 添加

@EnableAspectJAutoProxy(exposeProxy = true)
package cn.zzg.mybatisplus;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
@EnableTransactionManagement
@MapperScan({"cn.zzg.mybatisplus.mapper"})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

服务类调整后,事务生效代码片段

    @Override
  public boolean insertBaseProject(BaseProjectPO baseProject) {
      //return this.insertTrans(baseProject);
      return ((BaseProjectServiceImpl) AopContext.currentProxy()).insertTrans(baseProject);

  }

方案三: 使用ApplicationContext 获取Sevice 实现类

package cn.zzg.mybatisplus.components;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class ApplicationContetxtHolder implements ApplicationContextAware {
    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ApplicationContetxtHolder.context = applicationContext;
    }

    public static ApplicationContext getContext(){
        return context;
    }
}

 在BaseProjectServiceImpl服务实现类中如下实现:

    @Override
  public boolean insertBaseProject(BaseProjectPO baseProject) {
        //return this.insertTrans(baseProject);
        //return ((BaseProjectServiceImpl) AopContext.currentProxy()).insertTrans(baseProject);
        BaseProjectServiceImpl service = ApplicationContetxtHolder.getContext().getBean(BaseProjectServiceImpl.class);
        return service.insertTrans(baseProject);
  }

在Controller调用该方法

    @GetMapping("/insert")
    public boolean insert() {
        BaseProjectPO po = new BaseProjectPO();
        po.setId("6");
        po.setTid("6");
        po.setPtid("6");
        po.setProjectNo("6");
        po.setProjectName("6");
        return service.insertBaseProject(po);
    }

 4. 方法使用 final 或 static关键字

如果Spring使用了Cglib代理实现(比如你的代理类没有实现接口),而你的业务方法恰好使用了final或者static关键字,那么事务也会失败。更具体地说,它应该抛出异常,因为Cglib使用字节码增强技术生成被代理类的子类并重写被代理类的方法来实现代理。如果被代理的方法的方法使用finalstatic关键字,则子类不能重写被代理的方法。

如果Spring使用JDK动态代理实现,JDK动态代理是基于接口实现的,那么finalstatic修饰的方法也就无法被代理。

总而言之,方法连代理都没有,那么肯定无法实现事务回滚了。

解决办法

方法去掉final或者static关键字

5. 方法不是public

如果方法不是publicSpring事务也会失败,因为Spring的事务管理源码AbstractFallbackTransactionAttributeSource中有判断computeTransactionAttribute()。如果目标方法不是公共的,则TransactionAttribute返回null

// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
  return null;
}

解决办法

方法访问级别修改为publi

6. 错误使用传播机制

Spring事务的传播机制是指在多个事务方法相互调用时,确定事务应该如何传播的策略。Spring提供了七种事务传播机制:REQUIREDSUPPORTSMANDATORYREQUIRES_NEWNOT_SUPPORTEDNEVERNESTED。如果不知道这些传播策略的原理,很可能会导致交易失败。

@Service
public class TransactionService {


    @Autowired
    private UserMapper userMapper;

    @Autowired
    private AddressMapper addressMapper;


    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
    public  void doInsert(User user,Address address) throws Exception {
        //do something
        userMapper.insert(user);
        saveAddress(address);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public  void saveAddress(Address address) {
        //do something
        addressMapper.insert(address);
    }
}

在上面的例子中,如果用户插入失败,不会导致saveAddress()回滚,因为这里使用的传播是REQUIRES_NEW,传播机制REQUIRES_NEW的原理是如果当前方法中没有事务,就会创建一个新的事务。如果一个事务已经存在,则当前事务将被挂起,并创建一个新事务。在当前事务完成之前,不会提交父事务。如果父事务发生异常,则不影响子事务的提交。

事务的传播机制说明如下:

REQUIRED 如果当前上下文中存在事务,那么加入该事务,如果不存在事务,创建一个事务,这是默认的传播属性值。
SUPPORTS 如果当前上下文存在事务,则支持事务加入事务,如果不存在事务,则使用非事务的方式执行。
MANDATORY 如果当前上下文中存在事务,否则抛出异常。
REQUIRES_NEW 每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
NOT_SUPPORTED 如果当前上下文中存在事务,则挂起当前事务,然后新的方法在没有事务的环境中执行。
NEVER 如果当前上下文中存在事务,则抛出异常,否则在无事务环境上执行代码。
NESTED 如果当前上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。

解决办法

将事务传播策略更改为默认值REQUIREDREQUIRED原理是如果当前有一个事务被添加到一个事务中,如果没有,则创建一个新的事务,父事务和被调用的事务在同一个事务中。即使被调用的异常被捕获,整个事务仍然会被回滚。

7.没有被Spring管理


//@Service(value = "baseProjectServiceImpl")
public class BaseProjectServiceImpl extends ServiceImpl<BaseProjectMapper, BaseProjectPO> implements IBaseProjectService {
    ******
}

此时把 @Service 注解注释掉,这个类就不会被加载成一个 Bean,那这个类就不会被 Spring 管理了,事务自然就失效了。

解决办法

保证每个事务注解的每个Bean被Spring管理。

Spring 编程事务

编程事务实现方式:

  1. 使用TransactionTemplate 或 TransactionalOperator
  2. 直接创建TransactionManager的实现

官方推荐使用TransactionTemplate 方式。本章节也是重点讲解TransactionTemplate 使用

TransactionTemplate

带返回值

@Service 
public class UserService { 
     
  @Resource 
  private TransactionTemplate transactionTemplate ; 
  @Resource 
  private UsersRepository usersRepository ; 
     
  public Integer saveUsers(Users users) { 
    this.transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); 
    Integer result = transactionTemplate.execute(new TransactionCallback<Integer>() { 
      @Override 
      public Integer doInTransaction(TransactionStatus status) { 
        return usersMapper.insertUser(users) ; 
      } 
    }) ; 
    return result ; 
    } 
     
} 

无返回值

public void saveUsers(Users users) { 
  transactionTemplate.execute(new TransactionCallbackWithoutResult() { 
    @Override 
    protected void doInTransactionWithoutResult(TransactionStatus status) { 
      usersMapper.insertUser(users) ; 
    } 
  }) ; 
} 

事务回滚

public Users saveUser(Users users) { 
  return transactionTemplate.execute(new TransactionCallback<Users>() { 
    @Override 
    public Users doInTransaction(TransactionStatus status) { 
      try { 
        return usersMapper.insertUser(users) ; 
      } catch (Exception e) { 
        status.setRollbackOnly() ; 
      } 
      return null ; 
    } 
  }) ; 
} 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

几个原创文章自动生成软件推荐

在数字化时代&#xff0c;原创文章的重要性越来越被重视&#xff0c;但是频繁的写作工作却也耗费大量时间和精力。原创文章自动生成软件能够帮助大家快速生成各种类型的原创文章&#xff0c;节省时间和精力。本文将介绍6款不同的原创文章自动生成软件&#xff0c;并介绍一款知名…

mysql 常用命令练习

管理表格从表中查询数据从多个表查询修改数据sql变量类型 管理表格 创建一个包含三列的新表 CREATE TABLE products (id INT,name VARCHAR(255) NOT NULL,price INT DEFAULT 0,PRIMARY KEY(id) // 自增 ); 从数据库中删除表 DROP TABLE product; 向表中添加新列 ALTER TAB…

WPF中如何设置自定义控件

1.圆角按钮的设置&#xff1a; 众所周知在WPF中自带有提示信息&#xff0c;当我问创建Button时&#xff0c;点击空格出现如下可选设置 带有小扳手&#x1f527;图标为相应的属性&#xff0c;如果Button有CornerRadius&#xff08;角半径&#xff09;属性就能够直接设置Button实…

记录 android studio 通过安装NDK 编译C文件,得到需要的so文件

只怪自己太健忘&#xff0c;每次网上查了一圈&#xff0c;搞定后&#xff0c;再遇到又发现不会操作了&#xff0c;特此记下 不废话直接上步骤 &#xff08;1&#xff09; 进入AS的settinging如下界面 &#xff08;2&#xff09;选中图片箭头两个文件 进行下载 &#xff08;…

Jmeter分布式部署

前期准备&#xff1a; 1. 控制机一台&#xff0c;代理机一台&#xff0c;Jmeter安装包 操作步骤&#xff1a; 1. Linux安装Jmeter&#xff08;windows安装教程自己搜一下&#xff09; 1.1创建一个单独的文件夹(jmeter)&#xff0c;用来存放Jmeter的安装包 mkdir jmeter 1.2…

docker安装使用基础

一、镜像安装 我的docker安装在centos7虚拟机上&#xff08;关于虚拟机网络设置此前已有总结VMware 搭建centos虚拟机网络设置_vmware centos 网络配置-CSDN博客&#xff09;&#xff0c;现在默认操作系统和网络已就位。 1、安装工具包 # 安装操作一般都需要管理员权限&…

【leetcode】 剑指 Offer学习计划(java版本含注释)(下)

目录 前言第十六天&#xff08;排序&#xff09;剑指 Offer 45. 把数组排成最小的数&#xff08;中等&#xff09;剑指 Offer 61. 扑克牌中的顺子&#xff08;简单&#xff09; 第十七天&#xff08;排序&#xff09;剑指 Offer 40. 最小的k个数&#xff08;简单&#xff09; 第…

STL常见容器(set/multiset)---C++

STL常见容器目录&#xff1a; 7. set/multiset 容器7.1 set/multiset 基本概念7.2 set构造和赋值7.3 set大小和交换7.4 set插入和删除7.5 set查找和统计7.6 set和multiset区别7.6.1 pair对组创建7.6.2 set和multiset区别 7.7 set容器排序7.7.1 内置数据类型7.7.2 自定义数据类型…

记录 关于navicat连接数据库报错1045的问题

重装数据库之后就连接不上了 报错1045 而网上的解决方案大都是更改数据库密码&#xff0c;但是我在第一步就被卡住无法更改密码&#xff0c;输入指令也报错&#xff0c;检查的环境变量也没错&#xff0c;经过长时间的试错终于找到解决了办法 解决办法 删除data文件夹 如果无法…

吸引用户购买产品的文案技巧,媒介盒子揭秘

在营销过程中&#xff0c;想要吸引用户购买产品&#xff0c;文案是重中之重&#xff0c;需要一定的技巧才能将文案写好&#xff0c;今天媒介盒子就来和大家聊聊&#xff1a;在品牌推广中如何通过一些小技巧吸引用户购买产品&#xff1a; 一、 少说专业术语 少说行话、黑话。多…

如何选择合适的幻兽帕鲁服务器CPU核心数和内存大小,避免丢包和延迟高?

根据幻兽帕鲁服务器的实际案例分析选择合适的CPU核心数和内存大小以避免丢包和延迟高&#xff0c;首先需要考虑的是服务器的性能需求。幻兽帕鲁服务器推荐使用4核CPU和16GB内存&#xff0c;建议使用32GB以上的内存才能稳定运行。这表明对于幻兽帕鲁这样的游戏服务器来说&#x…

2024有哪些免费的mac苹果电脑深度清理工具?CleanMyMac X

苹果电脑用户们&#xff0c;你们是否经常感到你们的Mac变得不再像刚拆封时那样迅速、流畅&#xff1f;可能是时候对你的苹果电脑进行一次深度清理了。在这个时刻&#xff0c;拥有一些高效的深度清理工具就显得尤为重要。今天&#xff0c;我将介绍几款优秀的苹果电脑深度清理工具…

(done) 如何计算 Hessian Matrix 海森矩阵 海塞矩阵

参考视频1&#xff1a;https://www.bilibili.com/video/BV1H64y1T7zQ/?spm_id_from333.337.search-card.all.click 参考视频2&#xff08;正定矩阵&#xff09;&#xff1a;https://www.bilibili.com/video/BV1Ag411M76G/?spm_id_from333.337.search-card.all.click&vd_…

关于vue中关于eslint报错的问题

1 代码保存的时候会自动将单引号报错为双引号 导致eslint报错的问题&#xff0c; 解决思路&#xff1a; 在项目根目录下新建一个.prettierrc.json文件 { “tabWidth”: 2,“useTabs”: false,“singleQuote”: true,“semi”: false} 2 关于报错代码的时候 出现尾随逗号报错…

继承-学习2

this关键字&#xff1a;指向调用该方法的对象&#xff0c;一般我们是在当前类中使用this关键字&#xff0c;所以我们常说代表本类对象的引用 super关键字&#xff1a;代表父类存储空间的标识(可看作父类对象的引用) 父类&#xff1a; package ven;public class Fu {//父类成员…

WhatsApp代理設置指南

某些情況下&#xff0c;你可能需要使用WhatsApp代理來確保WhatsApp順暢且不受限制的通信。本篇文章將講解WhatsApp代理是什麼、WhatsApp代理的使用場景、以及如何在WhatsApp中使用和設置代理。​​​​​​​ WhatsApp代理指什麼? WhatsApp代理是位於可以訪問WhatsApp的國家或…

AtCoder Beginner Contest 341

C - Takahashi Gets Lost 分析&#xff1a;数据小&#xff0c;暴力没什么好说的 int h,w,n; string t; char c[505][505]; int dx[4]{1,-1,0,0}; int dy[4]{0,0,1,-1}; int get(char c){if(cL)return 3;if(cU)return 1;if(cR)return 2;return 0; } int check(int x,int y,int …

ensp路由器将不同网络连通在一起

1.拓扑结构信息如下 二层交换机&#xff1a;lsw2,lsw3,lsw5,lsw6 不进行ip配置&#xff0c;只是定义vlan&#xff0c;和主机标注的保持一致&#xff0c;向下连接pc用access&#xff0c;向上连接路由交换机用trunk lsw2配置信息如下图 定义vlan&#xff0c;设置各个连接口的方式…

如何进行弱网测试?

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 如今这个高度互联的时代里&#xff0c;网络环境对于应用程序的影响越来越重要。 而弱网测试就是…

SpringMVC 学习(七)之报文信息转换器 HttpMessageConverter

目录 1 HttpMessageConverter 介绍 2 RequestBody 注解 3 ResponseBody 注解 4 RequestEntity 5 ResponseEntity 6 RestController 注解 1 HttpMessageConverter 介绍 HttpMessageConverter 报文信息转换器&#xff0c;将请求报文&#xff08;如JSON、XML、HTML等&#x…