【Spring】Spring中的事务

文章目录

  • 1. Spring事务简介
  • 2. Spring事务的案例
    • 案例代码
      • 代码目录结构
      • 数据库
      • pom.xml
      • Resource/jdbc.properties
      • config/SpringConfig.java
      • config/JdbcConfig.java
      • config/MyBatisConfig.java
      • dao/AccountDao.java
      • service/AccountService.java
      • service/impl/AccountServiceImpl.java
      • 测试方法
    • 问题分析
    • 事务管理三步
      • 第一步:在业务层接口上加上注解@Transactional
      • 第二步:在JdbcConfig.java中注册事务管理器
      • 第三步:在SpringConfig.java上加上开启事务管理的注解@EnableTransactionManagement
  • 3. Spring事务角色
  • 4. Spring事务属性
    • 事务配置
    • 案例:转账业务追加日志
      • 案例代码
        • 代码结构
        • 数据库表
        • dao/LogDao.java
        • service/LogService.java
        • service/LogServiceImpl.java
        • 修改service/impl/AccountServiceImpl.java如下
      • 改进
    • 事务传播行为

1. Spring事务简介

事务作用: 在数据层保障一系列的数据库操作同成功、同失败
Spring事务作用: 在数据层或业务层保障一系列的数据库操作同成功、同失败
Spring为事务提供的接口和实现类:

// 接口
public interface PlatformTransactionManager{
	void commit(TransactionStatus status) throws TransactionException;
	void rollback(TransactionStatus status) throws TransactionException;
}
// 实现类
public class DataSourceTransactionManager{
	...
}

2. Spring事务的案例

需求: 实现两个账户间的转账操作
需求微缩: A账户减钱,B账户加钱
分析:
在这里插入图片描述

案例代码

代码目录结构

在这里插入图片描述

数据库

在这里插入图片描述

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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>project5</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>project5</name>
    <description>project5</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>6.0.3</version>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.0.33</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.11</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>3.0.1</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.13</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.25</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
    </dependencies>

</project>

Resource/jdbc.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=123456

config/SpringConfig.java

package com.example.project5.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource("classpath:jdbc.properties")
@ComponentScan("com.example.project5")
@Import({JdbcConfig.class, MyBatisConfig.class})
public class SpringConfig {
}

config/JdbcConfig.java

package com.example.project5.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

public class JdbcConfig {

    @Value("${jdbc.driver}")
    String driver;
    @Value("${jdbc.url}")
    String url;
    @Value("${jdbc.username}")
    String username;
    @Value("${jdbc.password}")
    String password;

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUsername(username);
        ds.setPassword(password);
        ds.setUrl(url);
        return ds;
    }

}

config/MyBatisConfig.java

package com.example.project5.config;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;

public class MyBatisConfig {

    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("com.example.project5.domain");
        ssfb.setDataSource(dataSource);
        return ssfb;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setBasePackage("com.example.project5.dao");
        return mapperScannerConfigurer;
    }

}

dao/AccountDao.java

package com.example.project5.dao;

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
import org.springframework.stereotype.Repository;

@Repository
public interface AccountDao {

    @Update("update account set money = money + #{money} where username = #{name}")
    void addMoney(@Param("name") String username, @Param("money") Double money);

    @Update("update account set money = money - #{money} where username = #{name}")
    void outMoney(@Param("name") String username, @Param("money") Double money);
}

service/AccountService.java

package com.example.project5.service;

public interface AccountService {
    /**
     * 转账操作
     * @param out 转出方
     * @param in 转入方
     * @param money 金额
     */
    public void transfer(String out, String in, double money);
}

service/impl/AccountServiceImpl.java

package com.example.project5.service.impl;

import com.example.project5.dao.AccountDao;
import com.example.project5.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    AccountDao accountDao;
    @Override
    public void transfer(String out, String in, double money) {
        accountDao.outMoney(out, money);
        accountDao.addMoney(in, money);
    }
}

测试方法

package com.example.project5;

import com.example.project5.config.SpringConfig;
import com.example.project5.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class Project5ApplicationTests {

    @Autowired
    private AccountService accountService;

    @Test
    public void testTransfer() {
        accountService.transfer("aaa", "bbb", 20);
    }

}

执行测试代码后,测试代码不会产生任何输出,但数据库中aaa的金额会由100变成80,bbb的金额会由111变成131:
在这里插入图片描述

问题分析

假如在AccountServiceImpl中手动制造一个错误:

package com.example.project5.service.impl;

import com.example.project5.dao.AccountDao;
import com.example.project5.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    AccountDao accountDao;
    @Override
    public void transfer(String out, String in, double money) {
        accountDao.outMoney(out, money);
        int a = 1/0;
        accountDao.addMoney(in, money);
    }
}

这时候,程序在执行完outMoney方法,也就是aaa转出了20之后就不会继续执行了,这20并没有转入到bbb的账户之中,这就是事务的不一致性。接着上面的aaa金额为80,bbb的金额为131执行这个会报错的代码,结果是:
在这里插入图片描述

对运行的结果简单进行分析:
在这里插入图片描述
我们需要进行事务管理,使得数据层中的数据同加同减,而不是分开操作

事务管理三步

第一步:在业务层接口上加上注解@Transactional

package com.example.project5.service;

import org.springframework.transaction.annotation.Transactional;

public interface AccountService {
    /**
     * 转账操作
     * @param out 转出方
     * @param in 转入方
     * @param money 金额
     */
    @Transactional
    public void transfer(String out, String in, double money);
}

在这里插入图片描述

第二步:在JdbcConfig.java中注册事务管理器

package com.example.project5.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.lang.management.PlatformLoggingMXBean;

public class JdbcConfig {

    @Value("${jdbc.driver}")
    String driver;
    @Value("${jdbc.url}")
    String url;
    @Value("${jdbc.username}")
    String username;
    @Value("${jdbc.password}")
    String password;

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUsername(username);
        ds.setPassword(password);
        ds.setUrl(url);
        return ds;
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }

}

在这里插入图片描述

第三步:在SpringConfig.java上加上开启事务管理的注解@EnableTransactionManagement

package com.example.project5.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@PropertySource("classpath:jdbc.properties")
@ComponentScan("com.example.project5")
@Import({JdbcConfig.class, MyBatisConfig.class})
@EnableTransactionManagement
public class SpringConfig {
}

启用事务管理后,保持刚才会报错的AccountServiceImpl.java的代码,恢复aaa金额为80,bbb金额为131,并再次进行测试,此时数据库中的内容不会发生任何改变:
在这里插入图片描述

3. Spring事务角色

在事务没有开启的时候:
在这里插入图片描述
outMoneyinMoney分别对应一个事务,我们手动写的异常是写在事务T1和事务T2之间的,则事务T1执行完毕以后发生了异常,所以事务T2不再执行
为了将两个事务统一起来,统一执行,或者统一不执行,我们在transfer方法上加了注解@Transactional,此时transfer本身是一个事务,我们将outMoneyinMoney都加入到这个事务中来:
在这里插入图片描述
此时我们将transfer方法称为事务管理员outMoneyinMoney称为事务协调员,具体定义如下:
在这里插入图片描述

4. Spring事务属性

事务配置

在@Transactional中还有很多属性
在这里插入图片描述
这里需要说明的是rollbackFor,默认的事务回滚,在我们没有定义rollbackFor的时候,只会在程序中出现运行时异常时候进行回滚,比如我们刚才手动指定的1/0就属于一个运行时抛出异常,假如修改这个异常如下:

package com.example.project5.service.impl;

import com.example.project5.dao.AccountDao;
import com.example.project5.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    AccountDao accountDao;
    @Override
    public void transfer(String out, String in, double money) throws IOException {
        accountDao.outMoney(out, money);
        if(true) throw new IOException();
        accountDao.addMoney(in, money);
    }
}

再执行测试代码,就会发现数据库中的内容会从(aaa:80,bbb:131)->(aaa:60,bbb:131)
再次印证:没有定义rollbackFor的时候,只会在程序中出现运行时异常时候进行回滚
那么我们定义一下rollbackFor属性,如下:

package com.example.project5.service;

import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;

public interface AccountService {
    /**
     * 转账操作
     * @param out 转出方
     * @param in 转入方
     * @param money 金额
     */
    @Transactional(rollbackFor = {IOException.class})
    public void transfer(String out, String in, double money) throws IOException;
}

再执行测试代码,就会发现数据库中的内容(aaa:60,bbb:131)->(aaa:60,bbb:131),没有发生改变,所以我们需要通过rollbackFor来指定一些非运行时异常,在定义rollbackFor以后,程序在遇到运行时异常仍会回滚。

案例:转账业务追加日志

在这里插入图片描述

案例代码

在上述案例代码中加上如下内容:

代码结构

在这里插入图片描述

数据库表

在这里插入图片描述

dao/LogDao.java
package com.example.project5.dao;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;

import java.util.Date;

@Repository
public interface LogDao {
    @Insert("insert into log(content, date) VALUES(#{content}, #{date})")
    void insertLog(@Param("content") String content, @Param("date") Date date);
}

service/LogService.java

注意,该方法上也要加上事务注解

package com.example.project5.service;

import java.util.Date;

public interface LogService {
	@Transactional
    void insertLog(String content, Date date);
}
service/LogServiceImpl.java
package com.example.project5.service.impl;

import com.example.project5.dao.LogDao;
import com.example.project5.service.LogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;

@Service
public class LogServiceImpl implements LogService {
    
    @Autowired
    LogDao logDao;
    
    @Override
    public void insertLog(String content, Date date) {
        logDao.insertLog(content, date);
    }
}
修改service/impl/AccountServiceImpl.java如下
package com.example.project5.service.impl;

import com.example.project5.dao.AccountDao;
import com.example.project5.service.AccountService;
import com.example.project5.service.LogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Date;

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    AccountDao accountDao;
    
    @Autowired
    LogService logService;
    
    @Override
    public void transfer(String out, String in, double money) throws IOException {
        try{
            accountDao.outMoney(out, money);
            accountDao.addMoney(in, money);            
        } finally {
            logService.insertLog(out + "向" + in + "转账" + money + "元", new Date());
        }
    }
}

当捕捉到异常时执行日志记录。
将数据库中的金额恢复为:aaa->100,bbb->111,并执行测试代码,得到account表和log表的结果:
在这里插入图片描述
在这里插入图片描述
正常执行的时候,会修改数据库中的金额、向日志记录中添加日志
假设我们在AccountServiceImpl的try中加上:

accountDao.outMoney(out, money);
int a = 1/0;
accountDao.addMoney(in, money);

我们期望的结果是:不修改数据库中的金额、向日志记录中添加日志,使用修改后的代码再执行测试方法,得到结果是account表和log表中的内容都没有发生任何变化,所以我们归纳总结出存在的问题:

在这里插入图片描述

改进

我们需要定义事务的传播属性propagation,在LogService.java下重新写注解,改为:

@Transactional(propagation = Propagation.REQUIRES_NEW)

此时,再次运行上面的代码,结果为:
account表中的内容不变,log表中新添了日志:
在这里插入图片描述
在这里插入图片描述
我认为这样的改进可以理解为,使用默认的propagation时,事务协调员都被添加到事务管理员的事务中,从而统一提交或统一回滚:
在这里插入图片描述
当我们在LogService上写明了事务的传播行为为Requires_New后,即使原有了事务,我们还是会为这个service实例开启一个新事务,如下,这样就不是统一受到事务t的控制了:
在这里插入图片描述

事务传播行为

在这里插入图片描述

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

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

相关文章

设计模式 简单工厂 工厂方法模式 抽象工厂模式

工厂模式介绍 工厂模式是我们最常用的实例化对象模式了&#xff0c;是用工厂方法代替new操作的一种模式。它是创建型模式。 简单工厂 简单工厂模式是指由一个工厂对象决定创建出哪一种产品类的实例, 但它不属于GOF 23种设计模式 简单工厂适用于工厂类负责创建的对象较少的场景,…

Command line is too long. Shorten command line for Application or also

一、问题描述 Error running ‘Application’: Command line is too long. Shorten command line for Application or also for Spring Boot default configuration? 二、原因分析 springboot项目启动命令过长&#xff01; 三、解决方案 第1步:点击项目启动配置项 第2步…

基于ssm的简单学校课程管理系统的设计与实现(源码+调试)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。今天给大家介绍一篇基于ssm的简单学校课程管…

vue2项目vue-qrcode-reader 扫一扫二维码插件

vue2项目 vue-qrcode-reader 扫一扫二维码插件 问题所在解决办法成功展示 问题所在 今天在引导师弟做扫二维码功能&#xff0c;发现通过npm install --save vue-qrcode-reade安装死活就是报错TypeError: Object...) is not a function 解决办法 百度了很多大牛的博客&#…

国内访问GitHub很卡,steam连接断开怎么办

目录 第一章、问题分析1.1&#xff09;问题1.2&#xff09;解决&#xff1a;下载个加速器就好了 友情提醒: 先看文章目录&#xff0c;大致了解文章知识点结构&#xff0c;点击文章目录可直接跳转到文章指定位置。 第一章、问题分析 1.1&#xff09;问题 国内访问GitHub很卡怎…

数据分析(一)(附带实例和源码)

一、主要目的&#xff1a; 主要利用Python包&#xff0c;如Numpy、Pandas和Scipy等常用分析工具并结合常用的统计量来进行数据的描述&#xff0c;把数据的特征和内在结构展现出来。熟悉在Python开发环境中支持数据分析的可用模块以及其中的方法&#xff0c;基于一定的样例数据…

Windows中安装Git软件和TortoiseGit软件

1、git软件下载地址 https://git-scm.com/download/win 2、TortoiseGit软件下载 >https://tortoisegit.org/download/ 3、软件安装 4、环境安装说明 上面介绍的是在Windows中使用git&#xff0c;如果你电脑已经装了Ubuntu系统&#xff0c;可以直接在Ubuntu中使用git命令提…

arthas获取spring bean

参考文章 arthas获取spring bean 写一个工具Util package com.example.lredisson.util;import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import o…

[RK-Linux] 移植Linux-5.10到RK3399(七)| 检查GPIO与LED节点,使能风扇接口

文章目录 一、原理图二、设备树配置三、节点控制一、原理图 ROC-RK3399-PC Pro 的 LED 接口如图: 工作灯 WORK_LED (蓝色灯)网络连接到 GPIO2_D3: DIY_LED (红色灯)网络连接到 GPIO0_B5: ROC-RK3399-PC Pro 要控制的 GPIO 接口主要是风扇,如图: FAN_CTL 网络连接到

【Java】SpringBoot中实现Redis Stream队列

SpringBoot实现Redis Stream队列 前言 简单实现一下在SpringBoot中操作Redis Stream队列的方式&#xff0c;监听队列中的消息进行消费。 jdk&#xff1a;1.8 springboot-version&#xff1a;2.6.3 redis&#xff1a;5.0.1&#xff08;5版本以上才有Stream队列&#xff09;…

【C++】STL 容器 - string 字符串操作 ⑤ ( string 字符串查找 | find 函数查找字符串 | rfind 函数查找字符串 )

文章目录 一、string 字符查找 - find 函数查找字符串1、string 类 find 函数原型说明2、代码示例 - 字符串查找3、代码示例 - 统计字符串子串 二、string 字符查找 - rfind 函数查找字符串1、string 类 rfind 函数原型说明2、代码示例 - rfind 字符串查找 一、string 字符查找…

前端常用去重的几种方式

文章目录 方式1: ES6新语法方式2: 遍历 利用filter方式3: 使用 new Map() for循环方式4: 利用 hasOwnProperty总结 在github 查看该文章 方式1: ES6新语法 过滤出网页中不重复的html标签 结合去重知识点考查 […new Set([…document.querySelectorAll(‘*’)].map(v>v.t…

HTML5+CSS3小实例:3D发光切换按钮效果

目录 一、运行效果 图片效果 二、项目概述 三、开发环境 四、实现步骤及代码 1.创建空文件夹 2.完成页面内容 3.完成css样式 五、项目总结 六、源码获取 一、运行效果 图片效果 二、项目概述 这个项目是一个演示3D发光切换按钮效果的网页。按钮由一个开关和一个指…

spring6 基于xml自动装配

目录结构 代码 UserContronller.java package bean.auto.controller;import bean.auto.service.UserService; import bean.auto.service.UserServiceImpl;public class UserContronller {private UserService userService;public void setUserService(UserService userServ…

智能配电房在线监测系统

智能配电房在线监测系统是一个综合性的系统&#xff0c;依托电力智慧运维工具-电易云&#xff0c;主要用于监控和调整配电房的环境、安防和电气设备状态。以下是该系统的一些主要功能和特点&#xff1a; 环境监控&#xff1a;实时监测配电房内的温度、湿度、SF6气体浓度、臭氧浓…

Cmake基础(4)

这篇文章在上一篇的基础之上应用多文件&#xff0c;即一个项目中添加多个文件 文章目录 GLOBsource_group排除文件 上一篇文章的cmake基本不变&#xff0c;这篇文章的重点在于add_executable(${EXECUTABLE_NAME} main.cpp) GLOB file(GLOB cpp_list ${CMAKE_CURRENT_SOURCE_…

【Python特征工程系列】8步教你用决策树模型分析特征重要性(源码)

一、问题 如果有一个包含数十个甚至数百个特征的数据集&#xff0c;每个特征都可能对你的机器学习模型的性能有所贡献。但是并不是所有的特征都是一样的。有些可能是冗余的或不相关的&#xff0c;这会增加建模的复杂性并可能导致过拟合。特征重要性分析可以识别并关注最具信息量…

浅谈MapReduce

MapReduce是一个抽象的分布式计算模型&#xff0c;主要对键值对进行运算处理。用户需要提供两个自定义函数&#xff1a; map&#xff1a;用于接受输入&#xff0c;并生成中间键值对。reduce&#xff1a;接受map输出的中间键值对集合&#xff0c;进行sorting后进行合并和数据规…

linux下的strerror和perror处理错误函数

strerror和perror是C语言中用于处理错误信息的函数。 strerror函数&#xff1a; strerror函数用于将错误码转换为对应的错误消息字符串。它接受一个整数参数&#xff0c;通常是由系统调用或库函数返回的错误码&#xff0c;然后返回一个描述该错误的字符串。 函数原型&#xff1…

双指针训练

1.原理 双指针是一种解题常用方法&#xff0c;常用于将数组按照某种要求进行分块/划分&#xff0c;这里的指针对于数组来说&#xff0c;可以替换成下标&#xff08;毕竟使用下标实际上就是用了指针&#xff09;。 1.1.划分区间 通常将这两个指针命名位dest/cur&#xff08;或…